CLAUDE.md · git:20260816.d006e72 · 2026-08-16 · sha256 dfc54411c289180a

CLAUDE.md git:20260816.d006e72A

Immutable. This exact content is served forever at /api/v1/blob/dfc54411c289180a.

# MeMesh — instructions for AI coding assistants

This file is a **pointer**, on purpose. It used to carry its own copy of the
module tree, the dependency list and the development standards, and a copy is a
thing that drifts. It was the last file in the repository still quoting a
benchmark figure (95.40% R@5) that release 4.2.11 was spent proving wrong, and
its test count was 44 behind. It was also untracked, so no reviewer ever saw it
change. Both problems had one cause: it duplicated documents that already
exist, are already public, and are already checked by CI.

So — **read the real documents.** Do not restate them here.

| Question | Read |
|---|---|
| How do I contribute, what must a PR include, which docs move with a code change | [CONTRIBUTING.md](CONTRIBUTING.md) |
| What are the modules, how does data flow, why is it built this way | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) |
| What is the MCP / HTTP / CLI surface, exactly | [docs/api/API_REFERENCE.md](docs/api/API_REFERENCE.md) |
| What does the product do, how is it installed | [README.md](README.md) |
| Colour, type, spacing, interaction — before ANY dashboard change | [DESIGN.md](DESIGN.md) |
| How do I report a vulnerability | [SECURITY.md](SECURITY.md) |
| I am an agent INSTALLING memesh for a user | [llms-install.md](llms-install.md) |
| I am an agent USING memesh (the loop, the 9 tools, hygiene) | [AGENTS.md](AGENTS.md) |
| What changed, and what is merged but unreleased | [CHANGELOG.md](CHANGELOG.md) (`[Unreleased]`) |

---

## The few things that live only here

Everything below is either non-obvious from the code or specific to working
with an assistant. If anything here starts duplicating a document above, delete
it here and link instead.

### Running the tests

```bash
node scripts/run-tests-isolated.mjs        # whole suite, against a throwaway HOME
npm test -- --run                          # vitest directly — uses YOUR ~/.memesh
```

Prefer the first. The suite writes to `~/.memesh`, so running vitest directly
mutates your real knowledge graph.

**Do not set `MEMESH_DB_PATH` when running the suite.** Several hook tests
exercise the "no database yet" branches, and pointing the env var at an
existing file makes those branches unreachable. An isolated `HOME` is the
right isolation; a fixed DB path is not.

Pool mode is `forks`, one worker, no file parallelism. That is not a
preference — several test files share one HOME and therefore one SQLite
database, and running them concurrently deadlocks on the write lock. It is
expressed as `maxWorkers: 1` + `fileParallelism: false`; the older
`singleFork`/`maxForks`/`minForks` keys do not exist in Vitest 4 and were being
silently ignored.

`npm run typecheck` uses `tsconfig.check.json`, which covers `src/`, `tests/`
and the root config files. `tsconfig.json` is narrower on purpose — it is the
config that emits `dist/`.

### Coverage, and what a 0% file means

```bash
npm run test:coverage        # whole suite + v8 coverage, throwaway HOME
```

Read the report with one caveat, or it will mislead you. Coverage is measured
**in-process**, and this project spawns a lot of what it tests: the CLI, the
hooks, the MCP server and the packaged binaries are exercised through
`spawnSync`, so they report **0% while being well tested**.
`src/transports/cli/cli.ts` is the clearest case — a whole directory of tests
against it, 0% in the report.

What the number is good for is the opposite direction: a file at 0% that is
*not* spawned anywhere is genuinely unexercised. That is where most of the
dashboard sits. Do not write the count down here — this file has already been
wrong about it once, and `tests/dashboard/component-contracts.test.tsx` derives
the real list from the directory and fails when a component belongs to neither
side of it.

### Verifying a change before claiming it works

Do not report a test result, a CI status or a benchmark number you did not
produce in this session. Paste the runner's actual output. `npm run verify:release` is the same gate the publish path runs, and
`scripts/check-doc-claims.mjs` — which it calls — checks every claim the public
documents make about the code.

