---
name: quality-gate
description: >-
  Runs the checks of a ddd-ts-kit project at the right scope (the changed files while
  iterating, the whole gate before a slice is closed or merged) and fixes what fails at the
  root cause. Use before closing a slice or opening a pull or merge request, when the user
  asks whether the work is clean, done or ready, or when a check, the guard or the pipeline
  reports a violation. For the size and message of the commit use commit-discipline.
allowed-tools:
  - Read
  - Edit
  - Bash(npm run verify)
  - Bash(npm run typecheck)
  - Bash(npm run lint *)
  - Bash(npm test *)
  - Bash(npm run test:coverage)
  - Bash(npm run test:e2e *)
  - Bash(npm run docs:check)
  - Bash(npm run regenerate)
  - Bash(npm run docs:generate)
  - Bash(npm run depcruise)
  - Bash(npm run deadcode)
  - Bash(npx ddd-ts-kit check *)
  - Bash(npx ddd-ts-kit verify-slice *)
  - Bash(npx ddd-ts-kit verify-merge)
  - Bash(npx ddd-ts-kit verify-merge *)
  - Bash(npx ddd-ts-kit merge-trunk)
  - Bash(npx ddd-ts-kit merge-trunk *)
  - Bash(npx ddd-ts-kit check)
  - Bash(npx ddd-ts-kit todo *)
  - Bash(npx ddd-ts-kit todo)
  - Bash(npx ddd-ts-kit status)
  - Bash(npx ddd-ts-kit story-check *)
  - Bash(npx ddd-ts-kit docs)
  - Bash(npx ddd-ts-kit explain *)
---

# Quality gate

## Scope the run

| When | Run | Cost |
|---|---|---|
| after each write | nothing: the guard runs `check-file` and returns the violations | none |
| while iterating | `npx ddd-ts-kit verify-slice <context> --since <base>`: regenerate, the rules on the changed files, the specs of the context, the files near the size limit and the paths of the slice to stage, in one run; a path it leaves out is never staged with the slice | seconds |
| before the commit that closes a slice | the two above, `npm run typecheck`, `npm run lint`, `npm run format:check` (`npm run format` settles it), `npx ddd-ts-kit todo <context>` (nothing left but `TODO(review)`), `npx ddd-ts-kit check --commit-size` | under a minute |
| to bring the trunk into the branch | `npx ddd-ts-kit merge-trunk`: one merge commit from the branch's own diff, derived files settled, `verify-merge` on the result before its commit; on a stop, resolve each hunk, `git add`, `merge-trunk --continue` | seconds |
| after a merge made otherwise | `npx ddd-ts-kit verify-merge`: each line, row done or test the merge lost, to restore from its side; it exits 1 on one (the pre-push hook and `verify-slice` run it too, and only warn in this version) | a second |
| before a merge or a pull request | `npm run verify`, on the branch with the trunk merged in; each non-blocking remark of the review written as a `TODO(review)` or settled | everything |
| before a story or a milestone is closed | `npx ddd-ts-kit story-check <code>` (`M1` for the rows `M1.*`): every row `done`, no marker of its own left; exits 1 with each blocker, why it is the story's, its `file:line` and its fix | seconds |

Layout is settled in this order: `npm run format`, then `npm run lint:fix`. Biome wraps a long
line, and the lint then asks for a blank line around the statement that now spans several lines:
that new finding is expected, never a conflict between the two. Never Prettier nor a bare
`npx biome` (without `--no`, it can fetch an unrelated package of that name); the guard refuses
both to a worker.

`check --changed-since` judges the changed files only: a cycle, an unused export or a rule
broken by the combination with the trunk escapes it, which is why the last run is `verify`, on
the whole tree, after merging the trunk. `npm run verify` chains types, lint, format, every spec with the coverage thresholds
(`npm run test:coverage`), end-to-end tests, the architecture rules, the import graph
(dependency-cruiser), dead code (knip) and documentation drift. Use
`npm run typecheck`, never `npx tsc`: the project compiles with the native compiler.
`npx ddd-ts-kit status` counts the `TODO(domain)`, `TODO(platform)`, `it.todo` and
`TODO(review)` left in each context: a gate is not passed while the slice still has one of the
first three.

## Review remarks

