Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
File renamed without changes.
7 changes: 7 additions & 0 deletions .cursor/hooks/CURSOR-HOOKS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `.cursor/hooks/`

Optional Cursor project hook scripts. Add `.cursor/hooks.json` at the repo root when you enable hooks.

- Scripts in this directory are referenced from `hooks.json` (not committed until you need hooks).
- Prefer skills and rules for static guidance; hooks automate events (shell gates, session setup, and so on).
- See Cursor hooks documentation and **abcmemory** skill.
File renamed without changes.
8 changes: 8 additions & 0 deletions .cursor/prompts/CURSOR-PROMPTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `.cursor/prompts/`

Reusable agent-facing prompt snippets (abcmemory). One topic per kebab-case `.md` file.

- Use for copy-paste or @-reference blocks that are not full skills or rules.
- Do not put human plan/history templates here — use `.llm/templates/` instead.
- When the user says **abcremember** with prompt-shaped guidance, prefer this directory.
- See **abcmemory** skill and [LLM-TEMPLATES.md](/.llm/templates/LLM-TEMPLATES.md).
7 changes: 7 additions & 0 deletions .cursor/rules/CURSOR-RULES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `.cursor/rules/`

Cursor rules (`.mdc` files): always-applied or glob-scoped agent constraints.

- Use `alwaysApply: true` for repo-wide conventions (for example `architecture-tier-dependencies.mdc`); use `globs` when rules apply only to certain paths.
- Prefer skills for long workflow docs; keep rules focused.
- See **llm-cursor-source** and [DOCS-DEVELOPMENT-LLM.md](/docs/development/llm/DOCS-DEVELOPMENT-LLM.md).
29 changes: 29 additions & 0 deletions .cursor/rules/abcmemory-vocabulary.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
description: User vocabulary abcmemory and abcremember for Cursor guidance files
alwaysApply: true
---

# abcmemory / abcremember vocabulary

## abcmemory

**abcmemory** means the committed files Cursor uses for agent guidance in this repo — **only** under `.cursor/` and the repo root files Cursor reads:

- `.cursor/skills/**`
- `.cursor/rules/**`
- `.cursor/prompts/**`
- `.cursor/hooks/**` and `.cursor/hooks.json` (when present)
- `.cursorrules`
- `.cursorignore`

## Not abcmemory

**`.llm/`** (plans, history, templates, context) is a **planning workspace**, not agent memory. Do not treat it as a second abcmemory tree.

Also not abcmemory unless you explicitly ask: `docs/`, `.github/`, and ad-hoc paths.

## abcremember

When the user says **abcremember**, **create or update** the best-fit abcmemory file(s) under **`.cursor/`** (or `.cursorrules` / `.cursorignore`): prefer updating existing guidance when the instruction extends the same topic; create a new skill, rule, or prompt only when no suitable file exists. Then follow that guidance in future work. Do **not** write to `.llm/` unless they explicitly say to remember something there.

For placement and workflow details, see `.cursor/skills/abcmemory/SKILL.md`.
38 changes: 38 additions & 0 deletions .cursor/rules/app-internal-import-aliases.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
description: App-internal TypeScript path aliases must match the full apps/<name>/ directory name
globs:
- "apps/api/**/*.ts"
- "apps/management-api/**/*.ts"
- "apps/api/tsconfig*.json"
- "apps/management-api/tsconfig*.json"
- "apps/api/vitest.config.ts"
- "apps/management-api/vitest.config.ts"
alwaysApply: false
---

# App internal import aliases

Node apps under `apps/` use a **TypeScript path alias** for imports from their own `src/` tree. The alias prefix must match the **full app directory name** under `apps/` — not abbreviations.

## Do

| App directory | Alias prefix | Example |
| ----------------------- | --------------------- | ------------------------------------------------------------ |
| `apps/api/` | `@api/*` | `import { config } from '@api/config/index.js'` |
| `apps/management-api/` | `@management-api/*` | `import { config } from '@management-api/config/index.js'` |

Configure in each app's `tsconfig.json` `paths` and mirror the prefix root in `vitest.config.ts` for tests. When the build uses alias rewriting, keep `tsc` + alias rewrite aligned with dist output.

