Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
425326d
feat(helix-decisions): rename adr-search to helix-decisions
kevinmichaelchen Jan 6, 2026
edb04c2
docs(helix-decisions): add TODOs for default location and git hooks
kevinmichaelchen Jan 6, 2026
b1b2b14
docs: add Phase 3 HelixDB integration specs and corrections
kevinmichaelchen Jan 6, 2026
caf0c99
docs: add specs for all shared crates
kevinmichaelchen Jan 6, 2026
2ee2391
docs: ADR-004 trait-based storage architecture
kevinmichaelchen Jan 6, 2026
21943a8
feat(helix-daemon): add crate skeleton with protocol types and helixd…
kevinmichaelchen Jan 6, 2026
a838bd8
docs: update specs to reference shared helixd daemon
kevinmichaelchen Jan 6, 2026
a9c4755
feat(helix-daemon): implement IPC server and client (Phase 2)
kevinmichaelchen Jan 6, 2026
2ce77bf
feat(helix-daemon): implement queue management with coalescing (Phase 3)
kevinmichaelchen Jan 6, 2026
6b8abbc
feat(helix-daemon): add idle timeout for auto-shutdown (Phase 4)
kevinmichaelchen Jan 6, 2026
9811b8f
feat(helix-decisions): integrate with helix-daemon for sync coordination
kevinmichaelchen Jan 6, 2026
ee395a2
docs: update tasks.md files to reflect daemon implementation status
kevinmichaelchen Jan 6, 2026
67744a7
feat(helix-decisions): add manifest and git_utils modules (Phase 3.1)
kevinmichaelchen Jan 6, 2026
2ea5b85
feat(helix-decisions): implement HelixDB backend and wire storage (Ph…
kevinmichaelchen Jan 7, 2026
666e96d
docs: update specs for Phase 3.2 implementation details
kevinmichaelchen Jan 7, 2026
bc014cf
feat(helix-decisions): add SyncStats tracking to sync flow
kevinmichaelchen Jan 7, 2026
347c7c2
test(helix-decisions): add integration tests for Phase 3.3
kevinmichaelchen Jan 7, 2026
18d43b4
docs: mark Phase 3.3 tasks complete in helix-decisions specs
kevinmichaelchen Jan 7, 2026
cf2b00d
fix(helix-decisions): correct Phase 3.3 delta detection and relations…
kevinmichaelchen Jan 7, 2026
0b1f708
feat(helix-decisions): enforce uuid validation
kevinmichaelchen Jan 7, 2026
d189ca3
chore: update gitignore and mark check command task complete
kevinmichaelchen Jan 16, 2026
5fed7fc
fix(helix-decisions): update for helix-db SecondaryIndex API change
kevinmichaelchen Jan 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions .decisions/004-trait-based-storage-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# ADR-004: Trait-Based Storage Architecture

**Status:** Accepted
**Date:** 2026-01-06
**Deciders:** Kevin Chen
**Tags:** architecture, storage, helixdb, dependency-injection

## Context and Problem Statement

helix-tools uses HelixDB for persistent graph and vector storage. The question is: how should tools integrate with HelixDB?

**Option A (rejected):** Every tool directly depends on HelixDB
- Tight coupling
- Tools can't be tested without HelixDB
- Shared crates become HelixDB-aware

**Option B (accepted):** Trait-based architecture with dependency injection
- Loose coupling
- Tools define their own storage interfaces
- HelixDB is an implementation detail, not a dependency

## Decision Drivers

1. **Testability** — Tools should be testable without external dependencies
2. **Loose coupling** — Avoid forcing HelixDB into every crate
3. **Dependency inversion** — High-level modules shouldn't depend on low-level modules
4. **Flexibility** — Easy to add alternative implementations (memory, mock, etc.)
5. **Clarity** — Each tool's storage needs should be explicitly defined

## Decision

**Each tool defines its own storage trait. HelixDB implementations are provided separately.**

### Pattern

```
tool/
├── src/
│ ├── storage/
│ │ ├── mod.rs # trait DecisionStore { ... }
│ │ ├── helix.rs # impl DecisionStore for HelixBackend
│ │ └── memory.rs # impl DecisionStore for MemoryBackend (tests)
│ └── lib.rs
```

### Example: helix-decisions

```rust
// storage/mod.rs — The trait (no HelixDB dependency)
pub trait DecisionStore: Send + Sync {
fn insert(&self, decision: &Decision) -> Result<()>;
fn get(&self, id: &str) -> Result<Option<Decision>>;
fn search(&self, query_embedding: &[f32], limit: usize) -> Result<Vec<SearchResult>>;
fn get_chain(&self, id: &str) -> Result<Vec<Decision>>;
fn get_related(&self, id: &str) -> Result<Vec<(Decision, RelationType)>>;
}

// storage/helix.rs — HelixDB implementation
pub struct HelixDecisionStore {
engine: HelixGraphStorage,
}

impl DecisionStore for HelixDecisionStore {
fn insert(&self, decision: &Decision) -> Result<()> {
// Uses HelixDB's native graph storage
// LMDB persistence is built-in
}
// ...
}

// storage/memory.rs — In-memory implementation for tests
pub struct MemoryDecisionStore {
decisions: HashMap<String, Decision>,
embeddings: HashMap<String, Vec<f32>>,
}

impl DecisionStore for MemoryDecisionStore { ... }
```

### Shared Crates: No HelixDB Dependencies

| Crate | Purpose | HelixDB Dependency |
|-------|---------|-------------------|
| helix-config | Configuration loading | NO — just documents paths |
| helix-id | ID generation | NO — pure utility |
| helix-embeddings | Text embeddings | NO — returns `Vec<f32>` |
| helix-discovery | Git root discovery | NO — pure utility |

Consumers decide what to do with embeddings. helix-embeddings doesn't know or care about storage.

## Consequences

### Positive

- **Testability** — Tests use `MemoryDecisionStore`, no HelixDB needed
- **Loose coupling** — Tools work with trait interface, not HelixDB directly
- **Clear contracts** — Each tool's storage needs are explicitly defined
- **Flexibility** — Easy to add backends (SQLite, Postgres, etc.) later
- **No shared HelixDB dependency** — Shared crates stay pure

### Negative

- **More code** — Each tool defines its own trait (but traits are small)
- **No shared storage trait** — Tools can't share a single `VectorStore` trait

### Neutral

- **helix-storage removal** — We don't need a shared storage abstraction; each tool defines its own

## HelixDB's Built-In Persistence

HelixDB already handles persistence via LMDB:

```rust
// From helix-db/src/helix_engine/storage_core/mod.rs
let graph_env = unsafe {
EnvOpenOptions::new()
.map_size(db_size * 1024 * 1024 * 1024)
.max_dbs(200)
.open(Path::new(path))? // Creates data.mdb + lock.mdb
};
```

This means:
- Data persists automatically across runs
- No JSON serialization needed
- No "index rebuild" on startup
- Tools just open HelixDB at a path and data is there

## Implementation Notes

### Storage Paths

Tools store HelixDB data in project-local directories:

```
{project}/.helix/data/{tool}/
├── data.mdb # LMDB data file
└── lock.mdb # LMDB lock file
```

### Configuration

helix-config documents HelixDB settings but doesn't depend on HelixDB:

```toml
# ~/.helix/config/config.toml
[helix_db]
map_size_mb = 1024
max_readers = 200
```

Tools read this config and pass it to their HelixDB backend.

## Alternatives Considered

### Shared `VectorStore` Trait in helix-storage

**Rejected** because:
- Different tools have different storage needs (graph traversal vs simple CRUD)
- Forces a lowest-common-denominator interface
- helix-storage was scaffolding, not a long-term solution

### Direct HelixDB Dependency in All Tools

**Rejected** because:
- Tight coupling
- Can't test without HelixDB
- Violates dependency inversion

## Related Decisions

- ADR-003: Binary Installation Strategy (distribution)
- Future: ADR for embedding model selection

## References

- [Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)
- [HelixDB Storage Core](https://github.com/HelixDB/helix-db/blob/main/helix-db/src/helix_engine/storage_core/mod.rs)
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Cargo.lock
.helix/helix.db/
.helix/models/
.helix-map/
.fastembed_cache/

# IDE
.idea/
Expand Down
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
[workspace]
resolver = "2"
members = [
"adr-search",
"hbd",
"hbd-ui/src-tauri",
"helix-decisions",
"helix-docs",
"helix-map",
"helix-repo",
"shared/helix-id",
"shared/helix-config",
"shared/helix-storage",
"shared/helix-embeddings",
"shared/helix-discovery",
"shared/helix-daemon",
# "shared/helix-embed",
# "shared/helix-search",
# "shared/helix-chunk",
Expand All @@ -22,6 +26,9 @@ repository = "https://github.com/kevinmichaelchen/helix-tools"
authors = ["Kevin Chen"]

[workspace.dependencies]
# HelixDB - Graph-vector database
helix-db = { git = "https://github.com/HelixDB/helix-db.git", features = ["vectors"] }

# Async runtime
tokio = { version = "1.40", features = ["full"] }

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ AI-native developer tools powered by [HelixDB][helixdb]. Git-first, offline-firs
| Tool | Description | Status |
|------|-------------|--------|
| **[hbd][hbd]** | Git-first issue tracker | Active |
| **[adr-search][adr-search]** | Semantic search over ADRs | Scaffolded |
| **[helix-decisions][helix-decisions]** | Decision graph with semantic search | Scaffolded |
| **[helix-docs][helix-docs]** | Documentation cache for AI research | Scaffolded |
| **[helix-map][helix-map]** | Codebase structure indexer | PoC |
| **[helix-repo][helix-repo]** | Repository clone manager | Scaffolded |
Expand Down Expand Up @@ -55,7 +55,7 @@ hbd create "My first issue" --type task
[config]: https://kevinmichaelchen.github.io/helix-tools/docs/configuration

<!-- Tools -->
[adr-search]: ./adr-search/
[helix-decisions]: ./helix-decisions/
[hbd]: ./hbd/
[helix-docs]: ./helix-docs/
[helix-map]: ./helix-map/
Expand Down
48 changes: 0 additions & 48 deletions adr-search/Cargo.toml

This file was deleted.

Loading
Loading