A review sorts its findings: a blocking one is fixed before the merge; a non-blocking one is
settled at once, or written before the merge as `TODO(review): #<row id> <remark>` at the line it is
about, by whoever fixes the branch (the `architecture-reviewer` agent gives the line and its
`file:line`, and never writes it). A slice may merge with `TODO(review)` markers; a story or a
milestone is closed only when `npx ddd-ts-kit story-check <code>` exits 0: every marker of its
own handled, or dismissed by a decision record (`npx ddd-ts-kit generate adr "<title>"`,
quoting the remark without its marker) with the marker deleted in the same commit. A marker is
the story's when its text names one of its rows (the `#<row id>` above), else when the commit that
wrote its line does (its `Slice: <row id>` trailer, else its message), else, not committed, when
the worker whose worktree holds it was briefed on one of its rows. One that belongs to no row
blocks only when no other open story has a row in its context; otherwise `story-check` lists it
without blocking: settle it, or name its row in it. `--strict` blocks on every marker of the
story's contexts.

## Coverage

`npm run test:coverage` fails with `Coverage for <rate> (<measured>%) does not meet
"<contexts>/<context>/<layer>/**/*.ts" threshold (<floor>%)`: that layer of that context holds
code no spec runs. The thresholds are the `coverage` key of `ddd-ts-kit.config.json` (the
reference when absent: domain 90/85/90/90, application 85/80/85/85, presentation 80/70/80/80,
infrastructure 70/60/70/70, as statements/branches/functions/lines).

- Read the uncovered lines in the report, write the spec of the behaviour that reaches them
  (`/ddd-ts-kit:testing`), or delete the code when nothing can reach it.
- Never add `exclude`, `include` or `thresholds` to the runner config, a `--coverage.*` flag to a
  script, or a `v8 ignore` / `istanbul ignore` comment: `test/coverage-governed` reports them.
- Lowering a threshold is a decision of the team: change the rate in the config and record it
  with `npx ddd-ts-kit generate adr "<title>" --deviation coverage.<layer>.<rate> --reason "<why>"`.
  Raising one is free.

## Read a violation

Each carries a rule id, a `file:line`, a message and a `fix` sentence. Open that file at that
line only. `npx ddd-ts-kit explain <rule-id>` gives the reason and a good and a bad example.

| Rule family | Usual root cause | Fix |
|---|---|---|
| `layer/*` | logic or a type in the wrong layer | move it, or a port in `application/ports/` and an adapter |
| `context/*` | a context reaching into another | a contract in the upstream's `published-language/`, an integration event, a declared relationship (skill `ddd-ts-kit:context-map`) |
| `tree/*`, `naming/*` | a file made by hand | `ddd-ts-kit generate`, then move the logic |
| `language/*` | a name outside the glossary | the business word, or the term added first (skill `ddd-ts-kit:glossary`) |
| `ports/*`, `declarations/*`, `composition/*` | wiring by hand | skill `ddd-ts-kit:wiring`; a stale barrel is `npx ddd-ts-kit docs` |
| `domain-events/*` | a handler or an adapter dispatching events itself; a `before-commit` handler depending on a gateway, an external port, `Cache`, `Lock`, `RateLimiter`, `JobScheduler` or `EventBus` | `await repository.save(aggregate)` and nothing else: the unit of work dispatches at its end; `phase: 'after-commit'` when the effect may be lost, an integration event when it must not |
| `authz/*` | a use case without a permission, a right decided in the wrong layer | skill `ddd-ts-kit:access-control` |
| `events/*`, `scheduling/*` | a contract outside the published language, a consumer without a name, a job doing work | skill `ddd-ts-kit:integration-event`; a job only sends a command |
| `purity/*`, `clean/*` | a hidden dependency, unclear code | skill `ddd-ts-kit:clean-code` |
| `aspects/*`, `tracing/*`, `standard/*` | a technical concern inside business code | skill `ddd-ts-kit:cross-cutting` |
| `governance/*`, `config/*` | a suppression or a config entry without its reason or record | skill `ddd-ts-kit:deviation` |
| `test/*` | behaviour without a test, or a mock | skill `ddd-ts-kit:testing` |
| documentation drift | generated files not regenerated | `npm run regenerate`, commit them with the code |
| `event-sourcing/*`, `unrecorded ... in stored-events.lock.json` from `docs:check` | a state change outside an apply method, a stored shape changed in place, a stored version not recorded | skill `ddd-ts-kit:persistence`; a changed stored shape is a new version with its upcaster, and an unrecorded one is recorded by `npx ddd-ts-kit docs --stored-events` once final, never by hand |

Five ids are held by ESLint (`domain/*`, `application/*`): `npm run lint` reports them.

## Not without the owner's explicit agreement

A suppression, a lowered severity, a wider allow-list, an extra directory, a relaxed
pipeline, `any`, a non-null assertion, a skipped or deleted test: skill
`ddd-ts-kit:deviation`. Getting to green is never the reason.

## Report

The commands run and their real result, in a few lines. A failing gate is reported as
failing, with what remains and why.
