# Seam — Progress Tracker
# Format: [x] = done, [>] = in progress, [ ] = not started
# Update this EVERY session. It is the first thing the agent reads.
#
# 2026-06-02: Phase 8 — Lean output + seam_impact summary tier complete
#   (branch feat/phase8-lean-output, issue #33, PR #34). Implemented via a Sonnet-agent
#   WORKFLOW (2 sequential TDD impl agents → parallel /review + /backend-taste).
#   #1 Lean output: verbose flag (default True) on seam_context/trace/impact/context_pack;
#      shared _apply_verbosity helper strips 6 heavy fields (decorators/is_exported/visibility/
#      qualified_name/resolved_by/best_candidate), keeps signature+core. CLI --lean on
#      impact/trace/pack. seam_query+seam_search enrichment-free → NO verbose flag.
#   #2 Impact summary: risk_summary {dir:{tier:count}} (full pre-cap), per-tier cap
#      SEAM_IMPACT_MAX_RESULTS=25 (config), truncated {dir:{tier:omitted}}, limit param (0=unlimited).
#      Cap applies by default → fixes hub-symbol blast (init_db 30k→4.5k tokens).
#   Review STOP (both reviewers): Rich CLI impact ignored --limit/--lean (called impact() directly).
#      Fixed: all 3 modes route through handle_seam_impact; _IMPACT_META_KEYS guards every iterator;
#      Rich truncation footer; quiet stderr signal. Also removed no-op verbose from seam_query;
#      corrected 3 misleading comments/docstrings (byte-identity, tier-distance, _apply_verbosity).
#   Benchmark re-run: 83.4%/77.6% → 91.8%/88.7% (Seam output 51k→26k tokens; row #2 -1.3%→+85.0%).
#   No schema change, no migration, no new deps, no new tool (count stays 10). 1 new config knob.
#   Test count: 1107 (1031 baseline + 74 Phase 8 net, incl. review-fix tests). Gate green.
# 2026-06-02: Phase 9 — Language expansion (5 → 11 languages) complete
#   (branch feat/phase9-language-expansion). Implemented via a scaffold + 3 parallel TDD
#   agents (Java/C# | C/C++ | Ruby/PHP), 6 review fixes + file-split (graph_ruby.py /
#   graph_php.py split from an oversized graph_ruby_php.py), and observability pass.
#   New grammar packages (6): tree-sitter-java 0.23.5, tree-sitter-c-sharp 0.23.5,
#     tree-sitter-ruby 0.23.1, tree-sitter-c 0.24.2, tree-sitter-cpp 0.23.4, tree-sitter-php 0.24.1
#   New extractor modules (4):
#     seam/indexer/graph_java_csharp.py — Java + C# symbol/edge/comment extractors
#       Java: class/interface/enum/record/method; annotations from modifiers
#       C#: class/struct/record/interface/enum/delegate/method; attributes; namespace traversed not emitted
#     seam/indexer/graph_c_cpp.py       — C + C++ symbol/edge/comment extractors
#       C: function/struct/union/enum/typedef; static→visibility=private
#       C++: class/struct/union/enum/function/method (Class.method); _dedup_cpp_symbols for in-class+out-of-line
#     seam/indexer/graph_ruby.py        — Ruby symbol/edge/comment extractors
#       class/module→class; def inside class→method; def self.x→method(Class.x); top-level def→function
#     seam/indexer/graph_php.py         — PHP symbol/edge/comment extractors
#       class/interface/trait/enum/function/method; grouped-use descent; enum method recursion
#   New leaf modules (2):
#     seam/indexer/signatures_ext.py   — Phase 4 enrichment for the 6 new langs (NodeFields re-declared;
#       drift test asserts key-parity with signatures.py:NodeFields)
#     seam/analysis/imports_ext.py     — Phase 5 import-mapping extraction for the 6 new langs
#       (_ImportMapping re-declared; drift test asserts key-parity with imports.py:_ImportMapping)
#       resolution returns [] for Java/C#/PHP qualified paths (classpath out of scope)
#   Builtins extended: seam/analysis/builtins.py gained 6 conservative per-language frozensets
#   Parser extended: seam/indexer/parser.py gained parse_java/parse_csharp/parse_ruby/parse_c/
#     parse_cpp/parse_php (PHP: language_php() factory); _dispatch_parser routes new language strings
#   Config: SEAM_LANGUAGE_MAP extended (.java, .cs, .rb, .c, .h, .cpp/.cc/.cxx/.c++, .hpp/.hh/.hxx, .php)
#     .h → c (deliberate MVP; see ADR-008 limitation a)
#   Docs: docs/adr/008-language-expansion.md created; ADR-005 updated with Phase 9 pointer;
#     CLAUDE.md, README.md, docs/ARCHITECTURE.md updated; code-comments WHY pass on new modules
#   MVP limitations (all documented in ADR-008 and CLAUDE.md Known Gotchas):
#     a) .h → C (not C++) — use .hpp/.hh/.hxx for C++ headers
#     b) nested classes have flat qualified names (Inner, not Outer.Inner) — matches Go/Rust precedent
#     c) C++ pure-virtual declarations (=0) NOT extracted (parse as field_declaration, not function_definition)
#     d) C function-pointer typedefs (typedef int(*Cb)(int)) NOT extracted
#     e) Java/C#/PHP import resolution returns [] (classpath/NuGet/Composer out of scope)
#     f) C/C++ system #include <...> resolution returns [] (no repo file found)
#   No schema change, no migration, MCP tool count stays 10, no new config knobs.
#   Test count: 1395 (1107 baseline + 288 Phase 9 net, incl. review-fix tests). Gate green.
# Next: roadmap item 8 (`seam install`) / v0.1.0 release prep.

