# Poltura development rules

<!-- From https://developers.poltura.com/ai/rules. Download the latest: https://developers.poltura.com/ai/rules/AGENTS.md -->

These rules are for AI coding tools working on a Poltura app or theme. The full documentation index is at
https://developers.poltura.com/llms.txt, and every page is also available as Markdown by adding `.md` to its
address.

## Always

- Read the matching docs page before writing code, through its `.md` address.
- Use only the public packages: `@poltura/app-sdk`, `@poltura/app-ui`, `@poltura/app-contracts`, `@poltura/cli`
  and `@poltura/theme-sdk`. Never import other `@poltura/*` packages and never reach a store's database.
- Do not invent APIs. If a name is not in the docs or in the package's types, it does not exist.
- Before you say the work is done, run `poltura check` in the app or theme directory and fix every error it
  reports. Each finding names the rule and says what to change. Use `poltura check --json` to read the result.

## Apps

- An app is `poltura.app.json` plus its sources. Scaffold one with
  `poltura app create <dir> --template native` or `--template hosted`.
- After every change run `poltura app build` and `poltura app validate`, and fix every error before moving on.
  Before finishing, run `poltura check`: it adds the permission, agent entry and code rule checks.
- Parse input where it enters your code: no `unknown` parameters or return types, no dictionaries of `unknown`,
  `any` or `object`, no runtime `typeof` on unparsed values, and a `// SAFETY:` comment on every type assertion.
- Declare the fewest permissions the app needs. Each permission needs a `purpose` of 8 to 280 characters that a merchant
  understands, because merchants read it before they approve.
- Declare storefront blocks and their settings once, with `defineBlock` from `@poltura/app-sdk/elements`, in the
  file `elements` names. A worker reads the merchant's values with `useSettings()`.
- A worker file starts with `import '@poltura/app-ui/worker'`, renders with `startUiWorker(<Root />)` from
  `@poltura/app-ui/worker-runtime`, and draws only with the primitives of `@poltura/app-ui/admin` (admin
  targets) or `@poltura/app-ui/storefront` (storefront targets).
- Workers cannot fetch your server or the App API. They call your backend through `useAction(actionId)`, and
  the backend verifies the request with `createActionHandler` from `@poltura/app-sdk/actions`.
- Installation access tokens stay on your server. Never send one to a worker, an iframe or the browser.
- Call the App API with `PolturaClient` from `@poltura/app-sdk`. Lists are cursor pages (`limit` up to 100,
  then `nextCursor`). Every write carries an `Idempotency-Key`. Money is `{ amount: "49.99", currency: "EUR" }`
  in major units; never multiply by 100.
- Verify every webhook with `createWebhookVerifier` from `@poltura/app-sdk/webhooks` before reading its body.
  Deliveries are retried, so handling one twice must be harmless.
- Publish with `poltura app publish --api "$POLTURA_API" --token "$POLTURA_APP_TOKEN"`, with `appId` in
  `poltura.app.json` or `--app`. The token is a developer token (`pltd_` for CI, or a personal `pltu_`) from
  Developer apps. Never commit a token.

## Themes

- A theme is TSX and JSON, edited in the store's theme code editor. Saves go to the theme's draft; publishing
  is a separate step.
- Import Poltura APIs from `@poltura/theme-sdk/<module>` (there is no root import). Besides that a theme may
  import its own files, React and a fixed set of UI libraries the theme check names; anything else is refused.
- Write one section per file, `src/sections/<type>.tsx`, and one block per file, `src/blocks/<type>.tsx`,
  declared with `defineSection` or `defineBlock` from `@poltura/theme-sdk/elements`. The `type` is kebab-case,
  matches the file name and stays fixed once merchants use it; rename with `renamedFrom`.
- A section's component receives `attrs`. Spread them onto exactly one root element and merge classes with
  `cn(attrs.className, ...)` from `@poltura/theme-sdk/ui`. Without that, the merchant's style controls do
  nothing.
- Settings are `field.*` builders with a `label` and a `default`. Read them from the typed `settings` prop, and
  use `EditableText` for text merchants edit in place.
- Draw prices with the `price` parts and format any other amount with `useFormat().money()`. Never format money
  by hand.
- Theme code renders on the server for every shopper of the store: no per-shopper state and no clock reads at
  module scope, and no `window` or `document` outside effects.
- Default copy has no em dashes and no middle dots.
- In the code editor the theme check runs on every save. Fix every problem it reports before publishing.
- Working on a theme in files, run `poltura check` before finishing; it runs the theme check and the code rules.