## Don't

- Do not abbreviate directory names (`@mgmt-api`, `@m-api`, etc.).
- Do not use npm workspace names (`@metaboost/api/*`) for in-app imports — those are for cross-package deps only.

## Distinction

- **`@metaboost/*`** — published/workspace packages under `packages/`
- **`@<app-dir-name>/*`** — internal imports within one deployable app

## Note

When adding or extending `paths` in an app, update this table if a new Node app is introduced under `apps/`.
16 changes: 16 additions & 0 deletions .cursor/rules/app-local-ui-wrappers.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Extract app-local thin wrappers when identical @metaboost/ui + i18n configuration repeats (2+ callsites in one app).
globs:
- "apps/web/src/**/*.tsx"
- "apps/management-web/src/**/*.tsx"
alwaysApply: false
---

# App-local configured UI wrappers

When the **same** configured usage of `@metaboost/ui` (including **`next-intl`** strings for `aria-label`, titles, or messages) appears in **two or more** places **within** `apps/web` or **within** `apps/management-web`, extract a thin client component under that app’s `src/components/**` that wires translations and forwards props.

- Keep wrappers **app-local** until **both** apps need the same wiring; then consider `packages/ui` plus app wrappers for copy (see workspace **i18n** rules).
- Do **not** use bare `export { X } from '@metaboost/ui'` as a substitute — wrappers must bind meaningful app conventions.

See **`reusable-components`** skill for the full rubric.
11 changes: 11 additions & 0 deletions .cursor/rules/architecture-tier-dependencies.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
description: Package tier dependency order — lower tiers cannot depend on higher
alwaysApply: true
---

# Architecture tier dependencies

Lower tiers cannot depend on higher tiers in the monorepo package graph.

Full tier table, directory layout, and app descriptions:
[.llm/context/architecture.md](/.llm/context/architecture.md) (contributor reference).
24 changes: 24 additions & 0 deletions .cursor/rules/build-order-doc-sync.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
description: Keep build order dependency-safe and docs synced when build orchestration changes.
globs:
- package.json
- scripts/ci/run-workspaces.mjs
- .github/workflows/ci.yml
alwaysApply: false
---

# Build order and docs sync

Use the **build-order** skill (`.cursor/skills/build-order/SKILL.md`) and
[AGENTS.md](/AGENTS.md) / [.llm/context/architecture.md](/.llm/context/architecture.md) for the
current Metaboost sequence.

## Required

- Preserve dependency-safe staged build execution (`npm run build:packages` before apps).
- Keep root `build` aligned with package/app build sequencing.
- Update contributor docs when build orchestration behavior changes.

## Prohibited

- Do not reintroduce all-workspace root build execution that can violate workspace dependency order.
1 change: 1 addition & 0 deletions .cursor/rules/commands-from-metaboost-root.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Always provide runnable terminal commands relative to the Metaboost repository r
## Do

- Use root-invoked workspace commands (for example `npm run <script> -w @metaboost/<workspace> -- [args]`).
- Use root **`npm run test:unit`** for unit tests across workspaces (runs `scripts/ci/run-workspaces.mjs` with API integration workspaces excluded). Do **not** suggest `npm run test:unit -w apps/web` — `test:unit` is a root orchestration script.
- For E2E commands, use `make` targets from repo root so dependencies and seed steps are handled.
- Use `SPEC`/`WEB_SPEC`/`MGMT_SPEC` for one spec or comma-delimited spec lists.
- Put runnable commands in fenced `bash` blocks so the editor shows copy buttons.
Expand Down
66 changes: 66 additions & 0 deletions .cursor/rules/config-type-safety.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
description: Config and type safety rules - no defaults, no non-null assertions outside config
globs:
- "**/config/index.ts"
- "**/config/*.ts"
alwaysApply: false
---

# Config and Type Safety Rules

## Critical Requirements

### 1. Never Set Default Values in Config Files

**NEVER** set default values for environment variables in `config/index.ts` files. This includes:

- Empty strings: `process.env.VAR || ''`
- Fallback values: `process.env.VAR || 'default'`
- Nullish coalescing: `process.env.VAR ?? ''`

**Why**: Default values hide configuration errors and allow apps to start with invalid state.

### 2. Use Non-Null Assertions (`!`) in Config Files Only

Config files are the **one exception** where `!` assertions are allowed, because:

- All env vars must pass through startup validation before config is used
- Validation ensures required values exist before the app starts
- This keeps config files clean and typed as `string` (not `string | undefined`)

**Pattern for config files:**

```typescript
/* eslint-disable @typescript-eslint/no-non-null-assertion -- env vars validated at startup */

export const config = {
nodeEnv: process.env.NODE_ENV!,
apiPort: process.env.API_PORT!,
database: {
host: process.env.DB_HOST!,
},
};
```

### 3. Avoid Non-Null Assertions Elsewhere

Outside of config files, **avoid** `!` assertions. Prefer proper null checks, optional chaining, or type guards.

## Approved Patterns

### Backend Apps (api, management-api)

1. **Create startup validation** (`lib/startup/validation.ts`).
2. **Call validation early in app startup** before importing config.
3. **Use `!` in config with eslint-disable** (`config/index.ts`).

### Next.js Apps (web, management-web)

Validate at build time via app validate-env scripts and prebuild hooks where configured.

## Summary

| Location | `!` Allowed | Default Values |
| -------- | ----------- | ---------------- |
| `config/index.ts` | ✅ Yes (with eslint-disable) | ❌ Never |
| Other files | ❌ Avoid | Use at point of use if needed |
25 changes: 25 additions & 0 deletions .cursor/rules/css-custom-properties-no-var-fallbacks.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
description: Do not use CSS var() fallback arguments in SCSS/CSS; rely on canonical theme tokens
globs:
- "**/*.scss"
- "**/*.css"
alwaysApply: false
---

# CSS custom properties — no `var()` fallbacks

## Do not

- Use the **second argument** to `var()` as a silent fallback: `var(--token, #hex)`, `var(--token, inherit)`, etc.
- Use **nested fallbacks** such as `var(--a, var(--b))` to paper over a missing `--a` — the outer form still hides when `--a` is absent.

Fallbacks mask missing or mistyped custom properties and make theme bugs hard to see in review.

## Do

- Reference tokens that are defined in **`packages/ui/src/styles/`** — especially `_variables.scss` (shared scale/mirrors) and `_themes.scss` (theme palettes on `[data-ui-theme]`, including `--color-border` and text/surface tokens).
- If a value is missing, **add or extend a named token** in those files, then use `var(--your-token)` with **one** argument.

## Inline styles (React `style`, Storybook decorators)

Apply the same rule: no `var(--x, fallback)` in style object strings. (Repo convention avoids inline styles for layout; see **global** skill — when inline strings include `var()`, still omit fallbacks.)
15 changes: 15 additions & 0 deletions .cursor/rules/documentation-updates.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Doc updates for config changes
globs:
- "package.json"
- "packages/*/package.json"
- "tsconfig.base.json"
alwaysApply: false
---

# Documentation Updates

When changing configs:

- Update [docs/architecture/ARCHITECTURE.md](/docs/architecture/ARCHITECTURE.md) if deps or tier layout changed
- Update `.llm/context/` files when conventions or architecture shift
22 changes: 22 additions & 0 deletions .cursor/rules/e2e-seed-id-text-limits.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: E2E seed fixture id_text values must fit nano_id_v2 (9–15 characters). Count length before adding IDs to seed scripts or E2E helpers.
globs:
- "tools/**/seed-e2e.mjs"
- "apps/**/e2e/**/*.ts"
alwaysApply: false
---

# E2E seed `id_text` length limits

Postgres **`nano_id_v2`** is **`VARCHAR(15)`** with length **9–15** (see
`infra/k8s/base/ops/source/database/linear-migrations/app/0001_app_schema.sql` and
`packages/helpers/src/nanoid.ts`).

When adding or renaming deterministic E2E fixture IDs in seed scripts or E2E helpers:

- **Count characters** on every new `id_text` before commit (descriptive names often exceed 15).
- Keep IDs in the **9–15** range; prefer existing short patterns like `e2eusr000001` (12).
- Update **all** sync points together (seed, specs, helpers, docs).
- Run `make e2e_seed_web` (or `make e2e_seed`) to verify inserts succeed.

Full workflow: **e2e-seed-nano-id-limits** skill.
40 changes: 20 additions & 20 deletions .cursor/rules/end-with-targeted-make-report-verify.mdc
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
---
description: End implementation responses with targeted make screenshot-report verification commands.
description: End implementation responses with targeted make screenshot-report verification commands for the operator.
alwaysApply: true
---

# End With Targeted Make Report Verify

For implementation responses in this repo:

0. **Do not run tests during agent or plan work.** Never run test, lint, or verification commands as part of implementation. Only instruct the operator in a fenced `bash` block at the end of the response.
1. **Give the user** one or more runnable `make` commands they can copy and run to verify the change; place them in a fenced `bash` code block at the end of your response.
2. **MANDATORY when the change affects E2E:** If you modified UI (apps/web or apps/management-web), E2E spec files, or any code that E2E tests cover, you **MUST** end the response with a fenced `bash` block containing the **EXACT** command(s) needed to run the affected E2E test(s). No exception. Example: `make e2e_test_web_report_spec SPEC=e2e/settings-bucket-owner.spec.ts`.
3. Prefer screenshot-report targets scoped to the changed feature area.
3. Avoid suggesting unrelated full-suite runs by default.
4. Use full-suite report mode only when the change scope is broad or cross-cutting.
5. Place final verification commands in a fenced `bash` code block so copy-to-clipboard is available.
6. Inside the code block, list plain commands (one per line), without bullets or inline backticks.
7. **When the change includes files under `infra/k8s/`:** Add a sentence in the response that the user should push to the Argo CD–tracked branch (e.g. `develop`) for the cluster to sync. See **argocd-gitops-push** skill for details.
2. **UI changes (apps/web, apps/management-web, packages/ui):** Instruct the operator to run the narrowest targeted screenshot report and where to open it (`.artifacts/e2e-reports/latest/web/index.html` or `.../management-web/index.html`). See **ui-e2e-screenshot-report** skill.
3. **MANDATORY when the change affects E2E:** If you modified UI (apps/web or apps/management-web), E2E spec files, or any code that E2E tests cover, you **MUST** end the response with a fenced `bash` block containing the **EXACT** command(s) needed to run the affected E2E test(s). Example: `make e2e_test_web_report_spec SPEC=e2e/smoke.spec.ts`.
4. Prefer screenshot-report targets scoped to the changed feature area.
5. Avoid suggesting unrelated full-suite runs by default.
6. Use full-suite report mode only when change scope is broad or cross-cutting.
7. Place final verification commands in a fenced `bash` code block so copy-to-clipboard is available.
8. Inside the code block, list plain commands (one per line), without bullets or inline backticks.
9. **When the change includes files under `infra/k8s/`:** Add a sentence in the response that the operator should push to the Argo CD–tracked branch (e.g. `develop`) for the cluster to sync. See **argocd-gitops-push** skill for details.

API gate (default: no API tests):

- **Always** give E2E verification commands **without** `E2E_API_GATE_MODE` so the default applies: API integration tests are **skipped**. Do not add `E2E_API_GATE_MODE=on` unless the change affected API or integration tests.
- To include API tests the user must pass the env var: `make E2E_API_GATE_MODE=on e2e_test_web_report_spec SPEC=...`

Preferred command order:
Preferred commands:

- Web feature verification:
- `make e2e_test_web_report_spec SPEC=e2e/<web-spec>.spec.ts`
- Management-web feature verification:
- `make e2e_test_management_web_report_spec SPEC=e2e/<management-spec>.spec.ts`
- Cross-app web + management-web verification:
- `make e2e_test_report_scoped WEB_SPEC=e2e/<web-spec>.spec.ts MGMT_SPEC=e2e/<management-spec>.spec.ts`
- Broad smoke:
- `make e2e_test_home_report`
- Full regression (four reports: web default, web signup-enabled, web admin-only-email, management-web):
- `make e2e_test_report`
- Web: `make e2e_test_web_report_spec SPEC=e2e/<spec>.spec.ts`
- Management-web: `make e2e_test_management_web_report_spec SPEC=e2e/<spec>.spec.ts`
- Both: `make e2e_test_report_scoped WEB_SPEC=e2e/<web>.spec.ts MGMT_SPEC=e2e/<mgmt>.spec.ts`
- Broad smoke: `make e2e_test_home_report`
- Full regression: `make e2e_test_report`
- API only: `npm run test:e2e:api`

Spec variables (`SPEC`, `WEB_SPEC`, `MGMT_SPEC`) may contain comma-separated spec paths when multiple related specs should run.

Extended reference: **response-ending-make-verify** skill (command tree, API gate, multi-spec examples).
**Final COPY-PASTA prompt:** When completing the last step in a plan set (`COPY-PASTA.md` / `00-EXECUTION-ORDER.md`), assume the operator ran all prompts without running tests until the end. List **all** cumulative verification commands for the whole set in one fenced `bash` block (dedupe; order: build/lint → unit → API → E2E). See **response-ending-make-verify**, **plan-completion**, and **plan-files-convention** skills.

Extended reference: **response-ending-make-verify**, **ui-e2e-screenshot-report**, and **plan-completion** skills.
13 changes: 13 additions & 0 deletions .cursor/rules/env-expiration-naming.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: Names ending in _EXPIRATION (env + exported constants) use seconds only; no _SECONDS suffix on the symbol.
globs:
- "**/*.env*"
- "**/parseEnv*.ts"
- "**/*Expiration*.ts"
- "**/productMembershipDefaults*.ts"
alwaysApply: false
---

# Expiration naming

Follow [`env-expiration-naming`](.cursor/skills/env-expiration-naming/SKILL.md): env keys and exported constants that denote expiration durations end with `_EXPIRATION`; values are seconds — do not append `_SECONDS` to those names.
15 changes: 13 additions & 2 deletions .cursor/rules/env-file-formatting.mdc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Environment file formatting - non-empty values in double quotes, empty unset
description: Node/Docker env formatting — double quotes for non-empty values; K8s source/*.env is unquoted
globs:
- "**/.env"
- "**/.env.*"
Expand All @@ -12,9 +12,20 @@ alwaysApply: false

# Environment File Formatting

## Node / Docker env files

Applies to app `.env.example`, `infra/config/env-templates/*.env.example`,
`infra/config/local/*.env`, and `dev/env-overrides/**/*.env`.

- **Non-empty values**: Use double quotes (e.g. `API_PORT="3000"`).
- **Empty/unset values**: No value after `=` (e.g. `OPTIONAL_VAR=`).
- **Alignment**: When creating or editing generated env files, follow variable order and grouping from canonical templates/examples in [`infra/config/env-templates/`](../../infra/config/env-templates/) and app `.env.example` files. Only values may differ.
- **Alignment**: When creating or editing generated env files, follow variable order and grouping from canonical templates/examples in [`infra/config/env-templates/`](/infra/config/env-templates) and app `.env.example` files. Only values may differ.
- **Order (mixed files)**: When a file mixes server-side keys with `NEXT_PUBLIC_*`, put **all non-`NEXT_PUBLIC_*` assignments first**, blank line, then all `NEXT_PUBLIC_*`. See `.cursor/skills/env-file-formatting/SKILL.md`.

Correct: `DATABASE_HOST="localhost"`, `EMPTY_VALUE=`. Incorrect: `DATABASE_HOST=localhost`, `EMPTY_VALUE=""`.

## K8s `source/*.env` (exception)

`infra/k8s/**/source/*.env` is **not** covered by the double-quote rule above. Use **unquoted**
values: `DB_PORT=5432`, `NODE_ENV=production`, `LOG_DIR=`. See
`.cursor/skills/env-file-formatting/SKILL.md` and [infra/k8s/INFRA-K8S-BASE.md](/infra/k8s/INFRA-K8S-BASE.md).
Loading
Loading