# ── Phase 0: Foundation ────────────────────────────
[x] 1.1 Repository structure created
[x] 1.2 DISCOVERY.md written and approved
[x] 1.3 CLAUDE.md skeleton created
[x] 1.4 PRD, APP_FLOW, TECH_STACK, BACKEND_STRUCTURE docs written
[x] 1.5 Architecture docs + ADRs created
[x] 1.6 Python package scaffold created (empty modules)
[x] 1.7 Makefile + pyproject.toml + gate passing
[x] 1.8 .gitignore, .env.example committed
[x] 1.9 GitNexus indexed + CLAUDE.md finalized

# ── Phase 0: Implementation ────────────────────────
[x] 2.1 db.py — schema + upsert + delete
[x] 2.2 db.py tests
[x] 3.1 Python parser
[x] 3.2 TypeScript/JS parser
[x] 3.3 Parser tests with fixtures
[x] 4.1 Symbol extraction
[x] 4.2 Edge extraction
[x] 4.3 Graph tests
[x] 5.1 FTS5 search
[x] 5.2 Concept query
[x] 5.3 Context lookup
[x] 5.4 Query engine tests
[x] 6.1 seam init command
[x] 6.2 seam status command
[x] 6.3 CLI integration tests
[x] 7.1 File watcher daemon
[x] 7.2 Watcher integration test
[x] 8.1 MCP server setup
[x] 8.2 MCP tool handlers
[x] 8.3 seam start command
[x] 9.1 Benchmark baseline (grep+read proxy — live Bach session deferred, see docs/benchmark.md)
[x] 9.2 Benchmark with Seam (MCP tool-handler outputs)
[x] 9.3 Benchmark report (docs/benchmark.md — 88.7%/85.8% reduction, target was >=30%)
[ ] 10.1 README.md + quickstart
[ ] 10.2 uv.lock committed
[ ] 10.3 Gate passes in clean env
[ ] 10.4 Tag v0.1.0

