# Workflows

## Prerequisites (once per machine)

- [ ] Docker, Node 24, `corepack enable` (pnpm 11)
- [ ] GitHub classic PAT with `read:packages`:
      `npm config set "//npm.pkg.github.com/:_authToken" <token>`
      `npm config set "@ezytail:registry" https://npm.pkg.github.com`
- [ ] From the EzyFlow portal (ask an activity administrator):
  - *API Tokens* page → `EZYFLOW_CLIENT_ID`, `EZYFLOW_CLIENT_SECRET` (secret shown once)
  - *Webhooks* page → register `<origin>/api/ezyflow/deliveries`, keep `EZYFLOW_WEBHOOK_ID`
    and `EZYFLOW_WEBHOOK_SECRET`
  - the activity key → `EZYFLOW_ACTIVITY`

## Scaffold (done once)

```sh
npx @ezytail/create-ezyflow-app ezyorder \
  --activity <ACTIVITY> --env recette --add-ons webhooks,i18n --package-manager pnpm -y
```

Then copy `.ai/` and `.claude/agents/` in, and paste `.ai/AGENTS-snippet.md` into `AGENTS.md`
outside the Brief markers.

## Run locally

```sh
docker compose up -d postgres oidc   # Postgres :5433, mock OIDC :8080
pnpm db:migrate
pnpm dev                             # http://localhost:3000
```

Fill `.env.local`: the four `EZYFLOW_*` secrets above, and
`SESSION_SECRET` (32 random bytes — the exact `node -e` line is in the file).
Mock login: type any subject and paste claims such as
`{ "email": "you@example.test", "email_verified": true, "groups": ["orders-admins"] }`
(`orders-viewers` for a viewer).

Full stack in containers (needs a real OIDC issuer): `docker compose --profile app up -d`.

## Add a push form (e.g. receipt notice → `procurement.created`)

1. Ask the activity what it accepts: MCP `getActivityEventCatalog`, then `getEventSchema` for
   `procurement.created`. Note required fields beyond the canonical schema.
2. `src/server/procurement-push.ts`: Zod input → `procurementCreatedBody(input)` (typed via
   `Extract<…, { type: "procurement.created" }>`) → `rehearseProcurementPush` and
   `pushProcurement` server functions, both `requireAdmin`, both through `asAnswer`.
   Model it on `src/server/push.ts`.
3. Unit tests: body builder, viewer refused, admin rehearsal OK, Problem surfaced.
4. Route `src/routes/procurements/new.tsx`: TanStack Form with the same Zod schema; Rehearse →
   Push; `ProblemPanel` on refusal; toast + link on acceptance.
5. Message keys in `messages/en.json` **and** `messages/fr.json`.
6. Nav entry in `app-shell.tsx` (admin only).
7. Playwright spec `tests/e2e/procurement-push.spec.ts`.
8. Rehearse a real body through MCP against `recette` before the first real push.

## Add a read screen (e.g. procurements list / detail)

1. `src/server/procurements.ts`: `listProcurement` / `getProcurement` /
   `getProcurementTimeline` behind `requireAppUser`, mapped to a view model.
2. Routes `src/routes/procurements/index.tsx` and `$key.tsx`, using `DataTable` and `PageHeader`.
3. Tests + message keys + nav entry, as above.

## Add a delivery handler

1. `src/server/handlers.ts`: `registry.on("procurement.created", async (delivery) => { … })`.
2. Make it idempotent (upsert by `delivery.payload.id` / reference, never blind insert).
3. Try it locally: `pnpm ezyflow:deliver procurement.created` (`--msg-id <id>` twice to see
   dedup, `--test` for a test delivery).

## Upgrade to a new API release

```sh
pnpm add @ezytail/ezyflow-sdk@<v> @ezytail/ezyflow-start@<v>   # always the same version
pnpm db:migrate
pnpm ezyflow:agents
pnpm test && pnpm build
```

Then re-read `docs/ezyflow/push.md` and update `.ai/domain.md` if fields moved.

## Release

- Image: `docker build` with the `node_auth_token` build secret (never baked into a layer).
- Helm (if added): `k8s-charts/app`, secrets via `existingSecret`, probes on `/ready` and `/health`.
