# examples

## Context

Real Grit applications, generated by the CLI and committed. They are the only
check in this repository that the framework produces something that works,
rather than something that compiles: a scaffold test asserts a template contains
a string, and an example proves an app builds, type-checks and runs.

Two kinds live here.

- **`library`, `commerce` and `saas`** are applications, **gated by CI**.
  `library` is a generated project; `commerce` is a shop with a hand-written cart
  on top, plus a `TUTORIAL.md` that reproduces it from scratch; `saas` is a
  subscription product whose customer portal and operator console share one users
  table, and whose tests prove a customer cannot read another customer's rows.
  Seven framework faults were found by building `commerce` and one more by
  building `saas`, which is the entire argument for having them.
- **`job-portal-*`** are one project per architecture, so a change to a shape can
  be diffed rather than described.

## Source of truth

- **What CI builds**: `.github/workflows/ci.yml`. `examples/library/apps/api`,
  `examples/commerce` and `examples/saas` have their own steps. `saas` has a
  second step running its isolation tests alone, so a failure names the breach
  rather than being one line in a wall of output.
- **`commerce/TUTORIAL.md`**: generated by `scripts/tutorial-build.py` from the
  checked-in app. **Never hand-edit its code blocks**; edit the app, re-run the
  script.
- **Which architecture each `job-portal-*` is**: its own `grit.json`.

## Boundary rules

1. **These are generated output, not source.** Do not hand-fix an example to make
   CI pass. If an example is wrong, the **template** is wrong: fix
   `internal/scaffold` or `internal/generate`, regenerate, and commit the result.
   A hand-patched example hides the bug it was there to find.

2. **The hand-written part of `commerce` is the exception**, and only the cart
   and the pages the tutorial describes. Everything else in it is regenerated.

3. **Regenerate with a rebuilt binary.** An example regenerated by a stale
   `grit.exe` commits the previous release's output and reports it as current.

4. **Compare by bytes.** Same project name, both binaries, read as bytes.
   Different names and decoded text each give a false answer about what changed.

5. **Stage new files explicitly.** A regeneration that adds a file in a tracked
   directory is exactly the case where the new file gets left out of the commit
   and breaks CI. Verify with `git diff --cached --stat`.

6. **Do not add an example casually.** Each one is a build in CI and a diff to
   maintain forever. Eight is already a lot.

## Validation workflow

A change that touches an example has to pass what CI runs, locally, first:

```bash
cd examples/library/apps/api && go build ./... && go test ./...
cd examples/commerce/apps/api && go build ./...
cd examples/commerce && pnpm install && pnpm typecheck
```

And if you regenerated:

1. Diff the regenerated tree against the committed one and **read** the diff. An
   unexplained change is a template change you did not intend.
2. Stand the app up and open it. `commerce` has a cart; put something in it.
3. Re-run `python scripts/tutorial-build.py` if `commerce` changed, and check
   the tutorial still describes the app that is there.