# ── Last Session ─────────────────────────────────
# 2026-06-01: Day 0 — Full 101 initialization complete
# Next: Start with 2.1 (db.py schema implementation)
# 2026-06-01: Track A complete — db.py + engine.py implemented; 48 tests passing; gate green
# Next: Track B (parser.py + graph.py) and convergence (CLI seam init)
# 2026-06-01: Track B — Parser + Graph Extraction complete
#   - parser.py: _parse helper + parse_python/typescript/javascript
#   - graph.py: extract_symbols + extract_edges for Python + TypeScript
#   - 38 tests passing, make gate green
# 2026-06-01: Convergence — Tracks A+B merged into main; all steps 2.1-5.4 complete
# Next: Step 6.1 (seam init command)
# 2026-06-01: CONVERGE — CLI integration complete; steps 6.1-6.3 done
#   - seam/cli/main.py: index_one_file(), init, status commands implemented
#   - tests/integration/test_indexer.py: 6 integration tests; all passing
#   - 92 total tests; make gate green (lint + typecheck + pytest)
#   - --db-dir option on init/status for test isolation
# 2026-06-01: Step 7.1-7.2 — File watcher daemon complete
#   - seam/watcher/daemon.py: SeamWatcher (FileSystemEventHandler) with per-file debounce
#   - on_created/on_modified: debounced by SEAM_DEBOUNCE_MS -> index_one_file
#   - on_deleted: db.delete_file (immediate, no debounce)
#   - start()/stop(): Observer lifecycle + .seam/watcher.pid management
#   - tests/integration/test_watcher.py: 4 integration tests; all passing
#   - 96 total tests; make gate green (lint + typecheck + pytest)
# 2026-06-01: Steps 8.1-8.4 — MCP server complete
#   - seam/server/tools.py: handle_seam_query/context/search — validation, clamping, relativization
#   - seam/server/mcp.py: create_server(conn, root) — FastMCP stdio, three tools registered
#   - seam/cli/main.py: `seam start` — watcher subprocess + MCP foreground + SIGTERM cleanup
#   - tests/integration/test_mcp_tools.py: 15 integration tests; all passing
#   - 111 total tests; make gate green (ruff + mypy + pytest)
# 2026-06-01: REVIEW + HARDENING + QA pass (commit e27f349)
#   - /backend-taste + /review (high) found index-corruption + extraction bugs.
#   - Fixed (Opus): per-connection foreign_keys + busy_timeout (db.connect());
#     stable-id upsert (ON CONFLICT) + explicit edge delete; new indexer/pipeline.py
#     (kills watcher->cli layer violation); watcher resolves root (macOS /private);
#     guarded background-thread callbacks; `python -m seam.watcher` entry + PID
#     liveness; status takes a path arg; engine score sign; query maps malformed-FTS
#     to INVALID_QUERY; graph nested-fn mis-tag + decorated-class drop + docstring fix.
#     +8 regression tests (test_hardening.py).
#   - QA (no HITL): 119 tests green; init/status/query/context/search verified;
#     TS indexing works; live watcher re-index 0.55s (<1s PRD target).
#   - 9.x benchmark (Bach): DROPPED per user.
# Next: Step 10.x release — README quickstart, commit uv.lock, tag v0.1.0 (HOLD tag for user go).
# 2026-06-01: Phase 2 — Graph community detection (clustering) complete (branch feat/phase2-clustering)
#   New modules:
#     seam/analysis/clustering.py       — pure-Python Louvain (no deps)
#     seam/analysis/cluster_naming.py   — deterministic + opt-in LLM labeling (stdlib urllib)
#     seam/indexer/cluster_index.py     — orchestration bridge; writes clusters + symbols.cluster_id
#     seam/query/clusters.py            — read-only: list_clusters, cluster_members, cluster_peers
#   Updated:
#     seam/query/engine.py              — context() enriched with cluster_id/label/peers
#     seam/server/tools.py              — handle_seam_clusters + handle_seam_context cluster fields
#     seam/server/mcp.py                — seam_clusters tool registered (8 tools total)
#     seam/cli/main.py                  — `seam clusters` command + clustering post-pass in init
#                                         + cluster count in `seam status`
#     seam/config.py                    — SEAM_CLUSTER_NAMING, SEAM_LLM_API_KEY,
#                                         SEAM_LLM_MODEL, SEAM_CLUSTER_MIN_SIZE
#     docs/database/schema.sql          — v4: clusters table + symbols.cluster_id column
#     docs/adr/003-heuristic-execution-flows.md — Phase 2 update note added
#     docs/adr/ADR-007-louvain-clustering.md    — new ADR
#   Test count: 517 (gate green — ruff + mypy + pytest)
#   Schema: v4 (guarded migration from v3; fresh DBs seeded at v4 directly)
#   Config defaults: SEAM_CLUSTER_NAMING=deterministic, SEAM_CLUSTER_MIN_SIZE=2
#   Known limitation: clusters keyed on symbol name → cross-file homonyms collapse (ADR-007)
# Next: docs sync (this session), then benchmark / v0.1.0 release prep
# 2026-06-02: Phase 4 — Node-field enrichment complete (branch feat/phase4-node-enrichment)
#   New modules:
#     seam/indexer/signatures.py    — LEAF: extract_node_fields() → NodeFields
#                                     per-language extraction for Python/TS/JS/Go/Rust
#                                     five fields: signature, decorators, is_exported, visibility, qualified_name
#   Updated:
#     seam/indexer/graph_common.py  — Symbol TypedDict gains the five enrichment fields
#     seam/indexer/graph.py         — Python/TS extractors call extract_node_fields()
#     seam/indexer/graph_go_rust.py — Go/Rust extractors call extract_node_fields()
#     seam/indexer/db.py            — upsert_file writes enrichment cols + _run_migration_v4_to_v5()
#                                     connect() auto-runs pending migrations on open
#     seam/query/engine.py          — context/search/query SELECT the five new columns + decode JSON decorators
#     seam/query/fts.py             — rescore() Signal-6: +15/term for signature matches
#     seam/server/tools.py          — handle_seam_context passes through the five enrichment fields
#     seam/config.py                — SEAM_MAX_SIGNATURE_LEN (default 300)
#     docs/database/schema.sql      — v5: five enrichment cols + FTS5 rebuilt for (name, docstring, signature)
#   Test count: 741 (gate green — ruff + mypy + pytest)
#   Schema: v5 (guarded migration from v4; fresh DBs seeded at v5 directly)
#   Key notes: enrichment cols NULL until full seam init; connect() auto-migrates; signature FTS-searchable
# 2026-06-02: Phase 5 — Import resolution & confidence promotion complete (branch feat/phase5-import-resolution)
#   New modules:
#     seam/analysis/imports.py      — LEAF: extract_import_mappings + resolve_import_source + compute_path_proximity
#                                     per-language import extraction for Python/TS/JS/Go/Rust; never raises
#                                     extension-order resolution: Python[.py/__init__.py], Rust[.rs/mod.rs],
#                                     TS[.ts/.tsx/.d.ts/.js], JS[.js/.mjs/.cjs/index.js], Go[package dir]
#     seam/analysis/builtins.py     — LEAF: is_builtin(name, language) over static frozensets
#                                     conservative vocabulary covering Python/TS/JS/Go/Rust
#   Updated:
#     seam/analysis/confidence.py   — resolve_edge() → Resolution{confidence, resolved_by, best_candidate}
#                                     load_import_mappings(conn, file_path) → list[ImportMapping]
#                                     resolve() kept as backward-compat thin shim
#                                     Resolution order: import-promotion → name-count → builtin → unresolved
#     seam/indexer/pipeline.py      — extract_import_mappings() called per file; upserts import_mappings
#     seam/indexer/db.py            — _run_migration_v5_to_v6() (adds import_mappings table)
#                                     connect() auto-runs pending migrations (now covers v5→v6)
#     seam/query/engine.py          — resolve_edge() threaded through traversal; resolved_by/best_candidate
#                                     added to impact + trace output
#     seam/server/tools.py          — handle_seam_impact + handle_seam_trace pass through resolved_by/best_candidate
#     seam/config.py                — SEAM_BUILTIN_FILTERING, SEAM_IMPORT_RESOLUTION,
#                                     SEAM_MAX_IMPORT_CANDIDATES, SEAM_PROXIMITY_MAX_CANDIDATES
#     docs/database/schema.sql      — v6: import_mappings table + two indexes
#   Test count: 939 (gate green — ruff + mypy + pytest)
#   Schema: v6 (guarded additive migration from v5; fresh DBs seeded at v6 directly)
#   Key notes:
#     - import_mappings NOT backfilled by migration — run seam init to enable Phase 5 resolution
#     - connect() auto-migrates v5→v6 on open (additive: new table only, no column changes)
#     - resolved_by computed at read time — always fresh after per-file watcher re-index
#     - seam_changes + seam_affected deliberately DO NOT use import promotion (byte-stable verdicts)
#     - Go module-qualified imports (github.com/...) out of scope — Go homonyms stay AMBIGUOUS
# Next: benchmarking / v0.1.0 release prep
# 2026-06-02: Phase 6 — Context-Pack Primitive complete (branch feat/phase6-context-pack)
#   New modules:
#     seam/query/pack.py            — LEAF: context_pack(conn, symbol_name) → ContextPack | None
#                                     orchestrates engine.context() + comments.why() into one enriched bundle
#                                     ContextPack: target, callers, callees (NeighborRef), why, cluster_peers, truncated
#                                     batched enrichment: single WHERE name IN (...) — O(1) DB round-trips
#                                     per-file diversity cap (SEAM_PACK_PER_FILE_CAP) applied before global limit
#   Updated:
#     seam/config.py                — SEAM_PACK_NEIGHBOR_LIMIT (default 10)
#                                     SEAM_PACK_PER_FILE_CAP (default 3)
#                                     SEAM_PACK_MAX_COMMENTS (default 10)
#     seam/server/tools.py          — handle_seam_context_pack (thin adapter)
#     seam/server/mcp.py            — seam_context_pack tool registered (10 tools total)
#     seam/cli/main.py              — `seam pack <symbol>` command (--json / --quiet)
#     CLAUDE.md                     — new config knobs documented, pack.py in package layout
#   Test files:
#     tests/unit/test_pack.py               — 14 unit tests (orchestration, enrichment, caps, graceful degradation)
#     tests/integration/test_pack_parity.py — 11 integration tests (handler vs pack parity, JSON envelope, INVALID_INPUT)
#     tests/integration/test_pack_homonym.py— 9 integration tests (per-file cap, diversity, ambiguous target)
#   Test count: 975 (939 baseline + 36 Phase 6, incl. review-fix tests) (gate green — ruff + mypy + pytest)
#   No schema change, no migration, no new deps
# 2026-06-02: Phase 7 — One-Shot `seam sync` complete (branch feat/phase7-seam-sync, issue #31, PR #32)
#   New module:
#     seam/indexer/sync.py          — LEAF: sync(conn, root, *, recompute_clusters, force_clusters,
#                                     naming_mode, llm_api_key, llm_model, min_size) → SyncResult
#                                     filesystem reconcile: mtime pre-filter → SHA-1 confirm
#                                     reuses walk_project + index_one_file + sha1 + delete_file + index_clusters
#                                     delete is existsSync-guarded (roadmap §6.1) — only remove if file truly gone
#                                     FULL cluster recompute gated on graph_changed (or force_clusters)
#                                     SyncResult: added, modified, removed, unchanged, skipped, graph_changed,
#                                       clusters_recomputed, cluster_count (None=skipped / -1=failed / ≥0=ok)
#   Updated:
#     seam/cli/main.py              — `seam sync [path]` command (--json / --quiet / --force-clusters)
#                                     routes through sync.py:sync(); NO MCP tool (CLI-only, server read-only)
#                                     cluster-failure (-1) surfaced as "failed" + warning (mirrors seam init)
#     CLAUDE.md / README.md / docs/ARCHITECTURE.md — Phase 7 documented; stale-clusters gotcha updated
#   Test files:
#     tests/unit/test_sync.py                  — reconcile + gating + cluster-failure (S13) + delete-safety (S14)
#     tests/integration/test_sync_cli.py       — CLI envelope, NO_INDEX, mutual-excl, cluster-failure (SC11)
#     tests/integration/test_sync_clusters.py  — end-to-end cluster freshness + gating + --force-clusters
#   Review: /review + /backend-taste converged on the index_clusters -1 sentinel (surfaced as healthy) —
#     fixed: clusters_recomputed=(count>=0) + CLI "failed" warning; added existsSync delete guard;
#     narrowed CLI except to sqlite3.Error→DB_ERROR (documented catch-all fallback preserves --json envelope)
#   Test count: 1031 (975 baseline + 56 Phase 7, incl. 6 review-fix tests) (gate green — ruff + mypy + pytest)
#   No schema change, no migration, no new deps, no new config knobs (reuses SEAM_CLUSTER_*)
# Next: roadmap item 8 (`seam install`) / v0.1.0 release prep

