Hive Elixir Review
This skill is intentionally narrow. Generic Elixir style, naming,
formatting, pipe chains, and lint hygiene are covered by mix format
and mix credo (including the custom checks under
credo/checks/). Do not flag anything credo already catches —
in particular: Repo.* calls inside Enum.* / Stream.* / for
(N+1 shape), or timestamps/1 without an explicit type:. Focus on the
rules below because they need diff awareness, semantic context, or
cross-file reasoning that credo cannot do.
For each finding, cite path:line (or Module.function/arity) and
quote the relevant snippet.
Only report findings whose cited snippet is present in the PR diff. If
the concern comes from unchanged context, do not emit a finding, do not
mention it as a note, and do not create a "findings outside this PR's
diff" section. If every possible concern is outside the diff, return no
findings.
Do not infer violations from nearby lines. A finding must be anchored on
a changed line in the diff.
1. Open-source secret hygiene
Hive is an open-source repository. Production secrets live in the
hive-k8s-production 1Password vault and are referenced from source only
by name.
Flag
- A real or plausible credential, token, OAuth secret, kubeconfig,
database URL, Docker config JSON, service account token,
.env
contents, or private key added to source, tests, fixtures, docs,
workflows, Helm values, or commit-oriented metadata. Severity:
critical.
- Test fixtures or example env values that look real instead of obvious
placeholders such as
"client-id" or "google-client-secret".
Severity: high.
- Production
ExternalSecret or Helm values that inline secret values
instead of referencing remote 1Password item names and fields.
Severity: critical.
Do not flag
- Existing low-stakes secret reference names such as
hive-google-oauth/credential, hive-secret-key-base/password, or the
vault name hive-k8s-production.
- Phoenix development
secret_key_base values in config/dev.exs or
test-only placeholder secrets.
2. Styling with Noora and data-part
Hive styles server-rendered Phoenix HTML with Noora. The CSS convention
is one anchor selector per layout, component, or route, with internal
regions selected via data-part.
Flag
- New CSS that introduces BEM-style child selectors such as
.headerbar__left, .layout__main, or .account-dropdown__menu.
Use nested [data-part="..."] selectors under the anchor instead.
Severity: medium.
- New route-specific CSS that is not scoped under the route root id, for
example
#login or a future route id. Severity: medium.
- New component or layout CSS that reaches for utility or atomic classes
instead of a component/layout anchor plus
data-part regions.
Severity: medium.
- A route template with a new root element that lacks a stable id while
adding route-specific CSS. Severity: medium.
- Hardcoded spacing, surface, font, radius, or z-index values where an
appropriate Noora variable exists. Severity: low.
Do not flag
- Plain Noora component usage that does not need extra CSS.
- Pixel dimensions for assets or visual effects where Noora has no
matching variable.
- The anchor class itself for reusable layouts or components, such as
.layout, .headerbar, or .account-dropdown.
3. Auth and session boundaries
HIVE_VISIBILITY=public|private controls whether the dashboard is
gated. Public instances allow everyone through, while private instances
require a signed-in session. Login remains available in both modes.
Flag
- A route that should be dashboard-protected but bypasses
HiveWeb.Plugs.RequireAuthenticated or its existing router pipeline.
Severity: high.
- A callback path that stores a user in the session before validating the
provider's allowed-domain rules via
Hive.Auth.check_domain/2.
Severity: high.
- Provider logic that bypasses
Hive.Auth and reads raw auth config
directly from controllers or templates. Severity: medium.
- OIDC provider code that hardcodes discovered endpoints instead of
relying on
ueberauth_oidcc issuer discovery. Severity: medium.
- Google auth changes that remove the server-side domain check and rely
only on the
hd authorize hint. Severity: high.
- Dynamic OAuth registration or another public unauthenticated endpoint
that trusts nested metadata to be the expected shape and can crash on
malformed user input instead of returning a controlled error or
normalizing to a safe default. Severity: medium.
- Dynamic OAuth registration changes that allow server-side URL fetching
from user-controlled metadata such as
jwks_uri, unless the diff also
adds explicit SSRF-safe validation and tests. Severity: high.
Do not flag
- Login being reachable when visibility is public. That is intentional
so admins can sign in to a public instance.
- Compile-time Ueberauth provider declarations in
config/config.exs.
Runtime credentials are populated separately in config/runtime.exs.
4. Test setup boundaries
Tests run async-by-default and stub through Mimic, not Application
config. The static parts of this rule are enforced by credo; what's left
here is the cross-file and semantic piece.
Flag
- A new mockable module used with Mimic (
stub/3, expect/3) that is
not added to test/test_helper.exs via Mimic.copy/1. The first
stubbing test will silently no-op. Severity: medium.
- Tests for a new route or controller action that are not mirrored under
the matching
test/hive_web/... path. Severity: low.
Do not flag
- Process-local Mimic stubs of modules already copied in
test/test_helper.exs.
- Database setup done through the existing async-safe ConnCase/DataCase
patterns.
5. Helm and production overlay boundaries
The Helm chart must remain generic by default. Tuist-specific production
values belong in infra/helm/hive/values-production.yaml.
Flag
- Generic chart templates or
values.yaml that hardcode
hive.tuist.dev, tuist.dev-specific auth domains, the
hive-k8s-production vault, Hetzner-specific storage classes, or
production-only External Secrets settings. Severity: high.
values-production.yaml changes that inline real secret values instead
of 1Password remote references. Severity: critical.
- Deployment workflow changes that print kubeconfig contents, secret
values, OAuth credentials, database URLs, or ExternalSecret payloads in
logs. Severity: critical.
- Helm changes that drop the production smoke test, rollback behavior, or
stuck-release recovery without replacing them with an equivalent safety
mechanism. Severity: medium.
Do not flag
- Tuist-specific values inside
infra/helm/hive/values-production.yaml
when they are references or non-secret deployment metadata.
- Generic chart support for External Secrets as long as values remain
configurable and disabled unless the deployer opts in.
6. Releases and commit scopes
Hive has two release tracks: app releases from non-helm Conventional
Commit scopes and Helm chart releases from helm-scoped commits.
Flag
- Release workflow changes that let Helm-only commits contribute to the
app changelog, or non-Helm commits contribute to the Helm changelog.
Severity: medium.
- Changes that remove the
helm@X.Y.Z tag prefix from release
detection or tagging. Severity: high. (App tags are plain
X.Y.Z; the legacy server@X.Y.Z form is still accepted by the
workflow's detection logic for backward compatibility.)
- PR titles or commit-message guidance that omits explicit Conventional
Commit scopes when documenting examples for this repo. Severity:
low.
Do not flag
- Existing
git-cliff behavior that accepts unscoped commits. The repo
convention still prefers explicit scopes, but release parsing tolerates
them.
7. LiveView lifecycle
The forage, dashboard, domain, and spec sections use Phoenix LiveView.
These rules catch lifecycle bugs that need semantic context (which
routes are tenant-scoped, what counts as "slow", which collections grow)
and so live outside credo.
Flag
- A new LiveView route on a tenant-scoped resource whose
mount/3 does
not gate access via the corresponding LetMe policy (e.g.
Hive.Forage.Policy). Severity: high.
- A
mount/3 performing a fetch that can exceed ~100ms (DB query,
agent run, external HTTP) without Phoenix.LiveView.assign_async/3.
Severity: medium.
- A new LiveView that assigns an unbounded collection
(e.g.
assign(socket, :specs, list)) instead of
Phoenix.LiveView.stream/3. Severity: medium.
- A
handle_event/3 performing a slow operation (DB write, agent run,
external HTTP) inline instead of offloading to a Task or background
process. Severity: medium.
- New domain logic (data access, multi-step flows, business rules)
placed inside a controller, LiveView
mount/3/handle_event/3, or
HEEx template instead of a context module under lib/hive/<domain>/.
The web layer should orchestrate; the domain should compute.
Severity: medium.
Do not flag
- Existing controller-rendered HEEx templates where the page does not
need LiveView interactivity.
- LiveViews that legitimately need synchronous data at mount (page
title, OpenGraph metadata) when the fetch is bounded and fast.
- Thin presentation helpers defined in the LiveView module itself
(formatting, derived assigns) that don't reach for the DB or external
systems.
8. Ecto changesets, tenancy, and templates
These rules require diff awareness or cross-file tracing that credo
cannot do.
Flag
- A new or materially changed
*_changeset/N function in lib/hive/**
whose diff does not also add or update a test calling that function in
the matching test/hive/**/*_test.exs. Material changes include any
new/modified cast, validate_*, unique_constraint,
foreign_key_constraint, or put_change line. Severity: medium.
- A changeset that
casts a programmatic foreign-key field (user_id,
account_id, organization_id, actor_id, or any FK naming the
actor/owner) from user params instead of setting it via put_change
from a verified actor. Severity: high.
- A controller, LiveView, or context function calling
Hive.Repo.get(Schema, id) / Repo.one(from s in Schema, where: s.id == ^id)
for a tenant-owned schema without a tenant constraint, when the call
site already has the tenant in scope. Severity: high.
- A HEEx template accessing
@record.assoc.field with no upstream
evidence (trace through the context function) that the association was
preloaded. The render-time crash on %Ecto.Association.NotLoaded{} is
silent at compile time. Severity: medium.
- New code that normalizes user-controlled params, JSON payloads, form
attrs, webhook attrs, or external API payloads by converting arbitrary
string keys to atoms, including
String.to_existing_atom/1. Use an
explicit key whitelist and ignore or reject unknown keys instead.
Severity: high.
- New in-memory request-facing counters, rate limiters, nonce stores, or
ETS tables that grow by client, token, IP, route, or time bucket
without a bounded retention or pruning path. Severity: medium.
- New or changed XML/HTML feed/rendering helpers that duplicate escaping
logic across formats instead of sharing one escaping helper, or that
interpolate user-controlled text into XML/HTML without escaping.
Severity: high.
Do not flag
- Trivial mechanical changeset edits (renaming a field already covered
by an existing test, formatting-only churn, reordering pipe steps).
- Diffs that exercise the changeset indirectly through a higher-level
context test that still passes.
- Closed-world atom conversion for trusted internal identifiers, such as
a route provider key that is immediately checked against configured
providers.
- Long-lived ETS tables whose key space is fixed and small, or stores
with an existing external TTL/cleanup owner.
9. Documentation and operator-facing surfaces
README.md describes behavior, configuration, and outcomes for the
operator or end user of any Hive deployment. Tuist-specific operational
details (production cluster, 1Password vault names, ESO config, Hetzner
storage classes, deployment workflow internals) belong in AGENTS.md,
not the README. Implementation details — module names, struct fields,
private helpers, refactoring artifacts — belong in code or AGENTS.md,
never in user-facing docs.
Flag
- A PR that adds, changes, or removes a feature visible to operators or
end users (new env var, new route, new ingestion source, changed
default, removed capability) without updating
README.md in the same
diff. Severity: medium.
- A
README.md change that introduces Tuist-specific operational
details (vault names, cluster names, ESO config, hcloud storage
classes, production-only workflow notes) instead of putting them in
AGENTS.md. Severity: low.
- A
README.md change that leaks implementation details (module names,
function signatures, private struct fields, internal refactoring
notes) instead of describing observable behavior. Severity: low.
- A doc cross-reference to something that no longer exists or now works
differently after the diff (stale env var, removed route, renamed
flag). Severity: medium.
Do not flag
- Internal-only changes (refactors, perf, ops-only paths, tests,
fixtures) that have no operator- or user-visible effect.
- Existing implementation references in
AGENTS.md — that file is
explicitly the place for them.
- README additions for genuinely user-facing operational concerns
(allowed-domain configuration, visibility modes) even if Tuist also
uses them.
Before submitting findings
For each finding, confirm:
- The
path:line is real and the snippet appears in the diff.
- The category above is one of 1–9; if it isn't, downgrade to a
question (
uncertain: ...) rather than asserting a finding.
- The severity is set: critical (auth bypass / cross-tenant read or
write / secret leak), high (likely security or correctness bug),
medium (compliance / consistency gap), low (nice-to-have).
- You are not reporting an unchanged line as a finding. Unchanged
context can explain a diff finding, but cannot be the finding itself.
Out of scope (handled elsewhere — do not flag)
- Module/function naming, pipe-chain start, nesting depth, parentheses
on no-arg calls →
mix format and built-in credo checks.
- Migration
timestamps(...) or schema timestamps(...) without
explicit type: → Hive.Credo.Checks.TimestampsType.
Repo.* calls inside Enum.* / Stream.* / for (N+1 shape) →
Hive.Credo.Checks.RepoCallInEnum.
- Missing
@spec / @type — this codebase intentionally avoids
typespecs. Never suggest adding them.
- Missing
@doc / @moduledoc on internal helper modules.
- Obvious
String.to_atom/1 on user input → credo's UnsafeToAtom.
Keep review-time findings for semantic cases such as
String.to_existing_atom/1 hidden inside external payload
normalization, where a whitelist is expected.
Skill maintenance (not a review-time instruction)
A rule belongs in this skill only when it needs at least one of:
- The PR diff ("changed in this PR", "co-changed with X").
- Semantic context that depends on the project ("this schema is
tenant-owned", "this route is public", "this is a request path").
- Cross-file or cross-module reasoning ("was this association
preloaded upstream", "is the mock registered in
test_helper.exs").
- Human-shaped pattern recognition ("this string looks like a real
credential", "this leaks implementation details").
- External-system awareness that the code itself doesn't expose
(Helm overlays, 1Password references, deployment workflow).
A rule that reduces to "match this AST shape in files under this path"
belongs in credo/checks/, not here. When the temptation is to add a
new section to this skill, first ask: could a Credo check do it? If
yes, write the check instead and leave only the semantic escalation
(severity by context, hot-path judgment) for the skill.
1---2name: hive-elixir-review3description: Project-specific PR-review rules for the tuist/hive Phoenix/Elixir codebase. Focuses on open-source secret hygiene, Noora data-part styling, OIDC auth, Helm deployment boundaries, release conventions, and a small set of Phoenix/Ecto/LiveView review rules that require diff awareness, semantic context, or cross-file reasoning that `mix format`/`mix credo` cannot do.4---56# Hive Elixir Review78This skill is intentionally narrow. Generic Elixir style, naming,9formatting, pipe chains, and lint hygiene are covered by `mix format`10and `mix credo` (including the custom checks under11`credo/checks/`). **Do not flag anything credo already catches** —12in particular: `Repo.*` calls inside `Enum.*` / `Stream.*` / `for`13(N+1 shape), or `timestamps/1` without an explicit `type:`. Focus on the14rules below because they need diff awareness, semantic context, or15cross-file reasoning that credo cannot do.1617For each finding, cite `path:line` (or `Module.function/arity`) and18quote the relevant snippet.1920Only report findings whose cited snippet is present in the PR diff. If21the concern comes from unchanged context, do not emit a finding, do not22mention it as a note, and do not create a "findings outside this PR's23diff" section. If every possible concern is outside the diff, return no24findings.2526Do not infer violations from nearby lines. A finding must be anchored on27a changed line in the diff.2829---3031## 1. Open-source secret hygiene3233Hive is an open-source repository. Production secrets live in the34`hive-k8s-production` 1Password vault and are referenced from source only35by name.3637### Flag3839- A real or plausible credential, token, OAuth secret, kubeconfig,40 database URL, Docker config JSON, service account token, `.env`41 contents, or private key added to source, tests, fixtures, docs,42 workflows, Helm values, or commit-oriented metadata. **Severity:43 critical.**44- Test fixtures or example env values that look real instead of obvious45 placeholders such as `"client-id"` or `"google-client-secret"`.46 **Severity: high.**47- Production `ExternalSecret` or Helm values that inline secret values48 instead of referencing remote 1Password item names and fields.49 **Severity: critical.**5051### Do not flag5253- Existing low-stakes secret reference names such as54 `hive-google-oauth/credential`, `hive-secret-key-base/password`, or the55 vault name `hive-k8s-production`.56- Phoenix development `secret_key_base` values in `config/dev.exs` or57 test-only placeholder secrets.5859---6061## 2. Styling with Noora and `data-part`6263Hive styles server-rendered Phoenix HTML with Noora. The CSS convention64is one anchor selector per layout, component, or route, with internal65regions selected via `data-part`.6667### Flag6869- New CSS that introduces BEM-style child selectors such as70 `.headerbar__left`, `.layout__main`, or `.account-dropdown__menu`.71 Use nested `[data-part="..."]` selectors under the anchor instead.72 **Severity: medium.**73- New route-specific CSS that is not scoped under the route root id, for74 example `#login` or a future route id. **Severity: medium.**75- New component or layout CSS that reaches for utility or atomic classes76 instead of a component/layout anchor plus `data-part` regions.77 **Severity: medium.**78- A route template with a new root element that lacks a stable id while79 adding route-specific CSS. **Severity: medium.**80- Hardcoded spacing, surface, font, radius, or z-index values where an81 appropriate Noora variable exists. **Severity: low.**8283### Do not flag8485- Plain Noora component usage that does not need extra CSS.86- Pixel dimensions for assets or visual effects where Noora has no87 matching variable.88- The anchor class itself for reusable layouts or components, such as89 `.layout`, `.headerbar`, or `.account-dropdown`.9091---9293## 3. Auth and session boundaries9495`HIVE_VISIBILITY=public|private` controls whether the dashboard is96gated. Public instances allow everyone through, while private instances97require a signed-in session. Login remains available in both modes.9899### Flag100101- A route that should be dashboard-protected but bypasses102 `HiveWeb.Plugs.RequireAuthenticated` or its existing router pipeline.103 **Severity: high.**104- A callback path that stores a user in the session before validating the105 provider's allowed-domain rules via `Hive.Auth.check_domain/2`.106 **Severity: high.**107- Provider logic that bypasses `Hive.Auth` and reads raw auth config108 directly from controllers or templates. **Severity: medium.**109- OIDC provider code that hardcodes discovered endpoints instead of110 relying on `ueberauth_oidcc` issuer discovery. **Severity: medium.**111- Google auth changes that remove the server-side domain check and rely112 only on the `hd` authorize hint. **Severity: high.**113- Dynamic OAuth registration or another public unauthenticated endpoint114 that trusts nested metadata to be the expected shape and can crash on115 malformed user input instead of returning a controlled error or116 normalizing to a safe default. **Severity: medium.**117- Dynamic OAuth registration changes that allow server-side URL fetching118 from user-controlled metadata such as `jwks_uri`, unless the diff also119 adds explicit SSRF-safe validation and tests. **Severity: high.**120121### Do not flag122123- Login being reachable when visibility is public. That is intentional124 so admins can sign in to a public instance.125- Compile-time Ueberauth provider declarations in `config/config.exs`.126 Runtime credentials are populated separately in `config/runtime.exs`.127128---129130## 4. Test setup boundaries131132Tests run async-by-default and stub through Mimic, not `Application`133config. The static parts of this rule are enforced by credo; what's left134here is the cross-file and semantic piece.135136### Flag137138- A new mockable module used with Mimic (`stub/3`, `expect/3`) that is139 not added to `test/test_helper.exs` via `Mimic.copy/1`. The first140 stubbing test will silently no-op. **Severity: medium.**141- Tests for a new route or controller action that are not mirrored under142 the matching `test/hive_web/...` path. **Severity: low.**143144### Do not flag145146- Process-local Mimic stubs of modules already copied in147 `test/test_helper.exs`.148- Database setup done through the existing async-safe ConnCase/DataCase149 patterns.150151---152153## 5. Helm and production overlay boundaries154155The Helm chart must remain generic by default. Tuist-specific production156values belong in `infra/helm/hive/values-production.yaml`.157158### Flag159160- Generic chart templates or `values.yaml` that hardcode161 `hive.tuist.dev`, `tuist.dev`-specific auth domains, the162 `hive-k8s-production` vault, Hetzner-specific storage classes, or163 production-only External Secrets settings. **Severity: high.**164- `values-production.yaml` changes that inline real secret values instead165 of 1Password remote references. **Severity: critical.**166- Deployment workflow changes that print kubeconfig contents, secret167 values, OAuth credentials, database URLs, or ExternalSecret payloads in168 logs. **Severity: critical.**169- Helm changes that drop the production smoke test, rollback behavior, or170 stuck-release recovery without replacing them with an equivalent safety171 mechanism. **Severity: medium.**172173### Do not flag174175- Tuist-specific values inside `infra/helm/hive/values-production.yaml`176 when they are references or non-secret deployment metadata.177- Generic chart support for External Secrets as long as values remain178 configurable and disabled unless the deployer opts in.179180---181182## 6. Releases and commit scopes183184Hive has two release tracks: app releases from non-`helm` Conventional185Commit scopes and Helm chart releases from `helm`-scoped commits.186187### Flag188189- Release workflow changes that let Helm-only commits contribute to the190 app changelog, or non-Helm commits contribute to the Helm changelog.191 **Severity: medium.**192- Changes that remove the `helm@X.Y.Z` tag prefix from release193 detection or tagging. **Severity: high.** (App tags are plain194 `X.Y.Z`; the legacy `server@X.Y.Z` form is still accepted by the195 workflow's detection logic for backward compatibility.)196- PR titles or commit-message guidance that omits explicit Conventional197 Commit scopes when documenting examples for this repo. **Severity:198 low.**199200### Do not flag201202- Existing `git-cliff` behavior that accepts unscoped commits. The repo203 convention still prefers explicit scopes, but release parsing tolerates204 them.205206---207208## 7. LiveView lifecycle209210The forage, dashboard, domain, and spec sections use Phoenix LiveView.211These rules catch lifecycle bugs that need semantic context (which212routes are tenant-scoped, what counts as "slow", which collections grow)213and so live outside credo.214215### Flag216217- A new LiveView route on a tenant-scoped resource whose `mount/3` does218 not gate access via the corresponding LetMe policy (e.g.219 `Hive.Forage.Policy`). **Severity: high.**220- A `mount/3` performing a fetch that can exceed ~100ms (DB query,221 agent run, external HTTP) without `Phoenix.LiveView.assign_async/3`.222 **Severity: medium.**223- A new LiveView that assigns an unbounded collection224 (e.g. `assign(socket, :specs, list)`) instead of225 `Phoenix.LiveView.stream/3`. **Severity: medium.**226- A `handle_event/3` performing a slow operation (DB write, agent run,227 external HTTP) inline instead of offloading to a Task or background228 process. **Severity: medium.**229- New domain logic (data access, multi-step flows, business rules)230 placed inside a controller, LiveView `mount/3`/`handle_event/3`, or231 HEEx template instead of a context module under `lib/hive/<domain>/`.232 The web layer should orchestrate; the domain should compute.233 **Severity: medium.**234235### Do not flag236237- Existing controller-rendered HEEx templates where the page does not238 need LiveView interactivity.239- LiveViews that legitimately need synchronous data at mount (page240 title, OpenGraph metadata) when the fetch is bounded and fast.241- Thin presentation helpers defined in the LiveView module itself242 (formatting, derived assigns) that don't reach for the DB or external243 systems.244245---246247## 8. Ecto changesets, tenancy, and templates248249These rules require diff awareness or cross-file tracing that credo250cannot do.251252### Flag253254- A new or materially changed `*_changeset/N` function in `lib/hive/**`255 whose diff does not also add or update a test calling that function in256 the matching `test/hive/**/*_test.exs`. Material changes include any257 new/modified `cast`, `validate_*`, `unique_constraint`,258 `foreign_key_constraint`, or `put_change` line. **Severity: medium.**259- A changeset that `cast`s a programmatic foreign-key field (`user_id`,260 `account_id`, `organization_id`, `actor_id`, or any FK naming the261 actor/owner) from user params instead of setting it via `put_change`262 from a verified actor. **Severity: high.**263- A controller, LiveView, or context function calling264 `Hive.Repo.get(Schema, id)` / `Repo.one(from s in Schema, where: s.id == ^id)`265 for a tenant-owned schema without a tenant constraint, when the call266 site already has the tenant in scope. **Severity: high.**267- A HEEx template accessing `@record.assoc.field` with no upstream268 evidence (trace through the context function) that the association was269 preloaded. The render-time crash on `%Ecto.Association.NotLoaded{}` is270 silent at compile time. **Severity: medium.**271- New code that normalizes user-controlled params, JSON payloads, form272 attrs, webhook attrs, or external API payloads by converting arbitrary273 string keys to atoms, including `String.to_existing_atom/1`. Use an274 explicit key whitelist and ignore or reject unknown keys instead.275 **Severity: high.**276- New in-memory request-facing counters, rate limiters, nonce stores, or277 ETS tables that grow by client, token, IP, route, or time bucket278 without a bounded retention or pruning path. **Severity: medium.**279- New or changed XML/HTML feed/rendering helpers that duplicate escaping280 logic across formats instead of sharing one escaping helper, or that281 interpolate user-controlled text into XML/HTML without escaping.282 **Severity: high.**283284### Do not flag285286- Trivial mechanical changeset edits (renaming a field already covered287 by an existing test, formatting-only churn, reordering pipe steps).288- Diffs that exercise the changeset indirectly through a higher-level289 context test that still passes.290- Closed-world atom conversion for trusted internal identifiers, such as291 a route provider key that is immediately checked against configured292 providers.293- Long-lived ETS tables whose key space is fixed and small, or stores294 with an existing external TTL/cleanup owner.295296---297298## 9. Documentation and operator-facing surfaces299300`README.md` describes behavior, configuration, and outcomes for the301operator or end user of any Hive deployment. Tuist-specific operational302details (production cluster, 1Password vault names, ESO config, Hetzner303storage classes, deployment workflow internals) belong in `AGENTS.md`,304not the README. Implementation details — module names, struct fields,305private helpers, refactoring artifacts — belong in code or `AGENTS.md`,306never in user-facing docs.307308### Flag309310- A PR that adds, changes, or removes a feature visible to operators or311 end users (new env var, new route, new ingestion source, changed312 default, removed capability) without updating `README.md` in the same313 diff. **Severity: medium.**314- A `README.md` change that introduces Tuist-specific operational315 details (vault names, cluster names, ESO config, hcloud storage316 classes, production-only workflow notes) instead of putting them in317 `AGENTS.md`. **Severity: low.**318- A `README.md` change that leaks implementation details (module names,319 function signatures, private struct fields, internal refactoring320 notes) instead of describing observable behavior. **Severity: low.**321- A doc cross-reference to something that no longer exists or now works322 differently after the diff (stale env var, removed route, renamed323 flag). **Severity: medium.**324325### Do not flag326327- Internal-only changes (refactors, perf, ops-only paths, tests,328 fixtures) that have no operator- or user-visible effect.329- Existing implementation references in `AGENTS.md` — that file is330 explicitly the place for them.331- README additions for genuinely user-facing operational concerns332 (allowed-domain configuration, visibility modes) even if Tuist also333 uses them.334335---336337## Before submitting findings338339For each finding, confirm:3403411. The `path:line` is real and the snippet appears in the diff.3422. The category above is one of 1–9; if it isn't, downgrade to a343 question (`uncertain: ...`) rather than asserting a finding.3443. The severity is set: **critical** (auth bypass / cross-tenant read or345 write / secret leak), **high** (likely security or correctness bug),346 **medium** (compliance / consistency gap), **low** (nice-to-have).3474. You are not reporting an unchanged line as a finding. Unchanged348 context can explain a diff finding, but cannot be the finding itself.349350---351352## Out of scope (handled elsewhere — do not flag)353354- Module/function naming, pipe-chain start, nesting depth, parentheses355 on no-arg calls → `mix format` and built-in credo checks.356- Migration `timestamps(...)` or schema `timestamps(...)` without357 explicit `type:` → `Hive.Credo.Checks.TimestampsType`.358- `Repo.*` calls inside `Enum.*` / `Stream.*` / `for` (N+1 shape) →359 `Hive.Credo.Checks.RepoCallInEnum`.360- Missing `@spec` / `@type` — this codebase intentionally avoids361 typespecs. Never suggest adding them.362- Missing `@doc` / `@moduledoc` on internal helper modules.363- Obvious `String.to_atom/1` on user input → credo's `UnsafeToAtom`.364 Keep review-time findings for semantic cases such as365 `String.to_existing_atom/1` hidden inside external payload366 normalization, where a whitelist is expected.367368---369370## Skill maintenance (not a review-time instruction)371372A rule belongs in this skill **only** when it needs at least one of:373374- **The PR diff** ("changed in this PR", "co-changed with X").375- **Semantic context** that depends on the project ("this schema is376 tenant-owned", "this route is public", "this is a request path").377- **Cross-file or cross-module reasoning** ("was this association378 preloaded upstream", "is the mock registered in `test_helper.exs`").379- **Human-shaped pattern recognition** ("this string looks like a real380 credential", "this leaks implementation details").381- **External-system awareness** that the code itself doesn't expose382 (Helm overlays, 1Password references, deployment workflow).383384A rule that reduces to "match this AST shape in files under this path"385belongs in `credo/checks/`, not here. When the temptation is to add a386new section to this skill, first ask: could a Credo check do it? If387yes, write the check instead and leave only the semantic escalation388(severity by context, hot-path judgment) for the skill.