# CLI-First Workflow

## When it applies

You arrived here from Step 0 of `SKILL.md`. The task is either (a) adding or removing a standard
CRUD operation from a module, or (b) understanding how the regeneration interacts with hand edits
already in place. For custom endpoints declared via `additionalApis`, use
[`additional-apis-workflow.md`](additional-apis-workflow.md) instead.

## Why CLI-first

The `aurora-catalyst` CLI emits every standard artifact of a module — controller, resolver, handler,
service, DTO, GraphQL schema, Sequelize model, repository, field schema, seed — from a single
source: `cliter/<bc>/<module>.aurora.yaml`. Hand-writing one of those files is fighting the
generator: the next `catalyst load` will either overwrite your file (if the integrity hash matches
the previous generation) or emit a `.origin` file next to yours (if it detects a divergence) and
leave you to reconcile. Both outcomes waste work that a YAML edit would have avoided. The rule is:
**if the CLI can generate it, let it**.

## The regeneration command

```bash
catalyst load back module --name=<bounded-context>/<module> --force
```

Short aliases:

```bash
catalyst load back module -n <bc>/<module> -f
```

Relevant flags:

| Flag               | Alias | Purpose                                                        |
| ------------------ | ----- | -------------------------------------------------------------- |
| `--name`           | `-n`  | `<bc>/<module>` (required)                                     |
| `--force`          | `-f`  | Overwrite existing files whose lockfile integrity hash matches |
| `--tests`          | `-t`  | Also generate e2e test files                                   |
| `--verbose`        | `-v`  | Report every step on screen                                    |
| `--noGraphQLTypes` | `-g`  | Skip the automatic `pnpm back:graphql:types` after generation  |
| `--noReview`       | —     | Skip the interactive `.origin` file review at the end          |
| `--target=<dir>`   | —     | Override the output subdirectory (defaults to `backend`)       |

The command also runs `pnpm back:graphql:types` automatically unless `--noGraphQLTypes` is set, so
the GraphQL type exports stay in sync with the YAML.

The frontend variant is `catalyst load front module --name=<bc>/<module>`; it is out of scope for
this skill.

## YAML switches that control generation

Both switches live at the top level of `<module>.aurora.yaml`, next to `aggregateProperties` and
`additionalApis`.

### `excludedOperations`

Tells the CLI to skip generating the named operations. Absent or empty list means "generate
everything".

```yaml
excludedOperations:
  - count
  - getRaw
  - max
  - min
  - sum
  - upsert
```

Operations the CLI recognizes:

| Operation             | Generates handler/service for                   |
| --------------------- | ----------------------------------------------- |
| `count`               | Count the rows matching a filter                |
| `create`              | Create a single row                             |
| `createBatch`         | Create many rows in one call                    |
| `delete`              | Delete many rows by filter                      |
| `deleteById`          | Delete one row by id                            |
| `find`                | Find one row by filter                          |
| `findById`            | Find one row by id                              |
| `get`                 | Get many rows by filter                         |
| `getRaw`              | Get many rows with raw SQL shape (no `@Format`) |
| `paginate`            | Paginated list                                  |
| `update`              | Update many rows by filter                      |
| `updateAndIncrement`  | Update + atomic increment of a counter field    |
| `updateById`          | Update one row by id                            |
| `upsert`              | Insert or update depending on key presence      |
| `max` / `min` / `sum` | Aggregate handlers over a column                |

To add an operation the YAML currently excludes: remove it from `excludedOperations` and run
`catalyst load back module --name=<bc>/<module> --force`. The CLI emits the handler, service,
resolver, controller, DTO, and GraphQL fragment. To remove an operation that was previously
generated: add it to `excludedOperations` and run `catalyst load ...` — **note**: the CLI does not
delete already-generated files; you have to remove them by hand after updating the YAML.

### `excludedFiles`

Fine-grained opt-out: skip specific file paths even if the operation itself is enabled. Mostly used
when an `additionalApis` variant collides with a CRUD file, or when a module has a custom
hand-written file that must not be overwritten.

```yaml
excludedFiles:
  - backend/src/@api/business-partner-portal/partner-address/handlers/business-partner-portal-create-partner-address-tenant.handler.ts
  - backend/src/@api/business-partner-portal/partner-address/handlers/business-partner-portal-update-by-id-partner-address-tenant.handler.ts
```

To include a previously-excluded file: remove its path from `excludedFiles` and run
`catalyst load ...`. The CLI will emit it. To exclude a newly generated file: add its path to
`excludedFiles` (the CLI will skip it on the next run, but again won't delete what already exists on
disk).

## Lockfile and `.origin` interaction

Every generated file has a lockfile entry recording the SHA-1 of the contents the CLI emitted. On
regeneration, the CLI compares the integrity hash of the file currently on disk with the lockfile
hash:

- **Hashes match** → no hand edits. The CLI overwrites the file with the new generated output.
- **Hashes differ** → the file has hand edits. The CLI writes the new generated output to
  `<file>.origin` next to the user's file. The user reviews the `.origin` and decides whether to
  merge changes. The `--noReview` flag skips the interactive step.

Consequence for hand edits: if you change only the body of a generated handler's `main()` and leave
the scaffold intact, regeneration produces a `.origin` with the (probably identical) scaffold.
Reviewing is trivial. But if you change decorators, imports, or the signature, the CLI regenerates
the "canonical" scaffold into `.origin` and you must decide each time whether your divergence is
intentional.

This is why Step 0 prefers editing YAML over hand-editing generated files for structural changes.

## Decision matrix

```
Need a new CRUD operation?
  └─ Is it in excludedOperations? → Remove the entry, run catalyst load --force.
  └─ Is it blocked by excludedFiles? → Remove the entry, run catalyst load --force.
  └─ Neither? → The operation is already generated; look under @app/<bc>/<module>/application/.

Need a custom verb (activate, approve, cancel, …)?
  └─ Go to additional-apis-workflow.md — not this file.

Need to remove an operation the CLI currently generates?
  └─ Add it to excludedOperations (or the relevant files to excludedFiles).
  └─ Run catalyst load --force.
  └─ Delete the now-orphaned files on disk by hand.

Need to change the body of a generated handler's main()?
  └─ Edit the handler file directly. The lockfile tracks the divergence; .origin review on
     next regen is trivial because your edits do not touch the scaffold.

Need to change decorators, signatures, or imports of a generated handler?
  └─ First ask: is this a YAML-expressible change? If yes, edit YAML. If no (truly custom),
     hand-edit and accept the .origin review on each regen. Consider whether the operation
     should move to additionalApis instead.
```

## Quick reference: YAML fragment shapes

```yaml
version: 0.0.1
boundedContextName: iam
moduleName: role
# ... aggregateProperties ...
additionalApis:
  - path: iam/role/inherit-permissions-role
    resolverType: mutation
    httpMethod: post
excludedOperations:
  - count
  - getRaw
  - max
  - min
  - sum
excludedFiles:
  - backend/src/@app/iam/role/application/upsert/iam-upsert-role.handler.ts
```

For `additionalApis` refinement after regeneration, continue with
[`additional-apis-workflow.md`](additional-apis-workflow.md).