# ─────────────────────────────────────────────────────────────────────────────
# Phase 10 — Swift support (11 → 12 languages); Kotlin deferred         [#37]
# ─────────────────────────────────────────────────────────────────────────────
#   Approach: same pipeline as Phase 9 — /to-prd → single TDD Sonnet agent (one
#     language = no inter-agent contention) → /review + /backend-taste → fix → /doc → PR+merge.
#   Grammar evidence (the go/no-go step): probed BOTH Swift + Kotlin against ts 0.25.2.
#     Swift  (tree-sitter-swift 0.7.3)  — clean, has_error=False on idiomatic code → ADD.
#     Kotlin (tree-sitter-kotlin 1.1.0) — ERROR nodes on interfaces/objects/ctor-classes;
#       recovered ~1 of 6 symbols on a realistic file → DEFERRED (would silently drop code).
#       No tree-sitter-kotlin-ng on PyPI. See ADR-009.
#   New:
#     seam/indexer/graph_swift.py   — symbol/edge/comment extractors (mirrors graph_go_rust.py)
#                                     class/struct/actor/extension→class, enum→type, protocol→interface
#                                     methods→Type.method; bare-identifier calls only; /// + /** */ docstrings
#     tests/fixtures/sample.swift, tests/unit/test_swift.py (58 tests)
#     docs/adr/009-swift-support.md — Swift added + explicit Kotlin deferral with evidence
#   Updated: config.py (.swift), parser.py (parse_swift via language()), pipeline._dispatch_parser,
#     graph.py (3 dispatch branches), graph_common._find_enclosing_function (Swift branch),
#     signatures_ext.py (_extract_swift), imports_ext.py (_extract_swift/_resolve_swift→[]),
#     builtins.py (_SWIFT_BUILTINS, trimmed to stay <1000 lines)
#   Review (/review + /backend-taste): cross-cutting = 0 findings; correctness = 1 — /** */ block
#     doc-comments dropped (only 'comment'///walked, not 'multiline_comment'). Fixed + regression test.
#   Test count: 1454 (1395 Phase-9 baseline + 58 Swift + 1 review-fix regression). Gate green.
#   No schema change, no migration, MCP tool count stays 10.
# Next: roadmap item 8 (`seam install`) / v0.1.0 release prep. (Kotlin: revisit on robust grammar.)