**Read the exit code, not a grep of the output.** `cmd 2>&1 | grep …` returns
*grep's* status and hides every line the pattern misses. Vitest prints
`Errors  N errors` for unhandled rejections *while reporting every test as
passed*, and exits 1 — a branch was pushed as green that way, and CI went
eight-red on it. Capture the verdict first, then look at detail:

```bash
node scripts/run-tests-isolated.mjs > /tmp/t.log 2>&1; echo "exit=$?"
grep -E 'Test Files|Tests |Errors ' /tmp/t.log
```

When you fix a bug, **revert the fix and confirm the test goes red.** A green
suite is not evidence that a fix is protected: three tests in this repository
have passed while the thing they guarded was removed.

### Working policy

How much process a change deserves is decided by its blast radius, not by
habit. Two modes:

- **Lightweight** — the change is confined to one module or one clear path,
  needs no multi-surface verification, and touches nothing security-sensitive
  or destructive. Do it directly: implement, run the affected tests plus
  `npm run typecheck`, read your own diff, done. Most fixes are this.
- **Full** — anything that changes behaviour across surfaces (hook + MCP +
  CLI + docs move together here), touches persistence, security boundaries,
  or user-facing contracts. Then: understand → plan → implement with tests →
  the full gate (`verify:release`) → break-test the guards you added
  (revert the fix, watch the test go red) → docs in the same PR.

Rules that hold in both modes:

- **Findings first, evidence over warnings.** A review or QA report leads
  with what is wrong and proves it (file:line, actual output), not with
  broad concerns. Gate verdicts use the same vocabulary `memesh doctor`
  uses: `PASS`, `PASS_WITH_CONCERNS`, `FAIL`.
- **No runtime claim without runtime evidence.** "It works" requires having
  run it — the verification section above is the how.
- **Delegating to subagents**: split ownership into disjoint file scopes so
  two writers never touch one file; isolate file-editing agents in
  worktrees; the orchestrator reads every diff before it lands. Do not
  delegate the critical path reflexively — coordination has a cost.
- **Internal working notes stay local.** Plans, scratch analyses, agent
  transcripts, private TODOs — never committed, never in commit messages or
  release notes. The repository carries only what reproduces shipped
  behaviour: source, tests, schemas, configuration, and the public docs
  above. (This is also why this file is a pointer.)
- **Docs move with the change** — a capability the docs do not describe, or
  describe wrongly, fails `check-doc-claims` and is not done.

### Git

- **Short-lived branch → PR → `main`. Never push directly to `main`.** That is
  the whole flow, and `main` is the only long-lived branch. This used to read
  "`main` ← `develop`", which described git-flow: a model for software with
  several release lines under support at once, and one whose own author now
  warns against using it for continuously delivered projects. Nothing here has
  release lines, and the branch proved it — `develop` sat 58 commits behind
  `main` and 0 ahead, through four releases, while every PR went straight to
  `main`. It was kept briefly as a passive mirror and then deleted: a branch
  that only ever receives a copy of `main` answers no question that a tag or
  `CHANGELOG.md` does not already answer, and it cost a full matrix re-run on
  every sync.
- Releases are tags on `main`. "Merged but not yet published" is answered by
  `CHANGELOG.md`'s `[Unreleased]` section, which is why a branch does not need
  to answer it.
- Commit format: `<type>(<scope>): <subject>`
- **No AI attribution.** Commit messages and PR descriptions must not contain
  `Co-Authored-By: Claude`, `🤖 Generated with [Claude Code]`, or any text
  crediting an AI as author or generator. Strip it from any default template.
- Never `git add -A` or `git add .` — stage the files you meant to change.

### Two storage facts worth knowing before you touch persistence

- `entities_fts` is a **contentless** FTS5 table. A delete must be issued with
  the exact text that was indexed, or the index silently keeps the old tokens
  and search answers for content that is gone.
- `entities_vec` is one sqlite-vec table for the **whole database**, not one per
  namespace. Dropping it drops every namespace's embeddings, and only a full
  re-embed brings them back — on a paid provider, at cost.