Responsibilities
What each file owns and what it should not contain.
| File | Owns | Should not own |
|---|---|---|
domain.ts | Types and errors. | SDK calls or IO. |
files.ts | File discovery and adaptation. | RAG search or completion. |
rag.ts | Chunking and prompt. | Model lifecycle. |
qvac.ts | QVAC SDK integration. | UI or folder walking. |
store.ts | Local JSON persistence. | Model calls. |
locallens.ts | Product workflow. | HTTP details or DOM code. |
cli.ts | Terminal interface. | RAG internals. |
server.ts | HTTP interface. | Business logic. |
ui/ | Browser interaction. | QVAC calls. |
Two columns matter here. The "owns" column is what you modify when you're working on a feature in that area. The "should not own" column is what to keep out. When a change pulls in something from the right column, that's a sign the change wants a different home.
Why each rule
domain.ts should not own SDK calls or IO
Domain types are imported by every other file. If they reach for QVAC or the filesystem, the dependency direction inverts and the whole graph becomes circular. Keep this file pure.
files.ts should not own RAG search or completion
The file adapter only produces LocalDocument[]. Chunking and
embedding happen later, in rag.ts and qvac.ts. If you ever
feel the pull to embed during file discovery — say, to deduplicate
— resist it. Add a separate step.
rag.ts should not own model lifecycle
rag.ts calls ragChunk, but it doesn't load models. The QVAC
gateway manages that. Change models and qvac.ts changes once.
rag.ts keeps working.
qvac.ts should not own UI or folder walking
The gateway is your single source of truth for "what does QVAC do?". It doesn't read folders, render messages, or know about HTTP. That's why the five-call surface fits in your head.
store.ts should not own model calls
The JSON store is for what brains exist and what chunks they
have. It never embeds, searches, or completes. When a brain is
deleted, store.ts removes the entry. The workflow class handles
the QVAC side.
locallens.ts should not own HTTP or DOM code
LocalLensApp is the workflow class. The CLI calls it, the
server calls it, and the UI calls it transitively. None of them
should leak their concerns into this class. If request.headers
ever shows up here, something is wrong.
cli.ts should not own RAG internals
The CLI is one of two thin entry points over LocalLensApp. It
knows about argv and console.log. It doesn't know about chunk
sizes, embeddings, or prompts. A flag that would change RAG
behaviour belongs in the workflow layer, not the CLI handler.
server.ts should not own business logic
Same shape as the CLI: thin. The server is route-matching, JSON
parsing, and error mapping. The moment you find yourself writing
branchy logic in a route handler, it belongs in LocalLensApp
instead.
ui/ should not own QVAC calls
The browser can't talk to QVAC directly anyway, but the rule still matters: the UI talks only to the server's JSON API. It doesn't duplicate chunk size constants, model names, or workspace IDs.
Two entry points, one core
Notice that the CLI and the server are both thin. They sit on
the same core (LocalLensApp) and provide different surfaces.
That's why adding a feature to the workflow layer benefits both —
the CLI gets it the moment the server's route does.