# ─────────────────────────────────────────────────────────────────────────────
# Agentic-readiness hardening (post-Phase-10) — 3 critical audit fixes; NO installer
# ─────────────────────────────────────────────────────────────────────────────
#   Trigger: end-to-end agentic-readiness audit. Method: built a fresh multi-language
#     repo, ran `seam init`, connected a REAL MCP stdio client (mcp lib, as Claude does),
#     drove all 10 tools + sad paths. Verdict: tools excellent; the edges around them weren't.
#   Findings fixed (critical-only; installer deferred):
#   #1 PyPI name collision — `seam` on PyPI is Seam Labs' smart-lock SDK (v1.145.0); README's
#      `pip install seam` installs the WRONG package. → renamed distribution to `seam-mcp` in
#      pyproject (import pkg + console command stay `seam`); README install now from-source
#      (not published yet); also fixed the bogus `["start","--stdio"]` MCP config (no such flag).
#   #2 MCP error contract — app-level rejections returned {"error","message"} with isError=FALSE
#      (a protocol-compliant agent reads a rejection as success); not-found returned empty content.
#      → new module-level `_finalize(result)` in seam/server/mcp.py, applied to all 10 closures:
#        error-dict sentinel → raise ToolError → FastMCP sets isError=True ("CODE: message");
#        None → {"found": false}; success dict/list pass through unchanged. Handlers/CLI/output.py
#        UNTOUCHED (CLI keeps {ok:false,error:{code,message}}; same code+message, native signal each).
#        FastMCP fact (probed): isError flips ONLY on raise, and content gets an
#        "Error executing tool <name>: " prefix — so clean JSON can't survive a raise; the right
#        cross-transport contract is same-code+message, each transport's native error signal.
#   #3 seam_changes polluted by its own index — init created `.seam/` but no gitignore, so
#      `.seam/seam.db`/-shm/-wal showed as changed "modules". → `seam init` writes `.seam/.gitignore`
#      containing `*` (probed: git stops reporting `.seam/`; written INSIDE `.seam/` so the
#      "nothing outside `.seam/`" property holds). Idempotent (exists() guard).
#   New tests: tests/integration/test_mcp_error_contract.py (8: _finalize unit + in-memory MCP
#     client wiring), tests/integration/test_init_gitignore.py (3: written/hides-from-git/idempotent).
#   Docs: CLAUDE.md (Current Phase + 2 new gotchas), README (from-source install + fixed MCP config),
#     docs/api-contracts/mcp-tools.yaml (error-contract header note).
#   Live re-verify (fresh repo, real MCP client): app-error isError=True ✓, not-found {found:false} ✓,
#     seam_changes excludes .seam ✓, success path intact ✓.
#   Test count: 1465 (1454 + 11). Gate green (ruff + mypy 46 files + pytest). No schema change,
#     no migration, MCP tool count stays 10. Plan: .claude/tasks/agentic-readiness-hardening.md.
# Next: roadmap item 8 (`seam install` / AgentTarget) + actually publish to PyPI as `seam-mcp`.

