effx
Specifications

Spec 0025 — Organization and Mailing-list transport migration, Gate 4

Status: operator-approved for local implementation (2026-10-04); acceptance not yet claimed. The baseline is clean but unpublished mono-web 23bc0d6b (unpublished) with five accepted generated groups; mw/ means mono-web repository-relative source and unprefixed paths mean effx. The operator-owned mono-web main is read-only. Spec commit 1b824e4 was fast-forwarded onto effx main before implementation. Approval authorizes only this local slice, not landing mono-web, deployment, production data, provider or credential changes (docs/specs/0004-monoweb-migration-plan.md:235-327; mw/AGENTS.md#verification-and-resources).

Outcome and fixed boundary

The authorized person can see current appointment and delegation management, create an Organization directory entity, and issue a confined lifecycle/delegation command; the public can continue reading the three directories conditionally, and an authorized staff member can list team interest and Mailing recipients. Replace the thirteen existing external organization.* HTTP operations, as one group, with effx-generated native contract and raw backend binding from the same project IR that already emits Profile, Directory, SocialEvents, Content and TeamApplications. Twelve operations belong to Organization; organization.listMailingLists is Mailing-owned business behavior in the existing Organization HTTP group. Do not split it into a new HTTP group, silently rename its SDK operation, or make a fourteenth operation. The accepted root still imports the handwritten OrganizationApi; its generated SDK index has exactly these thirteen IDs (mw/packages/http-api/src/organization.ts:574-596; mw/packages/http-api/src/api.ts:12-20,34-50; mw/packages/sdk/native-api-operations.json:219-296; mw/docs/model/contexts.cml:1970-1999).

Keep Organization's domain service, AccessControl's reach/capability interpreter, PostgreSQL authority projection and migrations, attributable lifecycle/delegation history, idempotent business commands, transport response receipts and retries, Better Auth, three existing Foldkit programs, and Mailing's React route application-owned. effx declares contracts and binds existing handlers; it is not an authority engine, SQL adapter, transaction manager, receipt store, or UI runtime (mw/packages/domain/src/organization/service.ts:75-105,174-216; mw/packages/domain/src/authz/reach.ts:41-146; mw/apps/backend/src/organization/http.ts:1217-1254; mw/apps/dashboard/app/foldkit/organization/elements.ts:26-100).

Exact current operation interface

The original names, route, credential, scope resolver, decision time, schema, ordered local problem union, and response semantics below are the contract. Person means PersonSecurity (Better Auth cookie or delegated Person OAuth bearer); no principal comes from the dashboard role or an earlier read. None of these endpoints has path parameters. All thirteen are external, reveal-concealment operations; only the three public reads have no Person middleware. Every row retains its current operation summary, description and OpenApi metadata (mw/packages/http-api/src/organization.ts:194-596; mw/packages/http-api/src/access.ts:31-104; mw/packages/http-api/src/common.ts:50-108).

Qualified operationMethod and exact pathAccessSpec capability; resolver; timeInput → success
organization.listDepartmentsGET /api/departmentsAnonymous/None; organization.public-departments; SnapshotReadOptional conditional headers; DepartmentJson[] 200 or bodyless 304; public cache.
organization.listTeamsGET /api/teamsAnonymous/None; organization.public-teams; SnapshotReadOptional conditional headers; TeamJson[] 200 or bodyless 304; public cache.
organization.listFieldOfStudiesGET /api/field-of-studiesAnonymous/None; organization.public-field-of-studies; SnapshotReadOptional conditional headers; FieldOfStudyJson[] 200 or bodyless 304; public cache.
organization.listTeamInterestGET /api/team-interest-registrationsPerson; organization.read-team-interest; organization.team-interest-registrations; SnapshotReadOptional department, semester; private 200 TeamInterestResponse with hydra:member and hydra:totalItems.
organization.listMailingListsGET /api/mailing-listsPerson; organization.read-mailing-lists, not an invented Mailing grant; organization.mailing-lists; SnapshotReadOptional department, semester, type=assistants|team|all; private 200 MailingListResponse (name, emails); omitted type defaults to assistants.
organization.createDepartmentPOST /api/departmentsPerson; organization.create-department; organization.department-create; TransactionIdempotency-Key and CreateDepartmentRequest; 201 DepartmentJson with ETag and Location.
organization.createTeamPOST /api/teamsPerson; organization.create-team; organization.team-create; TransactionIdempotency-Key and CreateTeamRequest; 201 TeamJson with ETag and Location.
organization.createFieldOfStudyPOST /api/field-of-studiesPerson; organization.create-field-of-study; organization.field-of-study-create; TransactionIdempotency-Key and CreateFieldOfStudyRequest; 201 FieldOfStudyJson with ETag and Location.
organization.readAppointmentManagementGET /api/organization/appointmentsPerson; organization.manage-appointments; organization.appointment-management; SnapshotReadPrivate 200 AppointmentManagement.
organization.readBoardRostersGET /api/organization/board-rostersPerson; organization.manage-appointments; organization.appointment-management; SnapshotReadPrivate 200 BoardRosters.
organization.executeLifecyclePOST /api/organization/appointments/commandsPerson; organization.manage-appointments; organization.appointment-management; TransactionIdempotency-Key and OrganizationLifecycleCommand; 200 OrganizationLifecycleResult with ETag.
organization.readDelegationManagementGET /api/organization/delegationsPerson; organization.manage-delegations; organization.delegation-management; SnapshotReadPrivate 200 DelegationManagement.
organization.executeDelegationPOST /api/organization/delegations/commandsPerson; organization.manage-delegations; organization.delegation-management; TransactionIdempotency-Key and DelegationCommand; 200 DelegationResult with ETag.

The three public GETs accept optional If-Match/If-None-Match and preserve strong ETag, Vary: Origin and Cache-Control: public, max-age=60, s-maxage=300, must-revalidate on their conditional response branches. Staff GETs are 200 only, private, no-store/Vary: Origin, without an ETag. The three creates are 201 with strong ETag, origin-relative Location, no-store and Vary; two command responses are 200 with strong ETag, no-store and Vary (not 201/204). POST bodies are JSON with the exact domain SchemaRefs. Lifecycle/delegation expected revisions belong in relevant body variants, not a synthesized HTTP If-Match header. Preserve repeated query parameters as the existing string-or-array schemas and their first-value selection rather than coercing, rejecting or concatenating them in a generated client (mw/packages/http-api/src/organization.ts:74-136,194-559; mw/packages/http-api/src/http-semantics.ts:53-56,83-99,136-202; mw/packages/domain/src/organization/lifecycle.ts:99-162; mw/packages/domain/src/authz/delegation.ts:295-322; mw/apps/backend/src/organization/http.ts:797-916).

Directory JSON schema identifiers, descriptions and examples, and the interest/Mailing response schema identifiers/examples are current OpenAPI facts, not merely sample UI data. Keep the original exported domain schemas (DepartmentJsonSchema, TeamJsonSchema, FieldOfStudyJsonSchema, appointment/delegation schema exports) and the present annotated array/schema references, without copying a schema AST or fabricating a privacy-safe but nonidentical example. Keep exactly the current request structures: create bodies derive from the existing domain jsonCreate schemas; TeamInterestResponse keeps its Hydra shape; no transport schema gives a new user-supplied entity ID or revision (mw/packages/http-api/src/organization.ts:1-192,195-392; mw/packages/http-api/src/v2-schemas.ts:114-128).

Problems are endpoint-local, ordered and closed. Retain all three public directory unions (OrganizationListDepartmentsProblem, OrganizationListTeamsProblem, OrganizationListFieldOfStudiesProblem), the scoped interest and Mailing unions, each of the three separate creation unions, and the existing OrganizationLifecycleProblem used by all five management reads/commands. The public unions have six codes including conditional-precondition failures; scoped unions have seven including authority/origin and invalid-reference failures; each create union has seventeen including invalid/in-flight/digest-conflict/expired receipts, validation, dependency and transaction failures; the lifecycle union has fifteen including stale preconditions, authority and idempotency failures. The respective source tuples and NativeProblemRegistry own their exact order, status, body and RFC 9457 projection; do not collapse them into a convenient group-wide superset or hard-code statuses. Better Auth 401 middleware and Origin ingress still apply before the endpoint handler, separately from its declared union (mw/packages/http-api/src/endpoint-problems.ts:168-299; mw/packages/http-api/src/organization.ts:400-416,418-559; mw/packages/http-api/src/http-semantics.ts:1520-1547; mw/packages/http-api/src/common.ts:50-108).

Authority and ordering that a generated guard must preserve

  • One reach interpreter, current facts and intended invariant. Organization owns appointments, unit status/classification, board governance and current relationships. AccessControl owns principals, capability semantics, named delegation and the pure reach interpreter; PostgreSQL projects those current facts at an instant. A team leader reaches only that team; an appointed independent Styret leader reaches its department, the appointed Hovedstyret leader reaches organization scope, and a derived board seat alone grants no administration. The intended model denies home-department reach over a national team, but accepted 23bc0d6b (unpublished) still treats the team as inside its home department; reproduce and repair that observed defect before claiming Organization authority parity, without merging an unapproved branch. A global-administrator grant is explicit, not inferred from a UI role or membership. Delegation reaches only a conforming ordinary active team's members, area, holders and [start,end) interval; delegations.manage and organization.govern cannot be delegated (mw/packages/domain/src/authz/{reach,delegation}.ts; mw/packages/database/src/organization/authority-postgres.ts:374-551; mw/docs/system.md:397-435; mw/docs/architecture.md:225-249; fix/organization-review-0926:mw/packages/domain/src/authz/reach.ts:34-72).
  • Target-specific denials. Create and lifecycle authorization must still ask the current actor's current target and governance facts: appointed versus derived seats, account usability, independent-board reach, national-team classification, local/national delegation area, active/suspended membership, and who may change national-board/account access. Existing accepted createTeam policy is OrganizationAdministrator even though the intended model describes governing-board team creation; existing SQL stores is_team_leader and free-text position even though the model assigns role types to positions. Document these seams; the transport cutover must not silently assert that the target model already replaced current authority. UI ROLE_*, canManage and visible controls are navigation/display only (mw/packages/domain/src/organization/{lifecycle,authority}.ts; mw/packages/database/src/organization/{administration-postgres,lifecycle-postgres}.ts; mw/docs/system.md:406-435; mw/docs/model/contexts.cml:944-975; mw/STATE.md:174-182).
  • Reads are not automatically a common snapshot. The SnapshotRead annotation declares intent, not evidence that all five protected GET handlers run in one SQL snapshot. Mailing recipients are recomputed in a repeatable-read read-only snapshot; management reads use their current transaction/person-lock/ForShare projection; initial interest/Mailing HTTP authority projections have their own placements. Preserve the existing placement and re-evaluation when binding guards rather than moving decisions to a generated pre-route middleware or claiming a stronger old guarantee than the source provides. Never emit a 304 for a staff GET (mw/apps/backend/src/organization/http.ts:797-997,1089-1127; mw/packages/database/src/organization/authority-postgres.ts:33-70,521-551; mw/packages/database/src/organization/mailing-lists.test.ts).
  • Commands and replay. The five POSTs require current authority, not just a prior successful receipt. The HTTP serializable executor runs preparation before any HTTP receipt lookup, repeats it on a serialization retry, and commits the HTTP response capsule in the ambient transaction. For creates the HTTP path resolves current actor/AccessSpec in preparation before the domain create; the direct create SQL service checks its own receipt before its own authorization, so do not claim that SQL service alone reauthorizes a replay. Lifecycle and delegation services resolve/lock current actor and target and check domain history before replay; in the HTTP preparation their domain effects precede the second current-credential resolution and reflected AccessSpec check. Preserve this exact in-transaction order, response identity/canonical digest, commandId === Idempotency-Key, CAS expected revision where applicable, before/after/reason audit and response receipt; no generated eager outer guard, extra transaction, synthetic retry or lease. A revoked actor must not replay an earlier HTTP success (mw/apps/backend/src/organization/http.ts:395-536,1000-1085,1131-1212; mw/apps/backend/src/http-api/receipt-transaction.ts:241-329; mw/packages/database/src/organization/{administration-postgres,lifecycle-postgres}.ts; mw/packages/database/src/authz/delegation-postgres.ts:168-255).

Application seam and three existing Foldkit programs

Use the prior generated Content/TeamApplications pattern, not a second generator or a hand-coded endpoint mirror. A declaration-only mw/packages/http-api/src/organization.effx.ts exports one Http.group({ root: ExternalNativeApi, group: "organization", ... }) with thirteen Operation.query/Operation.command declarations and current group title Organization, description and x-displayName. Group defaults cover only shared exposure, registry and metadata; explicit per-operation credential/principal/middleware, capability, resolver, time, params/query/headers, statuses and ordered problems preserve the mixed public/Person surface. The app-owned organization-effx-adapters.ts exposes the existing SchemaRefs, HTTP header schemas, ordered problem tuples and access/operation annotation bridge; avoid copying a second source of truth from handwritten endpoints. Inspect the existing directory-effx-adapters.ts, team-application.effx.ts, content.effx.ts and generated raw guard interfaces before implementation. These filenames describe the intended seam, not committed files (mw/packages/http-api/src/{directory-effx-adapters,team-application.effx,content.effx}.ts; mw/packages/http-api/.effx/generated/directory-contract.ts; mw/apps/backend/.effx/generated/directory-handlers.ts; docs/decisions/0004-schema-ref.md; docs/decisions/0006-query-is-a-semantic-claim.md).

Add this one declaration to the existing contract and backend compiler project files and the existing just effx-native recipe. Bind exactly thirteen generated raw handler aliases back to the thirteen current mw/apps/backend/src/organization/http.ts Effects and current authorizeAnonymous/authorizePerson seams. Neither IR nor generated code selects a current board, interprets delegated reach, starts transactions or commits SQL. Once parity holds, remove the handwritten OrganizationApi .add(...) declarations, obsolete endpoint imports, old HttpApiBuilder.group(...).handleRaw(...) binding and all old-symbol callsites/tests in a single cutover. Do not keep aliases, dual groups, phantom methods or duplicate native operation registries. The mw/packages/sdk generated Effect client and browser cookie/SSR bridges remain the callers; whole native OpenAPI/SDK still have 107 external operations and one separate internal receipts operation (mw/packages/http-api/src/api.ts:12-75; mw/packages/http-api/src/organization.ts:574-589; mw/apps/backend/src/organization/http.ts:1217-1254; mw/justfile:25-37).

There are three distinct existing Foldkit stateful programs, not a single invented Organization Model: appointment management at /dashboard/team, FieldOfStudy catalog at /dashboard/linjer, and delegation management at /dashboard/delegeringer. Their Models own request identity, pending command IDs and uncertain/retry states; Connect embeds and Disconnect disposes each runtime. The Team kind of the catalog program is currently unmounted because the /dashboard/team element embeds appointment management instead. Do not claim its catalog view is an exercised page or rework its routing for this HTTP migration. The browser client continues through the same-origin generated Effect SDK. Mailing has a fourth UI surface but no fourth Foldkit program: the /dashboard/epostliste React Router loader uses authenticated server-side SDK organization.listMailingLists, then renders private read-only copyable addresses. Preserve the loader's access, query and no-store policy; do not return recipients to an anonymous catalog (mw/apps/dashboard/app/foldkit/organization/{elements,model,command,update,management,delegations,browser-client}.ts; mw/apps/dashboard/app/routes/{dashboard.team._index,dashboard.linjer._index,dashboard.delegeringer._index,dashboard.epostliste._index}.tsx; mw/apps/dashboard/app/lib/{browser-api,api.server}.ts).

Two unmerged branches are evidence, not code to merge

Read-only inspection of fix/organization-review-0926 at 8c0f2510 (unpublished) (unpublished, unmerged) and test/organization-race-0927 at 01e74103 (unpublished) (unpublished, unmerged); both diverge from mono-web main through an older base, not from the accepted 23bc0d6b (unpublished) cutover. The candidate-lookup branch adds a fourteenth POST /api/organization/appointments/candidate-lookup Query-over-POST; it is not in the accepted thirteen nor in the current SDK index. Its exact-email body/no-store Person-authorized SnapshotRead design is relevant to ADR 0010, but neither it nor the associated Foldkit picker/runner changes may slip into this thirteen-route transport migration (mw/packages/http-api/src/organization.ts:418-589; docs/decisions/0010-query-over-post.md:5-19; branch diff fix/organization-review-0926:mw/packages/http-api/src/organization.ts:444-471).

The review branch also proposes correcting national-team area vs home department, a one-Hovedstyret uniqueness index, independent-department board guards/triggers/lock ordering, and appointment candidate provenance (verified login email vs editable contact email), with reviewed-cohort import guards. The race branch adds a 390-line PostgreSQL test against those shared governance changes: concurrent board creation, raw uniqueness conflict, recognition/demotion in both orders, and service-level command races. The two branches carry the same governance-race test blob, not independent green proofs; their candidate identity policy differs. The trigger does not make board-active status an unconditional database invariant: its source explicitly permits deactivation. These are branch-source observations only; no test, migration upgrade, Alloy model check or main-branch approval was inferred (fix/organization-review-0926:mw/packages/database/migrations/{0081-national-board-singleton,0082-independent-department-board}.sql; test/organization-race-0927:mw/packages/database/src/organization/governance-race.test.ts:195-383; test/organization-race-0927:mw/packages/database/migrations/0082-independent-department-board.sql:6,40-47; fix/organization-review-0926:mw/packages/database/src/organization/lifecycle-postgres.ts:144-170).

Before implementing, compare the exact chosen mono-web source revision and these branches. If a relevant defect actually appears on the proposed baseline, record a focused failing real HTTP/SQL/browser regression red on a clean revision, repair at the owning source in a separate minimal change, exercise it green (supported PostgreSQL majors and PGlite/upgrade proof for any new migration), then revisit source/parity. Do not cherry-pick either branch wholesale, infer branch test claims as passed, mix mutually different candidate-identity policy, or silently rewrite Organization governance as a side effect of generating HTTP (mw/AGENTS.md#delegation-and-landing; mw/AGENTS.md#verification-and-resources; docs/specs/0004-monoweb-migration-plan.md:316-327).

Implementation and acceptance gates — after approval, not yet run

  1. Pin source and compiler. Obtain operator approval for this specification first. In the isolated mono-web integration worktree verify clean exact HEAD, devenv shell hooks, frozen Bun dependencies, accepted five-group generated bytes and effx distribution manifest. Re-vendor only if the effx pack changed, then re-prove the existing five groups before adding Organization. Do not touch the operator's mono-web main, provider or production. Compare unmerged diffs read-only; if an actual source defect is found, apply the red-first gate above. If strict compiler output fails, capture the actual diagnostic from thirteen real declarations and a minimal reproducing fixture for the effx compiler owner; do not bypass --strict-access, replace a header with a lie, or hand-edit .effx/generated files (mw/justfile:25-37; mw/AGENTS.md#verification-and-resources; docs/specs/0023-team-applications-migration.md:49-58).
  2. One authoritative IR and exact projection. just effx-native generates the thirteen endpoint group and raw handler interface alongside all five previous groups. Run effx check on the contract and backend projects with --strict-access; require matching semantic IR hashes, correct public None/Anonymous versus protected Person middleware and current authority timing. Regenerate the whole OpenAPI and SDK using mono-web recipes. Count exactly 107 external IDs, the original 13 Organization IDs, and the separately excluded internal receipts ID. Compare every byte of whole OpenAPI and SDK against the pinned clean accepted 23bc0d6b (unpublished) artifacts, not just the thirteen operation IDs or a generated endpoint shape. Differences in status, headers, bodyless 304, metadata/examples, security, named schemas or problems are failures until explained and repaired at source; the five previously accepted groups must stay byte-identical (mw/packages/http-api/test/native-api.test.ts; mw/packages/sdk/native-api-operations.json; mw/packages/http-api/src/organization.ts:194-596).
  3. Focused behavior and security. Run the existing mw/packages/http-api/test/native-api.test.ts plus thirteen-operation adapter/access parity tests and generated SDK tests; mw/apps/backend/src/organization/{http,team-interest.http}.test.ts; database mw/packages/database/src/organization/{leader-reach,mailing-lists}.test.ts, mw/packages/database/src/authz/delegation-postgres.test.ts; Foldkit mw/apps/dashboard/app/foldkit/organization/{update,view}.test.ts, and route-level Mailing behavior if altered. Add failing-before-fix tests only for real defects, not assertion-of-implementation snapshots. Prove public conditional 200/304 versus private 200, auth 401/403, scope denial and no cross-department/national-team escalation; all three 201 creates, idempotent replay versus changed-digest conflict and revoked-actor denial; lifecycle and delegation command revision/history/receipt and concurrent-state outcomes. just proof authorization-rules and the current local PostgreSQL migration/authority suites gate any touched reach/interpreter/DDL. Any upgrade/rewrite uses the repository's previous-schema PGlite and supported-major PostgreSQL upgrade proof, not an empty database only (mw/packages/http-api/test/native-api.test.ts; mw/apps/backend/src/organization/http.test.ts; mw/packages/database/src/{organization,authz}; mw/AGENTS.md#change-rule).
  4. Complete felt journey, not only the old browser subset. Run just e2e organization on the generated contract/raw handlers/SDK with real Better Auth and disposable PostgreSQL. The existing runner currently signs in, creates department/team/field, checks exact replay/conflict/denied references, visits appointment-selection and FieldOfStudy catalog, validates HTTP/SQL receipts, accessibility and cleanup. It does not exercise lifecycle writes, the delegation Foldkit program, or Mailing's React route. Extend its first-party named journey (or add a separately registered/hermetic named suite) to exercise authorized and denied appointment lifecycle writes, delegation issue/end and revoked replay through the current Foldkit/SDK/browser → native HTTP → PostgreSQL path, plus Mailing SSR's authorized recipient/denial/no-store behavior. Do not label the old just e2e organization pass as proof of the missing behaviors; preserve its existing assertions (mw/apps/dashboard/e2e/{native-organization-administration.spec.ts,run-real-native-organization-administration.mjs}; mw/apps/dashboard/app/routes/dashboard.epostliste._index.tsx; mw/AGENTS.md#delegation-and-landing).
  5. Adjacent and aggregate regression on one clean committed revision. Run the previously accepted five journeys separately: just e2e profile, just e2e schools, just e2e social-events, just e2e content-publication, just golden team-application including its two previously accepted interruption fault-mode probes. Run just e2e owner too as a cross-context receipt-owner authorization regression, not as a substitute for Organization's delegation/appointment browser proof: that recipe runs run-real-receipt-owner.mjs. Then run full serial just check --concurrency=1 and just test --concurrency=1. All heavy commands run from devenv shell serially with just measure --class <class> -- <command...> under the one machine-wide lock; do not kill an unrelated holder or mark an aggregate pass from partial legs. Record exact command, source revision, exit, wall time, peak RSS, clean tree and unverified boundaries from just measure --report (mw/justfile:139-170; mw/apps/dashboard/package.json:35,40; mw/AGENTS.md#verification-and-resources; docs/research/team-applications-slice-evidence.md).
  6. Source accounting and custody. At 23bc0d6b (unpublished) and the final same clean accepted revision, count wc -l of named Organization contract/adapter/backend, reach/domain/SQL, three Foldkit programs, Mailing route and affected callers; count generated contract/handler TypeScript and whole OpenAPI/SDK separately. This is a descriptive maintained-source delta, not a target to game. Store measured machine/runtime evidence and source bundles outside the public product repo; update the effx and mono-web gate/state evidence only on an actually clean passing revision. Keep the integration branch unlanded until the operator grants landing authority; approval to implement is not permission to deploy, alter credentials/provider data, or transfer a production writer (docs/specs/0004-monoweb-migration-plan.md:305-332; mw/AGENTS.md#verification-and-resources).

Definition of done: one generated thirteen-operation Organization HTTP group and generated raw backend binding, unchanged whole native OpenAPI/SDK bytes and 107+1 inventory, preserved Organization/AccessControl authority and transaction/receipt behavior except the separately proved national-team reach repair, browser-proven appointment/directory/delegation/Mailing paths, the five accepted journeys plus receipt-owner regression and full check/test at the same clean source commit. No Organization acceptance is claimed yet; branch landing and production actions remain operator-controlled.

Approval clarification (2026-10-04). The national-team home-department discrepancy may be repaired only toward the documented model: mw/docs/system.md:402-407 says national-team scope is organization-wide and Hovedstyret governs it; mw/docs/model/authority.als:28-38,100,116-119,155,601-608 derives team area from its scope rather than its home and confines a local Styret. Commit a failing real-PostgreSQL regression on the accepted baseline before editing the one reach interpreter or its SQL projection; run just model check after that authority repair. If those two documents disagree, stop for an operator decision. The approved Organization browser extension includes lifecycle writes, delegation issue/end and revoked replay, and the Mailing SSR route, preserving old assertions and registering any new named suite with just layout. The candidate lookup and unmerged branch governance changes remain excluded unless a source defect reproduces red.

On this page