# GENERAL RULE
Don't say "You're absolutely right" each time I correct you. Mix it up, that's so boring!

# MemberJunction Development Guide

<!-- This file is deliberately small. Anthropic's guidance is ~200 lines per CLAUDE.md: longer
     files consume context AND reduce adherence. Detailed guidance lives in path-scoped rules,
     nested CLAUDE.md files, and skills — all of which load on demand. See the routing table
     below. `npm run check:claude-md` enforces the budget and proves nothing was lost.
     Before adding anything here, read "Where new guidance goes" at the bottom. -->

Everything below is either **unrecoverable if violated** (so it must survive `/compact`) or **true
on every task regardless of what you're touching**. Everything else loads on demand — see
[Where the rest of the guidance lives](#where-the-rest-of-the-guidance-lives).

---

## 🚨 CRITICAL RULES — VIOLATIONS ARE UNACCEPTABLE 🚨

### 1. NO COMMITS WITHOUT EXPLICIT APPROVAL
- **NEVER run `git commit` without the user explicitly asking you to**
- **Each commit requires ONE-TIME explicit approval** — don't assume ongoing permission
- **NEVER ask to commit** — wait for the user to request it
- **ONLY commit what is staged** — never modify or add to staged changes
- **NEVER commit work-in-progress** that isn't staged by the user

### 2. NO DESTRUCTIVE GIT OPERATIONS WITHOUT EXPLICIT APPROVAL
- **NEVER run `git checkout -- <file>` or `git restore <file>`** to discard changes without the user explicitly approving — even in bypass/auto-approve permission mode
- **NEVER run `git reset --hard`** without explicit approval
- These commands destroy uncommitted work (staged and unstaged) and cannot be undone
- If you need to undo YOUR changes to a file, use `git diff` to identify only your changes and reverse them with targeted `Edit` calls — this preserves the user's other in-progress work
- **NEVER update title/description of merged PRs** without explicit approval each time
- Always ask before modifying any historical git data

### 3. FEATURE BRANCHES MUST TRACK SAME-NAMED REMOTE BRANCHES

**Why this matters**: if a feature branch tracks `origin/next` instead of `origin/<its-own-name>`, `git push` sends commits **directly to `next`**, bypassing PR review and potentially breaking the main branch for everyone.

```bash
# ✅ CORRECT — create branch and push with upstream tracking to the same-named remote
git checkout -b my-feature-branch
git push -u origin my-feature-branch

# ❌ WRONG — a branch created from next tracks origin/next by default. DANGEROUS.
git checkout next
git checkout -b my-feature-branch
```

**Before every push**, verify tracking:
```bash
git branch -vv
# * my-feature [origin/my-feature]  ✅ tracks same name
# * my-feature [origin/next]        ❌ tracks next — fix before pushing
```

Fix incorrect tracking with:
```bash
git branch --set-upstream-to=origin/my-feature-branch my-feature-branch
```

**This is a non-negotiable safety requirement.**

### 4. START WORK ON A FEATURE BRANCH
Before starting a new line of work, check the current branch. It must be (a) separate from the remote default branch and (b) named for the work being requested. If not, **ask first**, then cut and switch to one.

---

## ✅ Definition of Done

A change is not done until **both** test tiers pass. Report results — pass/fail/skip counts — to the user.

**Unit tests** — when you modify ANY package's source, run that package's tests:
```bash
cd packages/PackageName && npm run test
```
If tests fail because of your change, **update them**. If they fail for other reasons, **fix them**. Never leave broken tests behind. Never assume tests still pass after changing signatures, renaming methods, changing return values, or altering behavior.

**Integration tests** — the deterministic tier must be run headless and pass:
```bash
npm run test:integration     # = MJ_INTEGRATION_TEST=1 mj test suite "Integration Tests — Deterministic"
npx mj test run "IT30 - Conversation Compaction (assembly layer)"   # single bundle while iterating
```
Unit tests passing is necessary but **not sufficient** — the integration tier catches the seams between packages that unit tests mock away. Run it after migrations + CodeGen have been applied. Full details, authoring rules, and the client-first transport doctrine: [`guides/INTEGRATION_TESTING_QUICKSTART.md`](guides/INTEGRATION_TESTING_QUICKSTART.md).

---

## Always-true facts

**Strong typing, always.** Never use `any`, `as any`, or `unknown`-as-a-shortcut — MJ has a proper type for everything; ask if you think you need one. Never use `.Get()`/`.Set()` as a substitute for generated entity properties. Details: [`.claude/rules/typescript-style.md`](.claude/rules/typescript-style.md).

**Entity names carry the `MJ: ` prefix.** As of v5.0, all core entities are `MJ: AI Agents`, `MJ: AI Prompt Runs`, etc. An unprefixed name throws `Entity ... not found in metadata`. Verify names in `packages/MJCoreEntities/src/generated/entity_subclasses.ts`.

**The generated ORM is the schema's source of truth** — not migration SQL. Migrations are append-only history; the entity classes reflect the net result.

**CodeGen reads JSONType definitions from the database, not from `metadata/`.** Run `mj sync push` **before** `mj codegen`, or CodeGen regenerates from stale definitions and *silently deletes* properties from the generated types. Full ordering + why it's silent: [`migrations/CLAUDE.md`](migrations/CLAUDE.md).

**Build commands:**
```bash
npm run build            # all packages, from repo root
npm run watch            # watch mode
npm run start:api        # MJAPI (port 4001)
npm run start:explorer   # MJExplorer (port 4201)
cd packages/PackageName && npm run build   # single package — use this, NOT turbo from root
```
After making code changes, **always compile the affected package** and fix all TypeScript errors before proceeding.

**NPM workspace**: add dependencies to the individual package's `package.json`, then run `npm install` **at the repository root**. Never run `npm install` inside a package directory.

**Migration folder**: always use the highest-numbered `migrations/v*/` folder (currently `migrations/v5/`).

**Record Changes**: MJ has built-in version control for all entities. Don't implement custom versioning.

---

## Where the rest of the guidance lives

Guidance is **loaded on demand**, so it costs nothing until it's relevant. This table is the router — if you need a rule you don't currently have, it's here.

### Path-scoped rules (`.claude/rules/`) — load when you open a matching file

| Rule | Loads for | Covers |
|---|---|---|
| [`data-access.md`](.claude/rules/data-access.md) | `**/*.ts` | `RunView`/`RunViews`, `ResultType`/`Fields`, `BaseEntity` Save/Delete error handling, entity metadata lookup (`EntityByName`), per-provider `Metadata` scoping, caching + `BaseEngine`/`BaseEngineRegistry`, keyset pagination, `UserInfoEngine` preferences |
| [`typescript-style.md`](.claude/rules/typescript-style.md) | `**/*.ts` | No `any`, no `.Get()`/`.Set()`, derive field types from the entity, no cross-package re-exports, `BaseSingleton`, no dynamic `import()`, naming conventions, functional decomposition, OOD |
| [`design-tokens.md`](.claude/rules/design-tokens.md) | `**/*.scss`, `**/*.css` | No hardcoded colors, the semantic token catalog, hex→token mappings, `color-mix()`, the CI gates |
| [`testing.md`](.claude/rules/testing.md) | `**/*.test.ts`, `**/__tests__/**` | Vitest conventions, test structure, the scaffold script, fixing test drift, CI integration |

### Nested `CLAUDE.md` — load when you read a file in that tree

| Path | Scope |
|---|---|
| [`migrations/CLAUDE.md`](migrations/CLAUDE.md) | Migration authoring — naming, hardcoded UUIDs, system columns CodeGen owns, CHECK constraints, CodeGen handoff |
| [`metadata/CLAUDE.md`](metadata/CLAUDE.md) | Metadata authoring — `@lookup`/`@file`/`@parent` refs, `uuidgen` primary keys, no per-PR sync migrations, seeding lookup tables, application metadata |
| [`metadata/components/CLAUDE.md`](metadata/components/CLAUDE.md) | Interactive component authoring — component architecture rules, the `ProductRevenueMatrix` reference implementation |
| [`docker/CLAUDE.md`](docker/CLAUDE.md) | Docker workbench + MJAPI container configurations |
| [`stats/CLAUDE.md`](stats/CLAUDE.md) | Repo LOC stats — generated files, do not hand-edit |
| [`packages/Actions/CLAUDE.md`](packages/Actions/CLAUDE.md) | Actions are boundaries, not internal APIs — when to create one, parameter validation, error handling |
| [`packages/Angular/CLAUDE.md`](packages/Angular/CLAUDE.md) | Angular conventions — multi-provider `@Input() Provider`, routing, query-param round-trip, component/module strategy, template syntax, UI components, forms, `NotifyLoadComplete` |
| [`packages/Angular/Generic/CLAUDE.md`](packages/Angular/Generic/CLAUDE.md) | Generic-Angular — no Router imports, reusability constraints |
| [`packages/Angular/Explorer/CLAUDE.md`](packages/Angular/Explorer/CLAUDE.md) | Explorer patterns — `NavigationService`, `BaseResourceComponent`, deep links |
| [`packages/Angular/Explorer/dashboards/CLAUDE.md`](packages/Angular/Explorer/dashboards/CLAUDE.md) | Dashboard page chrome, `NotifyLoadComplete`, agent context wiring |
| [`packages/Angular/Bootstrap/CLAUDE.md`](packages/Angular/Bootstrap/CLAUDE.md) | 🚨 Browser manifest — **no server-only dependencies** |
| [`packages/Angular/BootstrapLite/CLAUDE.md`](packages/Angular/BootstrapLite/CLAUDE.md) | 🚨 Lite browser manifest — **no server-only dependencies** |
| [`packages/MJCoreEntities/CLAUDE.md`](packages/MJCoreEntities/CLAUDE.md) | 🚨 Generated ORM — never hand-edit; the schema's source of truth |
| [`packages/CodeGenLib/CLAUDE.md`](packages/CodeGenLib/CLAUDE.md) | CodeGen — what it generates, when it runs, DB connection + env vars, class-registration manifests |
| [`packages/MJAPI/CLAUDE.md`](packages/MJAPI/CLAUDE.md) | Server runtime — connection pooling, startup mode, public URL, platform switching |
| [`packages/DBAutoDoc/CLAUDE.md`](packages/DBAutoDoc/CLAUDE.md) | DB auto-doc package conventions |

### Guides — the complete index is [`guides/README.md`](guides/README.md)

40 cross-cutting "read this before you build that" guides, categorized. **Consult the index before starting work in an unfamiliar area** — these capture patterns already litigated. Frequently needed: [Caching & Pub/Sub](guides/CACHING_AND_PUBSUB_GUIDE.md), [UUID Comparison](guides/UUID_COMPARISON_GUIDE.md), [Unified Permissions](guides/UNIFIED_PERMISSIONS_GUIDE.md), [Search Overview](guides/SEARCH_OVERVIEW_GUIDE.md), [Dashboard Best Practices](guides/DASHBOARD_BEST_PRACTICES.md), [Forms Architecture](guides/FORMS_ARCHITECTURE_GUIDE.md), [Transport Layer](guides/TRANSPORT_LAYER_ARCHITECTURE_GUIDE.md), [Remote Operations](guides/REMOTE_OPERATIONS_GUIDE.md).

### Skills — load only when invoked

`debug-build-failures` (package won't build / Turbo / circular deps), `playwright-cli` (browser automation + the MJ dev-server loop), `scaffold-mj-dashboard` (new Explorer dashboard).

### Project-wide standards

- **[Publish-Then-No-Breaking-Changes Policy](packages/OpenApp/PUBLISH_NO_BREAK_POLICY.md)** — within a published OpenApp major version, only additive schema changes are allowed. Consult before authoring any migration that modifies an existing schema. (Adopted 2026-04-29, prospective from each app's next published version.)
- **[Full Autonomy Development](claude-full-auto.md)** — for sandboxed/air-gapped machines only. **Not for regular development machines.**

### Local CI mirrors

```bash
npm run check:ui          # design-token + button gates on changed CSS/SCSS (mirrors the PR gate)
npm run check:esm         # native-ESM import guard for "type": "module" packages
npm run check:claude-md   # instruction-file budget, link validity, and routing-table coverage
```

---

## Working style

**Parallelize aggressively.** Whenever you need to spin up tasks that don't require user interaction and aren't interdependent, run them **in parallel**. Never process independent tasks sequentially.

<!-- Function decomposition rules deliberately live ONLY in .claude/rules/typescript-style.md.
     They were summarized here at one point, and the paraphrase drifted from the rule it was
     summarizing — a restated rule is a second source of truth that silently diverges. If a rule
     has a home, link to it; don't re-say it. -->

---

## Where new guidance goes

Before adding anything to this file, route it:

1. **Is it unrecoverable if violated?** (destroys work, bypasses review, ships a breaking change) → here, so it survives `/compact`.
2. **Is it true on literally every task?** → here.
3. **Does it apply to a file type?** (`.ts`, `.scss`) → a path-scoped rule in `.claude/rules/`.
4. **Does it apply to a directory/subsystem?** → that subtree's `CLAUDE.md`.
5. **Is it a multi-step procedure?** → a skill in `.claude/skills/`.
6. **Does an enforcement script already exist?** → a hook, not prose.
7. **Is it "read this before building X"?** → a guide, indexed in [`guides/README.md`](guides/README.md).

Anything reaching step 3 or beyond **does not belong in this file**. The 30-second test from
Anthropic's own guidance: *"Would removing this line cause Claude to make mistakes?"* — and then:
*"on every task, or only when touching a particular kind of file?"* The second answer routes it out.