# ─────────────────────────────────────────────────────────────────────────────
# `seam install` (roadmap item 8) — one-command MCP wiring; Claude + Cursor + Codex
# ─────────────────────────────────────────────────────────────────────────────
#   Why now: the readiness audit's last gap was onboarding — users had to hand-write MCP config.
#   Scope (user-chosen): 3 agents, 2 config formats. Verified each format from docs before coding
#     (same discipline as the Claude probe): Claude .mcp.json needs type:"stdio"; Cursor omits type;
#     Codex is TOML [mcp_servers.<name>] in ~/.codex/config.toml.
#   New package seam/installer/:
#     __init__.py  — TARGETS {claude,cursor,codex} registry + get_target + resolve_seam_command()
#                    (resolves the ABSOLUTE seam path via sys.argv[0] — cwd/PATH-independent at agent launch)
#     core.py      — AgentTarget ABC + InstallResult + shared idempotent JSON install/uninstall_entry
#     jsonfile.py  — LEAF (stdlib json): load/atomic-write/get_in/set_in/delete_in — Claude + Cursor
#     tomlfile.py  — LEAF (tomlkit): load/atomic-write/get/set/delete [mcp_servers.<name>] — Codex
#     claude.py    — .mcp.json (project) / ~/.claude.json projects.<abs-root> (user); entry has type:stdio
#     cursor.py    — .cursor/mcp.json (project) / ~/.cursor/mcp.json (user); no type field
#     codex.py     — ~/.codex/config.toml [mcp_servers.seam] (user scope ONLY); tomlkit round-trip
#   New dep: tomlkit 0.15.0 (stdlib tomllib is read-only; tomlkit preserves user comments/formatting).
#   seam/cli/install.py — `seam install` + `seam uninstall` Typer commands (registered onto app in
#     main.py with 2 lines — main.py is already >1000 lines so logic lives in its own module).
#     Flags: --target claude|cursor|codex|all, --location project|user, --print-config, --json.
#     Idempotent (deep-equal→unchanged), atomic temp+rename, .backup on corrupt config, preserves
#     other servers. Codex+project (or any unsupported target,location): explicit single target →
#     INVALID_INPUT; under --target all → skipped-with-reason (not an error).
#   Review (self, /backend-taste lens): removed an unused detect() method (YAGNI); tightened
#     _select_targets typing to list[AgentTarget]|None.
#   New tests: tests/unit/test_installer.py (18: jsonfile/tomlfile/core/claude/cursor/codex/registry),
#     tests/integration/test_install_cli.py (9: create/idempotent/print-config/all-user/all-project-skips-
#     codex/no-index-warn/unknown-target/codex-project-invalid/uninstall). ALL use tmp paths + tmp HOME —
#     never the real ~/.claude.json/~/.cursor/~/.codex.
#   Live verify (real seam binary, fake HOME): all 3 configs written correctly; server BOOTS from the
#     written .mcp.json command+args (10 tools, seam_search ok); re-run → unchanged; uninstall → removed.
#   Test count: 1492 (1465 + 27). Gate green (ruff + mypy 54 files + pytest). CLI-only — NO new MCP tool;
#     tool count stays 10. No schema change, no migration. Plan: .claude/tasks/seam-install.md.
# Next: v0.1.0 — publish to PyPI as `seam-mcp`; add more agent targets (one file each) as needed.

