diff --git a/docs/available-tasks.md b/docs/available-tasks.md
index 09c29794..031778d3 100644
--- a/docs/available-tasks.md
+++ b/docs/available-tasks.md
@@ -126,6 +126,6 @@ Run HTTP tests against production
## `test`
-- Depends: dev:partitioned-test 1, dev:partitioned-test 2
+- Depends: dev:partitioned-test 1, dev:partitioned-test 2, dev:partitioned-test 3, dev:partitioned-test 4
- **Usage**: `test`
diff --git a/docs/project-conventions.md b/docs/project-conventions.md
index 5fb3ab9a..b995bfd1 100644
--- a/docs/project-conventions.md
+++ b/docs/project-conventions.md
@@ -20,15 +20,15 @@ Key rules: imperative present tense, single-line under 60 characters, task ID pr
The pre-commit hook (`scripts/dev/precommit`) inspects staged files and runs only the checks relevant to the change, rather than always running the full verification suite. CI always runs the full suite unconditionally.
-| Category | File pattern | Checks (conditional) |
-| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| **elixir** | `lib/`, `test/`, `config/`, `mix.exs`, `mix.lock`, `priv/repo/migrations/`, `priv/gettext/`, `.credo.exs`, `.formatter.exs`, `.sobelow-conf` | `mix format --check-formatted`
`mix credo --strict`
`mix sobelow --compact --exit`
`mix gettext.extract --check-up-to-date`
`mix test`
`mix deps.unlock --unused` † |
-| **shell** | `scripts/`, `.shellcheckrc` | `shellcheck` on all `scripts/` files (excluding `.hurl`) |
-| **assets** | `assets/`, `.pi/extensions/.*\.(ts\|js\|json)$` | `prettier` on CSS, JS, and TS/JSON in `.pi/extensions/` (excluding `node_modules`) |
-| **docs** | `docs/`, `README.md`, `AGENTS.md` | `prettier` on Markdown and Livebook files |
-| **backlog** | `backlog/` | `prettier` on all backlog markdown files |
-| **presto** | `presto/` | `(cd presto && mise run test)` — runs pytest |
-| **docker** | `Dockerfile`, `.dockerignore`, `compose.yaml` | `mise run dev:validate-docker-image` (only if `Dockerfile` is staged) |
+| Category | File pattern | Checks (conditional) |
+| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **elixir** | `lib/`, `test/`, `config/`, `mix.exs`, `mix.lock`, `priv/repo/migrations/`, `priv/gettext/`, `.credo.exs`, `.formatter.exs`, `.sobelow-conf` | `mix format --check-formatted`
`mix credo --strict`
`mix sobelow --compact --exit`
`mix gettext.extract --check-up-to-date`
`mise run //:test`
`mix deps.unlock --unused` † |
+| **shell** | `scripts/`, `.shellcheckrc` | `shellcheck` on all `scripts/` files (excluding `.hurl`) |
+| **assets** | `assets/`, `.pi/extensions/.*\.(ts\|js\|json)$` | `prettier` on CSS, JS, and TS/JSON in `.pi/extensions/` (excluding `node_modules`) |
+| **docs** | `docs/`, `README.md`, `AGENTS.md` | `prettier` on Markdown and Livebook files |
+| **backlog** | `backlog/` | `prettier` on all backlog markdown files |
+| **presto** | `presto/` | `(cd presto && mise run test)` — runs pytest |
+| **docker** | `Dockerfile`, `.dockerignore`, `compose.yaml` | `mise run dev:validate-docker-image` (only if `Dockerfile` is staged) |
> **† `mix deps.unlock --unused` sub-gate**: Runs only when `mix.exs` or `mix.lock` is in the staged files — not on every Elixir change. An unused dependency can only be introduced by changing the dependency specification, not by changing application code.
@@ -102,7 +102,7 @@ Key rules: feature setup stays in test modules that need it, fixture modules use
- **Markdown sanitization via MDEx (ammonia).** Use `Markdown.to_html/1` for user content. Annotate raw output with `# sobelow_skip ["XSS.Raw"]` and a comment explaining the sanitization.
- **Sobelow runs on CI and pre-commit** in skip mode for security analysis.
- **`mix deps.audit` runs on CI and pre-commit** (warn-only) via `mix_audit` for CVE scanning.
-- **ExSlop checks run via Credo** for code quality: no narrator/boilerplate docs (use `@moduledoc false` instead), no obvious/step/narrator comments, no identity `case`/`with` patterns, no `Repo.all` then filter, no `Enum.map` with inline queries, no unaliased nested module use (nested modules must be aliased at the top). Violations are caught by CI.
+- **ExSlop and custom Credo checks run via Credo** for code quality: no narrator/boilerplate docs (use `@moduledoc false` instead), no obvious/step/narrator comments, no identity `case`/`with` patterns, no `Repo.all` then filter, no `Enum.map` with inline queries, no unaliased nested module use (nested modules must be aliased at the top), no LiveComponent toast helper misuse (`put_toast!/2` in LiveComponents, `put_toast/3` in LiveViews). Violations are caught by CI.
- **All modules require `@moduledoc`.** The Credo `ModuleDoc` check is enforced in strict mode.
- **Dialyzer is enabled via `dialyxir` (dev/test only) with the `:no_opaque` flag.** Specs are required and checked. Opaque type checking is disabled to tolerate third-party opaque types (e.g., `Ecto.Changeset`).
- **Validate Docker builder image before updating versions.** When changing `ELIXIR_VERSION`, `OTP_VERSION`, or `DEBIAN_VERSION` in the Dockerfile, run `mise run dev:validate-docker-image` to confirm the generated `hexpm/elixir` tag exists on Docker Hub and supports both `linux/amd64` and `linux/arm64`.