11# Repository Guidelines
22
3- ## Authority
3+ ## Authority and scope
4+
5+ ` omp-codebase-memory ` distributes ` codebase-memory-mcp ` (CBM) as an installable
6+ OMP extension: it owns the executable's lifecycle, wires one MCP server entry,
7+ and ships context artifacts harvested from that executable. It is TypeScript on
8+ Bun, has no npm runtime dependencies, and commits its bundled entry points at
9+ ` dist/index.js ` and ` dist/augment.js ` . ` README.md ` states what the package does
10+ for an operator; this file does not restate it.
411
512` rasen/specs/ ` contains the accepted capability specifications and outranks
613change proposals. ` rasen/changes/ ` records design decisions and their rationale;
7- do not duplicate that rationale here.
8-
9- When implementation changes a normative decision, update the affected
10- specification in the same change.
14+ do not duplicate that rationale here. When implementation changes a normative
15+ decision, update the affected specification in the same change.
1116
12- ## Project boundaries
13-
14- ` omp-codebase-memory ` distributes ` codebase-memory-mcp ` (CBM) as an installable
15- OMP extension. It is TypeScript on Bun, has no npm runtime dependencies, and
16- commits its bundled entry points at ` dist/index.js ` and ` dist/augment.js ` .
17-
18- The following boundaries are fixed:
19-
20- - Consume CBM release artifacts; contributing changes to
21- ` DeusData/codebase-memory-mcp ` is out of scope.
22- - Adopt an existing ` codebase-memory-mcp ` on ` PATH ` and never replace it. Put a
23- package-managed copy under a package-owned root outside the plugin tree. Never
24- write ` ~/.local/bin ` or modify an executable this package did not install.
25- - Own exactly the ` codebase-memory-mcp ` key under ` mcpServers ` in the active OMP
26- agent directory's ` mcp.json ` . Upsert it idempotently, fail closed on an
27- unparseable file or foreign ` command ` , and remove it only while it still
28- matches the package-owned entry.
29- - Never create or modify an operator's OMP agent-directory ` AGENTS.md ` or
30- ` RULES.md ` .
31- - Never register a ` tool_call ` handler. Augment successful output through
32- ` tool_result ` , append rather than replace prior content, and fail open.
33- - Never use platform timer globals. Use the handler context's managed timers
34- through ` src/scheduler.ts ` .
35- - Never set an account-wide CBM configuration key for the operator.
36- - Never duplicate an action already exposed through CBM's MCP tools, including
37- indexing.
38- - Never hand-edit generated context artifacts; regenerate them from the CBM
39- executable.
40- - Never place verification scratch inside a directory the operator owns, and
41- never delete a directory this package or its verification did not create. A
42- project-local plugin root belongs in a temporary directory, not under the
43- repository's ` .omp/ ` , which holds the operator's own project-local skills and
44- configuration.
45-
46- This package owns only the executable it downloaded and its MCP entry. CBM owns
47- the graph, indexing, watcher, cache root, and updates to a system installation.
48- Windows and changes to any other operator file are out of scope.
17+ This file is the authority for repository guidance. ` AGENTS.md ` is a tracked
18+ symbolic link to it, so both agent tool families read one text; do not create a
19+ second copy of these rules anywhere.
4920
5021## Repository layout
5122
@@ -55,70 +26,228 @@ The working tree contains two independent Git repositories:
5526 ` test/ ` , ` dist/ ` , ` package.json ` , and ` .omp-plugin/ ` .
5627- ` rasen/ ` tracks planning artifacts in its own repository and remote.
5728
58- ` rasen/ ` is ignored by the outer repository. Commit its work with
29+ ` rasen/ ` is ignored by the outer repository, so ` git status ` at the root never
30+ reports its state and it has to be inspected on its own. Commit its work with
5931` git -C rasen ` ; never stage planning and implementation in the same repository.
6032
6133## Git workflow
6234
63- ` main ` and release tags are protected by the committed rulesets under
64- ` .github/rulesets/ ` . Before implementation, create a short-lived topic branch
65- named ` <type>/<short-slug> ` .
66-
67- Use Conventional Commits and land changes through pull requests. The ruleset
68- permits merge commits only; do not squash. Unresolved review threads block the
69- merge.
70-
71- Treat the committed rulesets as authoritative. Change and reimport those files
72- rather than editing protection through the web interface.
35+ Never commit implementation to the default branch. Cut a short-lived topic
36+ branch from it before implementation begins, and name it ` <type>/<short-slug> `
37+ using the Conventional Commits type that dominates the change. Branches already
38+ landed this way: ` feat/graph-context-and-agents ` ,
39+ ` docs/repository-guidelines ` , ` chore/commit-ruleset-payloads ` .
40+
41+ The default branch and release tags are protected by the rulesets committed
42+ under ` .github/rulesets/ ` . This paragraph and the list below it state two things
43+ and no more. First, the shape of that protection: the facts whose change would
44+ change the sequence of steps by which work lands here — what a contributor must
45+ do, which button merges, whether a branch must be current first. Second, the two
46+ literals a ruleset shares with a workflow: the required check name ` ci ` , which
47+ ` main.json ` requires and which is the gate job's own name in ` ci.yml ` , and the
48+ tag pattern ` v* ` , which ` tags.json ` protects and which ` release.yml ` triggers
49+ on. Nothing reads either ruleset file, so neither agreement is checked anywhere;
50+ that is why those two are stated here at all. (The third pairing, between a
51+ pushed tag and ` .omp-plugin/marketplace.json ` 's ` source.ref ` , is checked — the
52+ release version gate fails on a mismatch — so it lives in ` CONTRIBUTING.md `
53+ with the procedure, not here.)
54+
55+ A value that only tunes a threshold inside a step that stays the same is a
56+ parameter and stays in the rulesets: the actor and bypass lists, the
57+ review-dismissal and code-owner switches, the exemptions. Read those there
58+ rather than trusting a value repeated in prose. The approval count is zero
59+ today, so there is no approval step to describe; were it to change, the list
60+ below would gain one. The shape:
61+
62+ - The default branch rejects deletion and non-fast-forward pushes.
63+ - Landing a change requires a pull request whose review threads are resolved.
64+ - The merge commit is the only permitted merge method.
65+ - Exactly one status check, named ` ci ` , is required, under a strict policy — so
66+ a branch must be current with the default branch before it can merge.
67+ - Release tags matching ` v* ` reject deletion and non-fast-forward pushes.
68+
69+ Change protection by editing those files and reimporting them, never through the
70+ web interface. A rule changed in the browser is invisible to review and is
71+ overwritten by the next import.
72+
73+ The merge-only restriction is pinned rather than preferred. A squash rewrites
74+ the commits a dependent pull request still carries, so every downstream diff
75+ re-inflates with changes that already landed and each dependent branch needs a
76+ rebase per merge. Preserving the commits is what makes a dependent pull-request
77+ chain cheap here: a branch stacked on another stays mergeable while its parent
78+ lands.
79+
80+ Commit messages follow Conventional Commits. Choose commit boundaries for
81+ coherence — one reviewable decision per commit — rather than by file count or
82+ by when the work happened.
7383
7484## CI and release
7585
7686Branch protection requires one status check named ` ci ` . It is the gate job in
77- ` .github/workflows/ci.yml ` , runs under ` if: always() ` , fails when it aggregates
78- no jobs, and accepts only successful dependencies.
79-
80- - Update the gate's ` needs ` whenever a required runtime job changes. Do not add
81- runtime job names to the ruleset.
82- - Keep ` install-check ` outside the gate because it cannot install a pull
83- request's merge ref.
84- - Pin every external ` uses: ` to a full 40-character commit SHA followed by a
85- version comment.
86- - Default to ` permissions: contents: read ` , use
87- ` persist-credentials: false ` , and grant ` contents: write ` only to the release
88- publish job.
89- - Do not apply ` paths ` filters to jobs required by the gate.
87+ ` .github/workflows/ci.yml ` : it runs under ` if: always() ` , fails when it
88+ aggregates no jobs, and accepts only successful dependencies. ` always() ` is
89+ load-bearing, because a skipped required check blocks a pull request instead of
90+ failing it.
91+
92+ - Runtime job names never appear in a ruleset. Adding, removing, or
93+ restructuring a runtime job means editing the gate's ` needs ` and nothing else.
94+ Renaming the gate job silently blocks every merge, with no failing job to
95+ point at.
96+ - Keep ` install-check ` outside the gate because it installs by ref and a pull
97+ request's merge ref does not exist on the remote as an installable ref.
98+ - Every ` uses: ` reference is pinned to a full 40-hex commit SHA followed by a
99+ trailing version comment. The ` hygiene ` job enforces both halves and fails
100+ when it finds no references to check.
101+ - Default to ` permissions: contents: read ` , use ` persist-credentials: false ` ,
102+ and grant ` contents: write ` only to the release publish job.
103+ - Do not apply ` paths ` filters to jobs the gate requires.
90104- Pin Bun and its matching ` @types/bun ` version exactly. Install with
91105 ` --frozen-lockfile ` .
92106- Run checks through package scripts and print toolchain versions with results.
93107- Do not add a Node job; Node is not a supported runtime.
94108
95- ` dist/index.js ` and the feature entry ` dist/augment.js ` are committed. CI must
96- read the bundle list from ` package.json ` 's extension entries, build from
97- source, and compare each result byte-for-byte with its tracked bundle.
109+ ` dist/index.js ` and the feature entry ` dist/augment.js ` are committed. CI reads
110+ the bundle list from ` package.json ` 's extension entries, builds from source, and
111+ compares each result byte-for-byte with its tracked bundle.
98112
99- A release tag must match ` package.json ` 's version and both the version and source
100- ref in ` .omp-plugin/marketplace.json ` . Create releases only from verified tags.
113+ A release tag must match ` package.json ` 's version and both the version and
114+ source ref in ` .omp-plugin/marketplace.json ` . Create releases only from verified
115+ tags. ` CONTRIBUTING.md ` holds the procedure.
101116
102117## Testing and verification
103118
104- Add deterministic tests for changed behavior. Cover the package boundaries
105- affected by the change: release selection and checksums, archive validation,
106- executable resolution, MCP-entry ownership, transport security, scheduler
107- behavior, and handler fail-open paths.
119+ Add deterministic tests for changed behavior. Cover the package boundaries the
120+ change touches: release selection and checksums, archive validation, executable
121+ resolution, MCP-entry ownership, transport security, scheduler behavior, and
122+ handler fail-open paths.
123+
124+ Where a new test belongs:
108125
109- ` test/unit ` must not require a CBM executable or network access. Use recorded
110- fixtures under ` test/fixtures ` and helpers in ` test/support ` . ` test/packaging `
111- may build and load the bundle and touch the filesystem. Checks that require a
112- real executable or network access belong in a separate job.
126+ - ` test/unit ` by default. It must not require a CBM executable or network
127+ access. Use recorded fixtures under ` test/fixtures ` and helpers in
128+ ` test/support ` .
129+ - ` test/packaging ` when the test needs a build or a real load of the bundle. It
130+ may touch the filesystem.
131+ - A CI job of its own when the check needs a real executable or the network, as
132+ ` harvest ` and ` install-check ` do. Neither suite may acquire either.
113133
114134Report the commands and revision used for verification. State which relevant
115135checks were not run and why; never claim an unexecuted check passed.
116136
137+ ## Prohibitions
138+
139+ Each entry carries the mechanism that makes it a rule. A prohibition whose
140+ reason reduces to "it breaks things" is removed by the next contributor who
141+ finds it inconvenient.
142+
143+ ** Never replace an executable this package did not install.** Adopt an existing
144+ ` codebase-memory-mcp ` from ` PATH ` or ` ~/.local/bin ` as it is. Place a managed
145+ copy only under this package's own root, ` ~/.omp/codebase-memory/bin/<version>/ `
146+ (` src/paths.ts ` ), which is outside both the plugin tree and the agent directory
147+ — OMP replaces version-qualified plugin directories on reinstall, so an
148+ executable stored inside one is discarded and re-downloaded by a routine plugin
149+ upgrade. System installations win because CBM resolves one canonical per-account
150+ cache root and refuses to run when a process is configured against a different
151+ root while any CBM session is active: two executables of different versions
152+ sharing that root produce mismatched index generations. ` ~/.local/bin ` is CBM's
153+ own installer's directory and CBM's ` update ` owns the file there.
154+
155+ ** Own exactly the ` codebase-memory-mcp ` key under ` mcpServers ` in the active
156+ agent directory's ` mcp.json ` , and nothing else in that file.** Upsert it
157+ idempotently, and remove it only while it still matches the package-owned entry.
158+ OMP's own ` /mcp add ` writes the same file with no lock shared with this package,
159+ so the write is a read-modify-write against observed content that fails closed on
160+ a shape it does not recognise: a lost update degrades to a missing entry the next
161+ session start rewrites, rather than a corrupted file. A ` command ` under that key
162+ that this package did not write means another installer owns the name, so the
163+ file is left untouched and both paths are reported.
164+
165+ ** Never create or modify an operator's OMP agent-directory ` AGENTS.md ` or
166+ ` RULES.md ` .** Both are single-slot: the reader takes one file per slot, so
167+ writing either does not add to the operator's instructions, it silently
168+ suppresses them. In the agent directory ` AGENTS.md ` is the one surviving
169+ user-level context file, and ` RULES.md ` occupies the single slot reserved for the
170+ operator's sticky rules. This scopes to the operator's runtime directory. It is
171+ explicitly not about this repository's own root ` CLAUDE.md ` and ` AGENTS.md ` ,
172+ which are project context discovered from a checkout and claim neither slot — a
173+ reader who conflates the two will read the rule as contradicting the file it is
174+ written in.
175+
176+ ** Never delete ` .omp/ ` , at either location.** ` .omp/ ` is an OMP directory that
177+ belongs to the operator: ` ~/.omp/ ` holds their account configuration, and this
178+ repository's own ` <project>/.omp/ ` holds their project-local ` config.yml ` and
179+ skills root. Neither may be deleted, and neither becomes deletable because a
180+ verification step is what materialized it. Cleaning up verification scratch
181+ removes exactly the paths that step created, named one by one — never a
182+ containing directory. The loss is silent by mechanism: a global ignore excludes
183+ ` /.omp/ ` , so the directory is untracked, ` git status ` never reports it missing,
184+ and nothing fails until a later session reads what is no longer there.
185+
186+ The incident behind that entry: verifying an install with ` enabledFeatures: [] `
187+ needs a plugin root carrying that selection, and one was created under
188+ ` <project>/.omp/plugins/ ` . Cleaning it up took the operator's ` config.yml ` and
189+ project-local skills root with it, and the loss surfaced a session later as a
190+ skill registry advertising roughly sixty skills while resolving five. The
191+ placement that avoids it is a temporary directory — ` mktemp -d ` , which is what
192+ CI already uses for a scratch ` HOME ` — never a path under ` <project>/.omp/ ` or
193+ ` ~/.omp/ ` .
194+
195+ ** Never register a ` tool_call ` handler.** OMP treats a handler that throws or
196+ blocks there as a refusal of the tool call, so one slow graph query would deny
197+ the operator's ` grep ` . Augment successful output through ` tool_result ` , where a
198+ failure is caught and the run continues; append rather than replace prior
199+ content, and fail open.
200+
201+ ** Never call the platform timer globals.** Use the handler context's managed
202+ timers through ` src/scheduler.ts ` . A raw ` setTimeout ` callback that throws
203+ escapes handler dispatch entirely and surfaces as a process-level
204+ ` uncaughtException ` , which OMP's postmortem handler treats as fatal and tears
205+ down the whole session. The context's timers run the callback with handler
206+ isolation, are ` unref ` 'd, and are cleared on ` session_shutdown ` .
207+
208+ ** Never set an account-wide CBM configuration key for the operator.** CBM 0.10.8
209+ exposes six keys through ` codebase-memory-mcp config set ` — ` auto_index ` ,
210+ ` auto_index_limit ` , ` auto_watch ` , ` ui-lang ` , ` ui_enabled ` , ` ui_port ` — and every
211+ one is account-wide; none is scoped to a project or to a client. That store is
212+ shared with every CBM client on the machine, so a key written here silently
213+ changes what another editor's CBM session does. A read-before-write does not
214+ make it reversible: nothing records that this package wrote the key, and another
215+ client may set the same key meanwhile, so the value to put back is not knowable.
216+
217+ ** Never duplicate an action CBM's MCP tools already expose, indexing included.**
218+ The model already holds ` index_repository ` , and a second path through this
219+ package would take its own arguments and defaults — the two diverge, and the
220+ operator cannot tell which one ran.
221+
222+ ** Never hand-edit a generated context artifact.** Regenerate with
223+ ` bun run harvest ` . The source of these artifacts is embedded in the CBM
224+ executable and changes with it, so a hand edit is a second, diverging statement
225+ of the same contract until the next regeneration overwrites it. A unit test
226+ re-runs every build guard against the committed files, and the ` harvest ` CI job
227+ regenerates and diffs them.
228+
229+ Contributing changes to ` DeusData/codebase-memory-mcp ` is out of scope; this
230+ package consumes CBM release artifacts. This package owns only the executable it
231+ downloaded and its MCP entry. CBM owns the graph, indexing, the watcher, the
232+ cache root, and updates to a system installation. Windows and changes to any
233+ other operator file are out of scope.
234+
117235## Documentation
118236
119237` README.md ` is operator-facing and must be created or rewritten through the
120- ` readme-creator ` skill and its quality checklist. If that skill is unavailable,
121- stop rather than editing the README by hand.
122-
123- This file is the authority for repository guidance. ` AGENTS.md ` must remain a
124- tracked symbolic link to ` CLAUDE.md ` ; do not create another copy of these rules.
238+ ` readme-creator ` skill, following its phases and scored against its quality
239+ checklist. If that skill cannot be resolved by name, stop; do not edit the README
240+ by hand. ` CONTRIBUTING.md ` holds only procedures that need maintainer
241+ credentials and links here for every rule.
242+
243+ ` AGENTS.md ` must remain a tracked symbolic link to ` CLAUDE.md ` , with ` CLAUDE.md `
244+ as the regular file. Both names are needed and neither reader finds the other's
245+ file: Claude Code reads the root ` CLAUDE.md ` , while OMP's ` claude ` provider
246+ reads ` .claude/CLAUDE.md ` and discovers a root ` AGENTS.md ` through its
247+ standalone provider. Two regular files would satisfy both readers and diverge on
248+ the first one-sided edit.
249+
250+ Do not add ` .omp/AGENTS.md ` . This repository's ` .omp/ ` is non-empty, so a native
251+ project context file there would win the depth-0 scope and shadow the root file
252+ that both readers already find — replacing one text with a second that only OMP
253+ sees, for no gain.
0 commit comments