# ─────────────────────────────────────────────────────────────────────────────
# CLI-only completion + optional-MCP install profile
# ─────────────────────────────────────────────────────────────────────────────
#   User goal: use Seam fully from the terminal without the MCP server, and an install
#   profile that doesn't pull the mcp stack. Finding: the CLI already ran without MCP for
#   11/13 commands (they query .seam/seam.db directly). Only query/search/context were MCP-only.
#   New: seam/cli/read.py — `seam query` / `search` / `context` over the transport-agnostic
#     handle_seam_query/_search/_context (tools.py is MCP-free). Flags mirror the other read
#     commands: --json/--quiet; context has --lean (verbose=False). Registered onto app in main.py.
#     context not-found → {found:false} success (matches the MCP _finalize + impact contract).
#   Optional MCP:
#     - pyproject: `mcp` moved OUT of [project] dependencies INTO [project.optional-dependencies]
#       server=["mcp>=1.0.0"]; also added to [dependency-groups].dev so tests still exercise it.
#       (dependency-groups don't ship to pip users → `pip install seam-mcp` is mcp-free.)
#     - main.py: `from seam.server.mcp import create_server` is now LAZY inside start() via
#       _load_create_server(); missing mcp → friendly install hint + exit 1 (fails fast, before
#       spawning the watcher). mcp is imported at top level in exactly ONE module (server/mcp.py),
#       tools.py + the whole read path are mcp-free — so the CLI imports cleanly without mcp.
#   DISTRIBUTION BUG FIXED (surfaced by a real wheel install, not the editable dev install):
#     seam init read docs/database/schema.sql (OUTSIDE the seam/ package) → FileNotFoundError on
#     ANY `pip install` (docs/ isn't packaged). Fix: hatch force-include docs/database/schema.sql →
#     seam/_data/schema.sql; db.py loads packaged-first (_PACKAGED_SCHEMA_PATH) with dev fallback
#     (_DEV_SCHEMA_PATH). docs/ stays the single canonical source. Guard test added.
#   New tests: tests/integration/test_cli_read.py (8), tests/integration/test_no_mcp_profile.py (2:
#     read works w/ mcp poisoned; start exits 1), tests/unit/test_schema_packaging.py (2: schema
#     resolves + force-include configured).
#   Live verify (fresh venv, `uv pip install .` core-only → NO mcp): `import mcp` fails; seam init/
#     search/query/context all WORK; seam start → install hint + exit 1. (This is what caught the
#     schema-packaging bug — the editable dev install never would.)
#   Test count: 1504 (1492 + 12). Gate green (ruff + mypy 55 files + pytest). MCP tool count stays 10.
#     Plan: .claude/tasks/cli-query-context-search.md.
# Next: v0.1.0 — publish to PyPI as `seam-mcp` (CLI: `pip install seam-mcp`; agents: `[server]` extra).

