# fabular App conventions

Fixed guidelines for building fabular Apps (source: fabular-apps/app-conventions, commit 33fb183f).
Conventions of a single system come from the tool get_coding_conventions and win where they disagree.

---

# Answers to the user

Answer in the user's language (German by default), factual and short; separate clearly what was checked from what is assumed.

No closing line such as "Kurz gesagt: …" and no summary of what was just said. Show code in the chat only when asked or when it is needed to understand an answer.

---

# Buttons and actions

One primary button per context; Save is primary and shows progress, Delete asks twice, closing with unsaved changes asks before discarding.

- A button must look like a button; secondary actions are secondary or tertiary, never a second primary.
- Save: label "Speichern"; while saving the button is disabled and shows a spinner, then a check icon for a moment, then the label again. No fixed width; the button must not jump in size.
- Delete (reversible or minor): two clicks on the same button. First click changes the label to "Wirklich löschen?"; a second click within 4 seconds deletes, otherwise the label returns to "Löschen".
- Delete (irreversible, e.g. a whole record with dependents): a modal where the user types the name to confirm.
- Unsaved changes: closing a dirty form (X, backdrop, Escape) opens "Änderungen noch nicht gespeichert" with "Verwerfen" (secondary) and "Änderungen speichern" (primary); without changes it closes directly.

Source: fabUI patterns "Buttons", "Speichern-Verhalten", "Löschen-Bestätigung", "Ungespeicherte Änderungen".

---

# Commit messages

Conventional Commits in English: `<type>(<scope>): <summary>`, summary lowercase, imperative, at most 72 characters; details in the body.

- `type`: `feat` (new behaviour for users), `fix` (bug fix), `refactor`, `test`, `docs`, `chore` (dependencies, tooling).
- `scope` (optional): the part of the App, e.g. `frontend`, `search`, `data`, or a feature name such as `orders`.
- Example: `feat(orders): filter open orders by customer`.
- Not the `[Intern]Category: …` format of the ERP repository, and no German.

The history of an App is read by developers across teams and by the ERP's review; one language and one format keep it searchable.

---

# User interface with the ERP's fabUI

Use only classes and tokens of the ERP's `fabUI.css` (check them with `get_erp_fabui_styles`), tokens instead of raw values; DOM only inside the Shadow Root via `mount()`, ERP calls only via `api()`.

- Reference a token (`var(--fab-ui-theme-color-text-primary)`, `var(--fab-ui-shared-spacing-sp-6)`), never its resolved colour or pixel value. If no token fits exactly, take the nearest step of the scale.
- The fabui repository and its Storybook use other names; most of them do not exist in the ERP.
- Reuse what fabUI.css offers (`fab-ui-button`, `fab-ui-input`, …) instead of rebuilding look-alikes. If something is missing, tell the user instead of improvising a component that only looks like fabUI.

---

# Forms

Space form fields with `var(--fab-ui-shared-spacing-sp-6)` (16 px), vertically and horizontally, in drawers, modals and pages alike.

Use the ERP's input classes (`fab-ui-input` and related, checked with `get_erp_fabui_styles`) and a date picker instead of native `<input type="date">` where fabUI.css offers one. Mark required fields and show errors next to the field, not only in a toast.

Source: fabUI patterns "Formulare" and "Datumsauswahl".

---

# Commit only green states

Commit only when `node .app/check.mjs` and, in `frontend/`, `npm run build` and `npm test` pass; `package-lock.json` is part of the commit.

The pipeline runs the same checks and stops otherwise; a red state on `main` is never delivered. Do not commit intermediate states "to save work".

---

# A merge to main is the release

Merge only when the user explicitly asks for it; the pipeline then builds the App and the ERP installs it after its review.

There is no other way into the ERP: never upload Custom Pages directly, never change the pipeline files (`.gitlab-ci.yml`, `.github/workflows/`); the ERP refuses such merge requests.

---

# Naming

All names in code and data are English: files, folders, identifiers, search and table aliases, CSS classes, tests and branches. Text shown to users stays in the users' language.

- Files and folders: kebab-case (`order-list.ts`). TypeScript: `camelCase` for variables and functions, `PascalCase` for types and classes, `UPPER_SNAKE_CASE` for constants.
- Search and table aliases in `search/` and `data/`: short English nouns, lower case (`orders`, `open-complaints`), not German (`bestellungen`).
- Column names: English, `camelCase` (`rating`, `dueDate`), without type prefixes. Foreign keys to ERP tables keep the ERP's name `FK_<Table>_SID` (`FK_CRMPartner_SID`). Roles follow the contract in `.app/agent.md` (`ROLE_APP_<APP>_<ALIAS>_…`), so English aliases give English roles.
- CSS classes of the App: a prefix of the App name, e.g. `orders-toolbar`; never `fab-ui-*`, those belong to the ERP.
- UI texts (labels, messages) are German unless the user asks otherwise; keep them out of identifiers.

---

# No secrets in the repository

No tokens, passwords, keys, connection strings or real customer data in files, commits or fixtures.

`.app/preview/` is never committed; real answers fetched with `fabai_call` belong there only. Placeholders such as `"<token>"` are fine; a hard-coded fallback for a secret is not.

---

# Repository layout

Top level only `doc/`, `frontend/`, `search/`, `data/` and the files of the scaffold; read `.app/agent.md` and `doc/agent.md` first.

`.app/agent.md` is the contract and wins over this summary. Decisions, findings and open questions about the App go to `doc/`, so the next conversation can continue from there.

---

# Small changes, few rounds

Use `workspace_edit` with short excerpts, never whole file contents as replacement; bundle independent calls in one answer; never start dev servers or watch commands.

Every output token costs time (about 30–60 per second): an edit carrying a whole file takes more than a minute, a short excerpt a few seconds.
Split large refactorings into several small edits. Read several files with one `workspace_exec`, chain build and tests with `&&`.
`npm run dev`, `vite`, `vitest` without `--run` and anything else that does not end on its own runs until the time limit.

---

# Tests

Test behaviour, not implementation: one test per behaviour with a descriptive name, covering the normal case, edge cases and expected errors; run Vitest with `--run`.

- Arrange, act, assert, no logic in tests; realistic data instead of placeholders.
- Mock ERP calls at the `api()` boundary; never call the real ERP from tests.
- `npm test` must finish on its own: no watch mode (`vitest` without `--run` waits forever and hits the command time limit).

Source: fab4minds skill `f4m-core-gen-tests`.
