AGENTS.md
Rama main · réplica de lectura del repositorio original.
# Minddy agent rules and development conventions
`AGENTS.md` and `CLAUDE.md` are identical copies of this guide, so every coding
agent reads the same rules. Edit both files together;
`scripts/agent-instructions.test.mjs` fails when they differ.
## Architecture and entry points
Minddy is a Next.js App Router application with React, Tailwind CSS, and its
own UI components. Supabase provides Postgres, Auth, Storage, and Realtime. The
optional Electron shell has a separate package and build under `desktop/`.
| Area | Source of truth |
| --- | --- |
| Authenticated product routes | `app/(app)/` |
| Marketing and legal routes | `app/(marketing)/`, `app/(legal)/`, `lib/public-routes.ts` |
| HTTP APIs and public capabilities | `app/api/`, `app/f/`, `app/p/` |
| UI primitives and design tokens | `components/ui/`, `app/tokens.css` |
| Reusable UI and client behavior | `components/`, `lib/` |
| Application URLs (short codes, never titles) | `lib/app-urls.ts`, `lib/ref-registry.ts` |
| Rich text editor engine (every surface) and its block catalog | `components/editor/`, `components/pages/blocks/` |
| Server business logic | `lib/server/` |
| Browser, session, and privileged database clients | `lib/supabase.ts`, `lib/supabase-server.ts`, `lib/supabase-service.ts` |
| Numo conversations and tools | `lib/server/numo/`, `lib/server/assistant/` |
| Pull requests and forge integration | `lib/server/agent/`, `lib/server/git/` |
| External MCP tools and authentication | `lib/server/mcp/` |
| Database schema and policies | `supabase/migrations/` |
| Locale catalogs and configuration | `messages/`, `i18n/config.ts` |
| Release and maintenance tooling | `scripts/`, `.github/workflows/` |
Build every in-app link with `lib/app-urls.ts`: URLs address records by an
eight-character short code (`/projects/min-3f2a9c81`, `?issue=MIN-42`), never
by a title, because names are encrypted at rest and paths reach logs and
analytics. Read URL references back through `lib/ref-registry.ts` on the client
or `lib/server/route-refs.ts` on the server; both still accept full ids.
Server-only modules use the `server-only` import guard. Keep authorization at
server boundaries; a privileged database client does not replace membership or
capability validation. Follow the existing domain repository and codec when
reading or writing protected content rather than accessing its raw columns.
`npm run dev` and `npm run build` first generate the page-Markdown bundle
through their lifecycle hooks. Vitest also builds the page projection
through `test/build-pages-md-setup.ts`. The root TypeScript configuration excludes
`desktop/`; CI checks the Electron bundle separately with
`node scripts/build-desktop.mjs`.
See [README.md](README.md), [CONTRIBUTING.md](CONTRIBUTING.md), and
[the edition guide](docs/editions.md) for setup and supported deployment modes.
## Language policy: English-owned prose
- All new comments, docstrings, test descriptions, internal documentation,
configuration prose, and developer-facing CLI messages must be written in
idiomatic English.
- Do not add French prose to application code, tests, documentation, scripts, or
configuration. French runtime copy belongs in `messages/fr.json`; official
reader documentation and its assets use their declared locale under
`content/documentation/<locale>/` and `public/documentation/<locale>/`.
- Preserve intentional runtime translations in every supported catalog,
language-specific test fixtures, locale branches, proper names, legal credits,
identifiers, URLs, API values, and behavior. Do not translate those into
English copies.
- When editing existing French prose, translate it as part of the same change
unless it is one of the intentional runtime or language-fixture exceptions.
Do not change runtime translations or test semantics to satisfy the language
policy.
### Translation-task boundary
Translate comments and documentation without changing code, identifiers, URLs,
configuration keys, migration behavior, or test semantics. If a French string
is required to test localization or to serve the French locale, keep it and
make the exception obvious from its surrounding code or fixture path.
## Verification
- Run `npm run check:owned-english` after changing comments, documentation,
tests, scripts, or configuration prose.
- Run the smallest relevant test or lint command after source/configuration
changes (see [Lint and tests](#lint-and-tests)).
- Review `git diff --check` and confirm that excluded paths are untouched.
## Keep documentation current
- Always review documentation impact when modifying code. Add documentation
for new features, correct it when behavior changes, and remove obsolete
instructions and illustrations when a feature is removed. Complete affected
public guides, technical references and internal instructions in the same PR;
do not defer documentation to a later release or separate task.
- Documentation edits are required only when the change affects documented
behavior, controls, configuration or contributor workflows. A small fix,
refactor, formatting change or test-only PR that leaves documentation accurate
needs no documentation edit or new illustration. An accurate manual may need
no prose edit, but it always needs an impact review.
- Follow the [documentation maintenance contract](content/documentation/README.md#release-maintenance)
and [editorial guide](docs/documentation-editorial-guide.md), including their
writing, technical inline-code, factual review, illustration and capture rules.
- Keep the public manual equally complete in `en`, `fr`, `de`, `es`, `it`, and
`pt-BR`. Update full translations, localized UI labels, examples, captions,
alt text, screenshots and responsive diagrams together. Internal contributor
documentation remains English.
- Every release that changes the desktop version requires a documentation
review against the release delta and intended version before freezing the
candidate. Update affected articles, illustrations, compatibility and the
frontmatter `review` field; a version bump or changelog entry alone is
insufficient.
- Run `npm run check:documentation`, `npm run check:knowledge`,
`npm run check:owned-english` and `git diff --check` for documentation
changes, and require `npm run check:documentation:release` before
publication. List affected article/workflow/figure IDs and checks in the pull
request description, not in a committed file. For a PR with no documentation
impact, a brief note in the description is sufficient; do not require article
IDs or release-wide checks. Avoid cosmetic edits or invented review dates.
Unresolved documentation gaps caused by the change block completion.
## Repository hygiene: what a pull request may add
The repository holds the product, the tooling it runs again, and durable project
documentation. The working material of a change (its plan, investigation,
evidence and screenshots) belongs in the pull request description or the Minddy
issue, never in the tree. Before opening or updating a pull request, read
`git diff --stat origin/main...` and remove every file of the kinds below.
- **Documentation.** Add a file under `docs/` only for durable, general
knowledge: architecture, a cross-cutting contract, operations, security or a
contributor workflow. A fix, a feature or a refactor does not add a Markdown
file of its own; a standalone document is justified only when the change
reshapes Minddy deeply (a new subsystem or trust boundary), and it is then
written as a reference, not as a ticket report. Feature behavior goes into the
public manual or an existing reference. Never commit per-ticket plans,
audits, investigation notes, validation, performance or review reports,
evidence JSON, checklists or launch drafts.
- **Scripts.** Commit only scripts the project runs again: package scripts, CI,
release, tests and capture tooling. Delete one-off probes, benchmarks,
backfills, measurement harnesses and debugging scripts before the pull
request, or keep them outside the repository.
- **Images.** Committed images are product assets (`public/`, `app/`,
`desktop/build/`), manual figures under `public/documentation/<locale>/`, and
capture masters `captures/shots/<name>/out/<locale>-<theme>.png`, which
`captures/lib/publish.mjs` and `captures/focus-marketing.mjs` read. Pull request
screenshots, before/after comparisons, debug captures and test output are
never part of the tree. To show screenshots in a pull request description,
save them under the ignored `output/` directory, commit them with
`git add -f`, remove them in the next commit and push both commits together;
link each image through the first commit's SHA
(`https://github.com/mangue-dev/minddy/blob/<sha>/<path>?raw=true`). Squash
merging keeps them out of `main`, and the pull request keeps that commit
reachable. Never rewrite that commit away while the description links it.
- **Generated output.** Playwright results, capture run records, benchmarks and
other run output go under the ignored `output/` directory.
`npm run check:public-repo` (also run in CI) rejects the known working-material
locations: `output/`,
`docs/{audits,plans,validation,performance,reviews,screenshots}/`, images
anywhere under `docs/`, `content/documentation/reviews/`, `scripts/performance/`,
deepsec scan output under `.deepsec/data/` and capture outputs that are not
masters. Passing it does
not make another report, script or image acceptable.
## UI components
Minddy owns its UI primitives: `components/ui/` holds one implementation per
component (Radix-based controls, dialogs, sheets, menus, tooltips) and
`app/tokens.css` the design tokens that `app/globals.css` imports. Change a
primitive in its file rather than wrapping or re-exporting it under the same
name elsewhere; a variant with different behavior gets its own name, as
`ResponsivePopover` does next to `Popover`. Import primitives from their module
(`@/components/ui/button`, `@/lib/utils` for `cn`, `sonner` for `toast`); there
is no UI barrel. The former `mangue-ui` package is no longer a dependency, and
`lib/ui-components-ownership.test.ts` fails if it returns. The persisted theme
key `mangue-ui-theme` is kept for existing preferences.
[DESIGN.md](DESIGN.md) holds the interface rules every UI change follows,
starting with the rule that destructive actions always ask for confirmation
through `ConfirmDeleteDialog`.
## Internationalization: catalog and call-site contract
Visible interface copy uses `next-intl`. The supported locales are `en`, `fr`,
`de`, `pt-BR`, `it`, and `es`, defined in `i18n/config.ts`. Keep all catalogs aligned
on keys and placeholders when changing a message.
A placeholder message must receive its values:
```tsx
// messages/en.json: "deleteViewTitle": "Delete “{name}”?"
t("deleteViewTitle", { name: view.name });
```
The key types in `global.d.ts` do not prove placeholder correctness: imported
JSON values widen to `string`. Run the actual formatter contract whenever you
change `messages/*.json` or add a translation call:
```bash
npx vitest run lib/i18n-contract.test.ts
```
The test checks catalog parity and missing values at statically identifiable
call sites. Runtime-assembled keys still need explicit review. Use
`MessageKey<"Namespace">` from `lib/i18n-keys.ts` for key tables, and preserve the
namespace when typing a translator prop:
`ReturnType<typeof useTranslations<"Namespace">>`.
Angle-bracket syntax such as `<word>` is a rich-text tag in ICU messages. Use
plain notation such as `HMAC-SHA256(body)` for technical text unless a rich-text
formatter is intended.
## Writing user-facing text
Read [`.claude/skills/no-slop-writing/SKILL.md`](.claude/skills/no-slop-writing/SKILL.md)
and its `references/` before writing or changing any text a person reads: UI
strings and catalog values, the landing and marketing pages, emails,
notifications, error messages, the manual, changelogs and CLI output. This
applies when the text is only a small part of a code task. Claude Code loads it
as the `no-slop-writing` skill; other agents open the file at that path.
Where the skill and this repository disagree, the repository wins:
- Update every catalog in the same change, reviewed key by key, rather than
leaving the other locales for later translation (see the next section).
- The register is set per locale by
[docs/localization-and-copy.md](docs/localization-and-copy.md): formal in
French, informal in the other languages.
- Product names (minddy, Numo, Smart Fill, Smart Assign) keep their casing.
## Interface copy and translations
[docs/localization-and-copy.md](docs/localization-and-copy.md) is the contract
for every visible string: how to write the English source, the per-locale
register and glossary (issue term, statuses, product names), translation rules,
and how the manual and its screenshots follow a label change. Read it before
changing `messages/*.json`, `lib/command-palette/i18n.ts` or quoted labels in
`content/documentation/`.
- Review every translated string key by key against `messages/en.json`; never
ship unreviewed machine translation. Check meaning, grammar, register and the
glossary, and keep the ICU structure exactly.
- Use one term per concept per locale (issue → ticket, Ticket, incidencia,
ticket, ticket) and never translate product names (minddy, Numo, Smart
Assign, Smart Fill). Forge objects keep their own names.
- Address the user in the register the guide sets for each locale (formal
in French, informal in the other languages), in the UI and the manual alike.
- When a label changes, update it in all six catalogs, then every article that
quotes it, its figure alt and caption, the diagram SVG fallback, and recapture
the screenshots that show it with the scripts listed in the guide.
- Run `npx vitest run lib/i18n-contract.test.ts lib/i18n-glossary.test.ts lib/i18n-unused-keys.test.ts`
after a catalog change, plus `lib/seo-surface.test.ts` when page metadata
changes.
- Reuse a `Common` label (cancel, save, delete, copy…) instead of adding a
namespace key with the same text, and delete a key in all six catalogs when
its last caller goes; `lib/i18n-unused-keys.test.ts` fails on keys no code
reads.
## Public routes and sitemap dates
`lib/public-routes.ts` defines public route metadata and explicit localized URLs.
The proxy rewrites localized URLs to their canonical English implementation and
sets the locale header; internal authenticated routes use the locale preference
cookie. The table is shared by the sitemap, metadata, proxy, and navigation.
When public content materially changes, update only the corresponding
`lastModified` date (`YYYY-MM-DD`). Avoid changing dates on every build or for
refactors that preserve content. Changelog freshness comes from
`CHANGELOG_LAST_MODIFIED` in `lib/changelog.ts`.
Adding a public page requires its route implementation, catalogs, and a matching
`PUBLIC_ROUTES` entry with every supported locale path. Review neighboring route
and metadata tests as well.
## Toolchain and lockfiles
Use Node.js 24 (24.22.1 or later, the October 2026 security release) and
pnpm **10.28.0**, as pinned by the `packageManager` field
of `package.json` and by [the CI workflow](.github/workflows/ci.yml). Keep the
two identical: `pnpm/action-setup` fails when they differ, and the field is
what makes Dependabot regenerate `pnpm-lock.yaml` with the same pnpm instead of
dropping its overrides and patches.
```bash
corepack prepare pnpm@10.28.0 --activate
pnpm install --frozen-lockfile
```
The repository maintains `pnpm-lock.yaml` for actual installation and
`package-lock.json` for npm compatibility. After an intentional dependency
change, synchronize both lockfiles using the pinned pnpm version and
`npm install --package-lock-only --legacy-peer-deps`. Review the lockfile diff;
preserve `pnpm.packageExtensions`, overrides, and patched dependencies.
`@lobehub/icons` is imported per logo file, which needs React alone, so
`pnpm.packageExtensions` marks its `@lobehub/ui` and `antd` peers optional.
`npm run typecheck` uses the repository's TypeScript 7 compiler. The editor may
use a different TypeScript version; use repository diagnostics for validation.
Source-inspection tests import the `typescript-api` alias for the TypeScript 5
compiler API. Do not substitute an import from `typescript` in these tests.
`tsconfig.json` enables incremental compilation; remove `tsconfig.tsbuildinfo`
only when a clean diagnostic or timing comparison is required.
## Lint and tests
Run the smallest relevant behavioral tests after a code change, along with the
repository lint and type check where applicable:
```bash
npm run lint
npm run typecheck
npx vitest run lib/server/assistant
npm test
npm run test:tooling
```
`npm run lint` runs `oxlint --deny-warnings`. The vendored anti-slop plugin lives
in `tools/oxlint/anti-slop/`; `oxlint.config.ts` documents intentionally disabled
rules and their historical audit counts. Re-enable a count-based rule only after
its violations reach zero. Explain each local suppression with the concrete
reason that makes it necessary. For example, copying `headers.keys()` before
mutating the collection prevents iteration from skipping entries.
A successful type check does not prove lifecycle or runtime behavior. Add a
focused regression for a behavior change and read a neighboring test before
inventing a new fixture or mocking approach. Avoid tests that merely duplicate
the implementation. Vitest discovers tests in `lib`, `components`, and `tools`,
using Node by default; DOM suites opt into jsdom per file. Prefer observable
outcomes and distinct boundary cases over exact CSS, logo, or source formatting
assertions. Structural checks remain useful for security and cross-file contracts
that runtime tests do not exercise. Do not require a test file for every module.
Use fake timers for polling and debounce tests instead of real elapsed waits;
keep real process deadlines where process termination is the behavior under test.
| Test concern | Existing example |
| --- | --- |
| Pure logic | `lib/plan.test.ts` |
| Numo streaming loop | `lib/server/assistant/loop-completion.test.ts` |
| Numo tool contracts | `lib/server/assistant/tools-contract.test.ts` |
| Server boundary with external dependencies mocked | `lib/server/assistant/pull-request-writes.test.ts` |
CI additionally runs release tooling, self-hosted contracts, edition-specific
build/start checks, dependency auditing, and publication/secret guards. Consult
the workflow for the current commands; historical test counts and local timings
are not acceptance criteria. Opt-in database tests need an isolated fixture and
must be reported separately from the default suite's skipped cases.
`test:tooling` discovers deterministic Node tests under `scripts` and
`captures/lib`; `*.integration.test.mjs` suites keep their explicit prerequisites
and commands. CI starts its jobs in parallel: the publication and secret guards
(the pull request's own commits on a pull request, the whole history on
`main`), lint/typecheck/contract validation, one build-and-start job per
deployable edition, and four Vitest shards. The required `Tests & typecheck`
gate fails unless every one of them succeeds.
## Database environments and migrations
Preview (`main`, `preview.minddy.app`) and production (`production`,
`www.minddy.app`) use two distinct databases; see
[database environments](docs/database-environments.md).
- Develop and verify against the preview database. The local `.env`, the
capture scripts and the test accounts point there, and pull request evidence
comes from it.
- Apply a new migration to preview only: check that
`supabase/.temp/project-ref` is not the production ref, run
`supabase db push --dry-run`, confirm that only the new file is listed, then
`supabase db push`. State it in the pull request description.
- Never apply a migration, a reset or another write to the production database
by hand. `Promote production` applies pending migrations at release time.
- Keep each migration compatible with the previous release: add before use,
remove only once no deployed code reads the old shape.
## Encryption boundaries and operational gates
Scoped managed keys, row/object codecs, and bounded backfill workers live in
`lib/server/encryption/`. Domain `*-content.ts` modules connect those codecs to
application readers and writers. Project, user, and system scopes are distinct;
keep decrypted keys and content out of client persistence and diagnostic logs.
Changes to encrypted surfaces require both access and schema validation:
```bash
npm run check:encrypted-access
npm run check:encryption-schema
```
Read [the encryption inventory](docs/security/encryption/README.md) and
[the closure matrix](docs/security/encryption/closure-matrix.md)
for source/copy coverage, writer fences, authenticated backfill proofs,
historical-key requirements, sandbox cleanup, and local-client copies.
Keep code CI readiness separate from production data closure. The readiness
endpoint explicitly reports `globalReadiness: "not_assessed"`; critical-family
and forge-object scans cannot certify the whole application or retained copies.
Staging performance, actual Storage-service restore, historical-copy retirement,
and provider cleanup remain operational gates. Do not infer completed production
encryption from isolated fixtures, and do not enable flags or run a migration or
deployment without the applicable authorization.
## Background work and response lifetime
Use `afterOrNow` from `lib/server/after-safe.ts` for best-effort work outside a
request's critical path, including usage timestamps, session refresh, and
opportunistic maintenance:
```ts
afterOrNow(async () => {
await performMaintenance();
});
```
Return or await the work inside the callback. Detaching a promise from a request
can allow its invocation to finish before the work completes. `afterOrNow`
schedules with Next.js `after()` and catches errors; outside a request context it
starts the callback immediately. Required writes must still complete in the
critical path so a successful response reflects their outcome.
## Git workflow (this repository only)
Apply these rules only when the `origin` remote is `mangue-dev/minddy`. Never
apply them to another project.
### Before changing code
1. Check the Git state and refresh the remote references
(`git fetch origin --prune`).
2. Pick one dedicated branch for the goal.
3. Check whether that branch already exists locally or on `origin`:
- If it exists locally, switch to it and continue the work there.
- If it exists only on `origin`, create its local tracking branch.
- If it does not exist, create it with
`npm run work:start -- "short work name"`.
4. Never create a second branch for the same work.
5. Never commit or push directly to `main` or `production`.
6. Always preserve pre-existing uncommitted changes.
### When the work is ready
- Run `npm run work:pr -- "Short title" -m "Complete description"` to create
the pull request. The `-m` flag may be repeated for several paragraphs.
- If the pull request already exists, the command pushes the new commits but
keeps its title and description, including manual edits. Use `npm run work:pr`
for follow-up commits; supplying another title or description also succeeds
without replacing either field. Never retitle or rewrite the description
of an existing pull request unless explicitly requested. A deliberate rewrite
needs the explicit `--replace` flag of `work:pr` (or a manual `gh pr edit`).
- After confirming the pull request is merged, run `npm run work:done`.
- Run `npm run deploy` only when the user explicitly asks for a production
deployment.
## Commits and pull requests
- Always write commit messages and pull request titles and descriptions in
idiomatic English.
- Commit messages follow the Conventional Commits style used in this
repository (`feat:`, `fix:`, `refactor:`, …).
- Every pull request carries a complete, real description: what was done, why,
how it was verified, and anything a reviewer needs to know. Never reduce a
pull request description to a bare `Signed-off-by` trailer or a copy of the
commit message. The DCO trailer belongs in the commits, not as the body of
the pull request.
- For UI changes, include screenshots of the work in the pull request
description whenever feasible. Keep these images up to date as the pull
request evolves. Prefer light mode by default; include both light and dark
mode screenshots when relevant, but covering both modes is optional. The
images stay out of the tree; see
[Repository hygiene](#repository-hygiene-what-a-pull-request-may-add).
- Every commit must carry the DCO sign-off (see below).
## DCO sign-offs
- Every commit an agent creates or amends must include a `Signed-off-by`
trailer that exactly matches the commit author's name and email. Use
`git commit --signoff`, including when amending a commit.
- Never use a generic agent identity for the sign-off when the commit has a
different author. Before pushing or updating a pull request, verify every
non-merge commit in its range has the matching trailer and repair the
history when necessary.
## Minddy tools
- Treat the Minddy MCP tools as available for issue work in this repository.
Discover their callable names by searching for the `mcp__minddy__` prefix;
do not conclude that they are unavailable from an exact short-name lookup.
- When an issue identifier and project ID are provided, read the issue and its
plan, keep plan task states synchronized while working, add a concise outcome
comment, and update the issue status when the requested work is complete.
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->