# ─────────────────────────────────────────────────────────────────────────────
# Frontend Explorer — Tasks F5+F6: DetailPanel, ClusterLegend, build wiring
# ─────────────────────────────────────────────────────────────────────────────
#   TDD: wrote failing tests in web/src/__tests__/DetailPanel.test.tsx (21 tests)
#     confirmed failures (import error — components didn't exist), then implemented.
#   New frontend components:
#     web/src/components/DetailPanel.tsx  — right-side detail panel driven by useSymbol():
#       renders name (heading), all definitions (file:line each — homonym support),
#       signature + docstring from first def, WHY/HACK/NOTE comment rows with kind badges,
#       callers/callees counts, cluster label + colour stripe, and the ClusterLegend at the
#       bottom (via useClusters — TanStack Query caches it from the landing page call).
#     web/src/components/ClusterLegend.tsx — standalone colour-swatch legend:
#       maps all cluster_ids → label + colour via clusterColor() (same hash as SymbolNode
#       stripe and LandingPage dots — NO duplication). Empty list renders gracefully.
#   App.tsx updated: replaced the F4 "detail panel coming in F5" placeholder toast with
#     the real <DetailPanel selectedSymbol={selectedSymbol} />, shown when canvas is active.
#   Makefile: added `build-web:` target (cd web && npm ci && npm run build).
#     .PHONY updated to include build-web.
#   README.md: added `## Seam Explorer` section (seam serve quickstart, feature list)
#     and `## Development` now documents `make build-web`. Release ritual documented:
#     make build-web → uv build → uv publish.
#   Frontend tests: 58 passing (37 baseline + 21 new DetailPanel+ClusterLegend tests).
#   npm run typecheck: clean. npm run build: succeeds (seam/_web/ ~530 kB bundle).
#   Python gate: unchanged (1538+ passed — no Python files modified).
#   Plan: .claude/tasks/seam-explorer-frontend.md (tasks F5+F6 complete).
# Next: v0.1.0 — make build-web → uv build → uv publish (release ritual now documented).

# ─────────────────────────────────────────────────────────────────────────────
# Seam Explorer — Phase 2: impact / trace / changes / constellation + polish
# ─────────────────────────────────────────────────────────────────────────────
#   Branch: feat/explorer-phase2 (one PR, inline build, second-search-box trace).
#   Plan: .claude/tasks/seam-explorer-phase2.md.
#
#   BACKEND (4 HTTP routes over existing handlers — ZERO query-logic dup):
#     GET /api/impact        — handle_seam_impact (verbose=False → lean entries)
#     GET /api/trace         — handle_seam_trace (paths only)
#     GET /api/changes       — handle_seam_changes (NOT_A_GIT_REPO → 400)
#     GET /api/constellation — NEW pure leaf graph_api.build_constellation():
#       clusters + weighted inter-cluster links; homonym-safe name→cluster map;
#       never raises; pre-v4/empty → {clusters:[],links:[]}.
#     Pydantic models added to web.py (TS codegen source). 19 new py tests.
#     No schema change, no migration, MCP tool count stays 10.
#
#   FRONTEND (web/): all overlays derive from the base graph via useMemo —
#     toggling an overlay off restores the original view byte-for-byte.
#     F1 hooks: useImpact/useTrace/useChanges/useConstellation (opt-in enabled).
#     F2 polish: SymbolNode exported badge + visibility chip + tier ring; Legend
#       (confidence/clusters/risk); FilterBar (kind + confidence toggles).
#     F3 impact overlay: paint blast radius by tier, dim rest, off-canvas cards,
#       risk-summary chip. F4 trace: second "Trace to…" box → bold shortest path.
#     F5 ChangesDrawer: changed symbols + risk badge + NOT_A_GIT_REPO notice.
#     F6 ConstellationCanvas: Overview⇄Neighborhood toggle; region click drills in.
#     Pure helpers (riskTier/edgeFilter/impactOverlay/tracePath/constellationLayout)
#       unit-tested in isolation.
#
#   GATES: python 1558 passed, ruff + mypy clean. web 92 passed, tsc clean,
#     production build OK (seam/_web ~551 kB). Live smoke (4853-symbol index):
#     impact/trace/constellation/changes/SPA all return correct data.
# Next: /review + manual QA, then merge feat/explorer-phase2.
