|
| 1 | +--- |
| 2 | +id: ADR-2026-06-26-001 |
| 3 | +title: Loom code weave — source of the code graph (native parser vs. external indexer) |
| 4 | +status: draft |
| 5 | +created: 2026-06-26 |
| 6 | +updated: 2026-06-26 |
| 7 | +agent: claude-opus-4-8-1m |
| 8 | +confidence: high |
| 9 | +review_required: true |
| 10 | +# --- Approval workflow (optional, fill at review time) --- |
| 11 | +# reviewed_by: <reviewer-id> |
| 12 | +# reviewed_at: YYYY-MM-DD |
| 13 | +# review_outcome: approved |
| 14 | +risk_level: medium |
| 15 | +eu_ai_act_risk: not_applicable |
| 16 | +iso_42001_clause: [] |
| 17 | +alternatives_documented: [codebase-memory-mcp-as-dependency, code-graph-in-loom-only, no-feature-gate] |
| 18 | +api_changes: [] |
| 19 | +tags: [loom, experimental, code-weave, knowledge-graph, tree-sitter, architecture] |
| 20 | +related: [CHARTER-02-code-weave, ADR-2026-06-02-001] |
| 21 | +supersedes: [] |
| 22 | +--- |
| 23 | + |
| 24 | +# ADR: Loom code weave — source of the code graph (native parser vs. external indexer) |
| 25 | + |
| 26 | +## Status |
| 27 | + |
| 28 | +draft |
| 29 | + |
| 30 | +**Note**: This document was created by an AI agent and requires human review. |
| 31 | + |
| 32 | +> **Immutability Rule**: Once an ADR reaches `accepted` status, it MUST NOT be modified. If |
| 33 | +> the decision changes, create a new ADR with `supersedes: ADR-2026-06-26-001`. |
| 34 | +
|
| 35 | +## Context |
| 36 | + |
| 37 | +Loom today holds two disjoint graphs: the **document** knowledge graph (Spec 001) and the |
| 38 | +**architecture plan** (Spec 002), whose only bridge to code is **file-level** glob matching — |
| 39 | +a component is a bucket of globs, nothing inside it is a node. Spec 003 ("code weave") fuses |
| 40 | +**code symbols** into the same fabric so a function sits next to the ADR that decided it, the |
| 41 | +Charter modifying it, and the TDE flagging its debt — *"conocimiento extendido"*. |
| 42 | + |
| 43 | +The motivating external reference is **`codebase-memory-mcp`** (DeusData, MIT): it parses a |
| 44 | +repo with tree-sitter (≈158 grammars) into a symbol graph in SQLite and exposes it to agents |
| 45 | +over MCP, reporting large token savings for structural queries (its own arXiv preprint |
| 46 | +reports ~10× fewer tokens at a measured **−9-point answer-quality** trade-off, 83% vs 92% — |
| 47 | +not a free lunch). It proves the *shape of the idea* (code as a navigable graph) but indexes |
| 48 | +the half StrayMark does **not** care about in isolation; StrayMark's value is the **weave with |
| 49 | +governance**. |
| 50 | + |
| 51 | +This ADR records the one decision in Spec 003 with repo-wide consequences: **where the code |
| 52 | +graph comes from** — built natively in our own parsing layer, or sourced from an external |
| 53 | +indexer such as `codebase-memory-mcp`. It extends `ADR-2026-06-02-001`, whose central |
| 54 | +rationale ("one parser → drift structurally impossible") now applies to code as well as |
| 55 | +documents. |
| 56 | + |
| 57 | +## Decision |
| 58 | + |
| 59 | +We will: |
| 60 | + |
| 61 | +1. **Build the code graph natively**, reusing the project's existing tree-sitter layer |
| 62 | + (`arborist-metrics`, already a CLI dependency for `straymark analyze`), not by consuming |
| 63 | + `codebase-memory-mcp` (or any external indexer) as a runtime dependency or sidecar process. |
| 64 | +2. **Keep the code graph in `straymark-core` behind a default-off `codegraph` cargo feature**, |
| 65 | + with `arborist-metrics` enabled only by that feature. The default `core` build, the |
| 66 | + document knowledge graph, and `straymark audit` stay free of any code-parsing dependency. |
| 67 | +3. **Compose the weave, never annotate.** The `document → symbol` cross-edge is the |
| 68 | + composition `document → file` (already extracted by `core::architecture::gather`) ∘ |
| 69 | + `file → symbol` (the new code graph). This requires **no new frontmatter** (it preserves |
| 70 | + Spec 002 NFR5) and degrades to file-level when no line/parse evidence exists. |
| 71 | +4. **Leave `core::graph` (the audit oracle) unmutated.** Code nodes live in a separate |
| 72 | + `core::codegraph`; the weave is an overlay merged only behind `/api/graph?include=code`, so |
| 73 | + the bare `straymark audit` / `/api/graph` output stays byte-for-byte unchanged. |
| 74 | +5. **Defer `CALLS`/`IMPORTS`** (the call graph) to a later milestone (C3), shipped |
| 75 | + language-by-language; the file-level weave (C1) and function nodes (C2) come first. |
| 76 | + |
| 77 | +## Alternatives Considered |
| 78 | + |
| 79 | +### 1. Consume `codebase-memory-mcp` as a dependency / sidecar |
| 80 | +- **Description**: Run the external indexer (or link its engine), read its SQLite symbol |
| 81 | + graph, and weave governance onto it. |
| 82 | +- **Pros**: 158 languages out of the box; mature, fast, sub-ms structural queries; no symbol- |
| 83 | + extraction code to write; an arXiv-backed design. |
| 84 | +- **Cons**: Adds a **second process + external SQLite store + MCP/IPC surface** to a tool |
| 85 | + whose entire posture is "one read-only loopback binary" (Spec 001 §9, NFR4; `ADR-2026-06-02-001` |
| 86 | + §6). Introduces a **second parser** with its own grammar set — exactly the drift |
| 87 | + `ADR-2026-06-02-001` was written to prevent, now between "what *it* calls a symbol" and what |
| 88 | + our analysis does. Contradicts "single static binary, zero deps" for Loom and drags a daemon |
| 89 | + lifecycle into `straymark loom serve`. |
| 90 | +- **Why not**: The whole point of the weave is that it shows the *same* code truth the rest of |
| 91 | + StrayMark would compute. One parser family + one binary + loopback-only outweigh the |
| 92 | + convenience of a ready-made 158-language index. It remains valuable as **conceptual |
| 93 | + reference** and a *possible* optional post-graduation enrichment source — not the v0 engine. |
| 94 | + |
| 95 | +### 2. Put the code graph in `straymark-loom` only (keep `core` parser-free forever) |
| 96 | +- **Description**: Build the code graph and weave inside the Loom crate; `core` never gains a |
| 97 | + code-parsing dependency, not even optional. |
| 98 | +- **Pros**: `core` stays minimal; the CLI's `cargo publish` is untouched. |
| 99 | +- **Cons**: The CLI's textual companion (Spec 003 FR7, "what governs `path::symbol`?") and the |
| 100 | + consistency test (acceptance §5) both need the **pure** weave in `core`; splitting the pure |
| 101 | + function across crates duplicates it or couples `cli` → `loom`. |
| 102 | +- **Why not**: A **default-off cargo feature** gives the same "zero default cost" as keeping it |
| 103 | + out of `core`, without splitting the pure function. `core` stays parser-free unless a |
| 104 | + consumer asks for `codegraph`. Revisit only if the feature graph proves awkward. |
| 105 | + |
| 106 | +### 3. Add `arborist-metrics` to `core` unconditionally (no feature gate) |
| 107 | +- **Description**: Make the code graph always available in `core`. |
| 108 | +- **Pros**: Simplest dependency story; no `--features` juggling. |
| 109 | +- **Cons**: Forces a tree-sitter stack into **every** `core` consumer, including the lean |
| 110 | + document-only CLI paths and `straymark audit`; inflates build time and the `opt-level=z` CLI |
| 111 | + footprint for users who never weave. |
| 112 | +- **Why not**: Violates Spec 003 NFR6 ("default cost unchanged"). The feature gate is cheap |
| 113 | + insurance; CI keeps a `--no-default-features` build green to enforce it. |
| 114 | + |
| 115 | +## Consequences |
| 116 | + |
| 117 | +### Positive |
| 118 | +- One parser family → the code graph cannot silently disagree with the rest of StrayMark |
| 119 | + (the `ADR-2026-06-02-001` drift argument, extended to code). |
| 120 | +- Loom stays a single read-only loopback binary — no daemon, no external store, no IPC. |
| 121 | +- No new frontmatter for adopters (the weave is by composition); the file-level rung works |
| 122 | + with zero annotation. |
| 123 | +- The audit oracle is provably untouched (code merged only behind `include=code`). |
| 124 | +- Default builds pay nothing (feature-gated); `--no-default-features` stays viable. |
| 125 | + |
| 126 | +### Negative |
| 127 | +- We write **symbol-extraction code ourselves** rather than getting 158 languages free; the |
| 128 | + native parser covers ~12 languages, and `CALLS`/`IMPORTS` must be authored per-language (C3). |
| 129 | +- `arborist-metrics`' public surface may expose only a function's start line + complexity, not |
| 130 | + a full span; the C2 line-range intersection may need its richer internal types or an |
| 131 | + approximated span (Spec 003 §14). |
| 132 | +- A new (optional) code-parsing dependency enters `core`, gated but present in `Cargo.toml`. |
| 133 | + |
| 134 | +### Neutral |
| 135 | +- `codebase-memory-mcp` is not adopted now but explicitly left open as a *possible* optional |
| 136 | + enrichment source after graduation (not a v0 dependency). |
| 137 | + |
| 138 | +### Quality Impact Assessment |
| 139 | + |
| 140 | +| Quality Characteristic (ISO 25010:2023) | Impact | Description | |
| 141 | +|-----------------------------------------|--------|-------------| |
| 142 | +| Functional Suitability | + | Governance and code become one navigable graph (the headline capability) | |
| 143 | +| Performance Efficiency | ~ | Parsing per FS event is the watch-item; mitigated by per-file content-hash cache (C2) | |
| 144 | +| Compatibility | + | One parser family; the audit oracle and document graph stay byte-identical | |
| 145 | +| Maintainability | + | Pure `weave` mirrors the proven `project` split; no second indexer to track | |
| 146 | +| Security | + | No new process/store/network surface; Loom stays loopback-only read-only | |
| 147 | +| Flexibility | ~ | Native means per-language work for CALLS/IMPORTS, but full control of the model | |
| 148 | + |
| 149 | +## Affected Components |
| 150 | + |
| 151 | +| Component | Type of Change | Impact | |
| 152 | +|-----------|----------------|--------| |
| 153 | +| `straymark-core` (`core/`) | New `codegraph` + `weave` modules (feature-gated); `architecture::gather` refactor | High | |
| 154 | +| `straymark-cli` (`cli/`) | New weave-facing textual path; `audit`/`validate` unchanged | Low | |
| 155 | +| `straymark-loom` (`experiment-loom/`) | Code layer + weave endpoints + drill-down | Medium | |
| 156 | +| `experiment-loom/README.md` | Non-goals reversed (AST extractor allowed; no-new-frontmatter kept) | Low | |
| 157 | + |
| 158 | +## Implementation Plan |
| 159 | + |
| 160 | +1. C1 — file-level weave (no parser): `codegraph` (files only) + `weave` + the `WeaveLinks` |
| 161 | + refactor + CLI textual companion; then a `loom-0.x` file layer. |
| 162 | +2. C2 — symbol nodes via `arborist-metrics` + line-range intersection + per-file cache. |
| 163 | +3. C3 — `CALLS`/`IMPORTS` (deferred), language-by-language. |
| 164 | +4. C4 — viz scaling (collapse-by-component). |
| 165 | + |
| 166 | +(Detail in `experiment-loom/specs/003-code-weave/plan.md` and `tasks.md`.) |
| 167 | + |
| 168 | +## Success Metrics |
| 169 | + |
| 170 | +- After C1, `/api/graph` (no `include=code`) and `straymark audit` are byte-for-byte unchanged |
| 171 | + (oracle intact), while `/api/graph?include=code` adds file nodes + woven cross-edges. |
| 172 | +- A `--no-default-features` build of `straymark-core` compiles with **no** code-parsing |
| 173 | + dependency. |
| 174 | +- The CLI textual weave and the Spec 002 `project` agree on a fixture corpus (consistency |
| 175 | + test). |
| 176 | + |
| 177 | +## Validation Criteria |
| 178 | + |
| 179 | +| Metric | Target Value | Measurement Method | Timeline | |
| 180 | +|--------|-------------|-------------------|----------| |
| 181 | +| Audit-oracle regression | 0 | diff `/api/graph` & `straymark audit` vs pre-weave | C1 | |
| 182 | +| Default-build parser deps | 0 | `cargo tree --no-default-features` shows no tree-sitter | C1 | |
| 183 | +| Weave↔projection consistency | exact match | fixture test (analogous to `where_is_consistent_with_charter_list`) | C1 | |
| 184 | +| Symbol attribution correctness | file-level fallback on no line info | unit test on degraded provenance | C2 | |
| 185 | + |
| 186 | +## References |
| 187 | + |
| 188 | +- `experiment-loom/specs/003-code-weave/spec.md`, `plan.md`, `tasks.md` |
| 189 | +- `experiment-loom/CHARTER-02-code-weave.md` |
| 190 | +- `ADR-2026-06-02-001` (Loom stack — the "one parser, no drift" principle this extends) |
| 191 | +- Reused in-repo infra: `core/src/architecture/{projection,gather}.rs`, `core/src/graph.rs`, |
| 192 | + `core/src/ailog.rs`, `cli/src/analysis_engine.rs` (existing `arborist-metrics` usage) |
| 193 | +- Inspiration (idea only, not a dependency): `codebase-memory-mcp` (DeusData) — code as a |
| 194 | + navigable graph + MCP traversal |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## Revision History |
| 199 | + |
| 200 | +| Date | Author | Change | |
| 201 | +|------|--------|--------| |
| 202 | +| 2026-06-26 | claude-opus-4-8-1m | Initial creation (draft, pending human review) | |
| 203 | + |
| 204 | +<!-- Template: StrayMark | https://strangedays.tech --> |
0 commit comments