Skip to content

Latest commit

 

History

History
166 lines (140 loc) · 21.4 KB

README.md

File metadata and controls

166 lines (140 loc) · 21.4 KB

LearnStack Engineering Standards

This directory contains the engineering rules that apply across the LearnStack codebase. Architecture docs (docs/architecture/) explain what we are building; standards explain how we build it.

How to Use This Directory

  • Author-side: before opening a PR, skim the standards that touch your change.
  • Reviewer-side: reference standards in review comments rather than re-litigating preferences.
  • Newcomer-side: read 00-principles.md first; the rest is reference material.
  • Disagreement: propose a change via PR against the standard itself. Standards are versioned documents, not folklore.

Index

# Document Scope
00 Principles The handful of beliefs every other standard descends from.
01 Architecture Standards Module boundaries, dependency direction, ports & adapters, aggregate ownership.
02 Backend Coding Standards C# / .NET style, async, nullability, records, MediatR, EF Core.
03 Frontend Coding Standards TypeScript / React / Next.js style, components, hooks, data fetching.
04 API Design Standards REST, Problem Details, pagination, idempotency, versioning.
05 Database Standards Schema conventions, migrations, indexing, tenant-aware patterns.
06 Testing Standards Unit / integration / architecture / E2E / contract tests; pyramid; coverage targets.
07 Frontend Architecture Standards App Router layout, server vs client components, tenant context, SDK shape.
08 Localization Standards i18n rules for strings, content, URLs, SEO, formatting.
09 Error Handling Standards Exception hierarchy, Problem Details, frontend error boundaries, user-facing copy.
10 Observability Standards Logging, tracing, metrics, correlation, redaction.
11 Security Standards Auth, tenant isolation enforcement, OWASP, secrets, file uploads, headers.
12 Infrastructure Standards Docker, CI/CD, environments, configuration, deployment.
13 Documentation Standards ADRs, code comments, diagrams, doc style.
14 Git Workflow Standards Branching, commits, PRs, reviews.
15 Performance Standards Budgets, caching, query shape, frontend perf.
16 Accessibility Standards WCAG targets, semantic HTML, keyboard, screen readers.
17 Code Review Standards What to look for, what not to block on, etiquette. Zero-tolerance blockers and author self-review gate.
18 Audit Coverage Standards Which operations must be audited; payload contract; retention; per-module classification matrix.
19 Permissions Standards {module}.{resource}.{action} naming, closed action set, registry pattern, matrix template, built-in roles.
20 Infrastructure Stack Standards Demand-gated building blocks, the foundation ports (IEventBus, ICacheService, ISecretProvider), APISIX gateway, Hub contract surface + its two invariants, entitlement projection, outbox/inbox usage.
21 Architecture Tests + Analyzers Catalogue Single source of truth for the identifier, assertion, status, and source ADR / standard of every non-skippable architecture test or Roslyn analyzer. Cross-link target so renames touch one place.

Status of Each Standard

Standards have one of three states:

  • Active. Currently enforced; PRs must comply. There is code, tooling, or a live process that the standard governs today.
  • Adopted. Agreed and binding on the code that will implement it — but that code does not exist yet, so nothing enforces it beyond review.
  • Draft. Proposed; open for discussion.

The rule that makes the model mean something

A standard with no implementing code is Adopted, not Active.

Each document declares its state at the top, and until 2026-08-08 all twenty-two declared Active — including standards governing endpoints, migrations, permissions and audit rows that do not exist. A three-state model whose every member sits in one state is decorative: it tells a reader nothing, and it quietly overstates how much of the corpus is load-bearing.

"Adopted" is not a weaker commitment. It is the honest one. It says: this is the rule the implementing PR must satisfy, and there is nothing standing between a violation and main except a reviewer who remembers. That is exactly the situation in which a reviewer most needs to know.

Promotion Adopted → Active happens in the PR that lands the enforcement — the migration, the endpoint, the analyzer, the architecture test — not in a separate bookkeeping pass.

Honest status today

The table below describes the implementation as of 2026-10-13: Phase 02a and P02d-1–6 are complete and merged. P02d-5's ingress, configured server caller, dynamic entry and frontend guards are delivered through PR #26; the merge closeout owns its final review and CI evidence. P6's decision package and ADR-0027 are Accepted — 2026-10-09; P6 Step 1 implements the pinned i18n/document foundation, shared request-local loaders, chrome/states and atomic theme injection. Its delivery record owns foundation validation and review evidence. Steps 2–3 implement actual catalog/course/lesson views, pagination/metadata and blocking JSX accessibility controls. Step 4 implements automated product HTML/Flight proofs and completes manual accessibility, including maintainer-reported VoiceOver. P7 delivers demo/browser/Lighthouse. ADR-0055 remediation delivers single-bootstrap admission and passing production replacement proofs. All three steps completed both independent review rounds. The Step 3 record owns completed Step 3 reviews, verified fixes and delivery evidence; its five rules are Implemented. P6's merge closeout records PR #27's merge and final verification. P7 delivers the shared demo, G20 fences and seeded browser/Lighthouse enforcement. Its Step 4 record owns review, real CI and protection receipts. Acceptance alone promotes no status.

The documents and this table say the same thing, and a test holds them to it. Phase 02a Packet 10 reconciled the twenty-two status lines with this table, together with the architecture-test statuses, so the two views of "what is actually enforced" changed in one commit — Standard_Status_Headers_Match_The_Index fails the build if they part again, counts included.

# Standard Status What does or does not enforce it today
00 Principles Active Governs every PR and every ADR; principles 1, 16 and 17 are already deciding live scope questions.
01 Architecture Standards Active Module layout shipped; ModuleDomain_DoesNotDependOn_* and the planted-violation meta-test are green.
02 Backend Coding Active MediatR pipeline, Result<T>, IClock, the LS0001 analyzer and the pipeline-order test all ship. Packet 6 brought the first DbContext, the first aggregates and the ambient unit of work, so its EF Core and domain-modelling clauses became live. Tenancy, Customization, Audit and Education now implement domain and persistence code.
03 Frontend Coding Active ESLint and TypeScript strict mode are configured, the Vitest harness runs, and Packet 10 added the two rules this document's § Forbidden names — the direct-fetch ban and dangerouslySetInnerHTML outside the sanitised-HTML primitive — with a test that lints fixtures through the app's own configuration, because a configured rule and a missing one look identical from a green pnpm lint. P02d-5 delivers trusted entry/caller controls; P02d-6 Step 1 implements complete UI catalogues, ICU/callsite controls and synchronous chrome/state views; Step 2 adds catalog/course views and metadata; Step 3 adds ordered lesson presentation and blocking JSX accessibility controls.
04 API Design Active Packet 4 shipped the versioned route convention and its startup guards, one Problem Details shape on every error including the framework-minted ones, cursor pagination, the sort grammar, idempotency keys, ETag concurrency, correlation ids, the request-body limit and the tenancy edge — each with tests in the required backend check. P02d-4 adds four institution public GET/HEAD pairs, served/snapshot contract controls and compatibility CI; application writers remain unrouted.
05 Database Active Packet 6 applied it — two migration chains, ten tables — and Packets 8 and 9 took it to four chains and seventeen data tables. P02d-1 Step 2 adds Education: five chains and twenty-one data tables (twenty-six including five migration-history tables), with parent-mirror and Pattern A proofs. The four-role model and the canonical RLS template this document owns — ENABLE and FORCE, one AND-ed policy per table, an explicit WITH CHECK — are asserted against a real PostgreSQL as learnstack_app. Its § Concurrency, § Table classes, § Indexes and § GRANT matrix each have a test that fails without them. Partitioning and the retention job are still ahead.
06 Testing Active Unit, architecture and contract suites, and the Docker-free integration tests, run in the required backend job — Packet 4 removed the filter that used to exclude the integration assembly, which by then held the only tests that could catch an unversioned route. The Docker-bound backend-integration job activated in Packet 6 with the four-role provisioning suite; the split is by [Trait("Requires","Docker")] and the two jobs' filters are exact complements. The backend-integration job is required as of P02d-1, 2026-09-14 (CONTRIBUTING § Branch protection).
07 Frontend Architecture Active The one-app rule is mechanical — Frontend_Has_Only_The_Web_App fails a second application in this repository — and the route groups it prescribes exist as layouts. The rest, the server/client split and the tenant context an SDK call carries, is exercised first in Phase 02d: Active for what ships, and the phase that adds components is the one that tests them.
08 Localization Active Packet 6 shipped tenant_locales and the slug schema. The partial unique default index enforces at most one default, not existence of a default for every enabled set; P02d-2 ships supported-write validation and the default-enabled CHECK constraint; raw deletes can still leave an enabled locale set without a default. LocalizedText and LocalizedMessage ship with their own cases, and API errors are keyed. P02d-1 shipped Education Pattern A satellites, canonical locales and flat localized-slug uniqueness; P02d-4 Step 3 delivers exact enabled public locale admission and response labels. P02d-5 Step 3 delivers membership-first redirect G36. P02d-6 Step 1 implements Accepted ADR-0027/G39's next-intl 4.14.9 plugin/request runtime, app-local src/i18n/messages/{en,tr}/public.json and catalogue controls. Phase 04 inherits the foundation for CMS/Studio.
09 Error Handling Active L1 IExceptionHandler, the exception hierarchy, ProblemDetailsFactory and HttpStatusMap shipped in Packet 3.
10 Observability Active Serilog → OTLP, OpenTelemetry SDK, TenantContextSpanProcessor and the redaction enrichers shipped in Packet 3.
11 Security Active No authentication yet, and the isolation half of this document is live and mechanical. Row Level Security with the four roles and the isolation suite that runs as learnstack_app; the tenancy edge, the trusted-hop predicate and the anonymous rate limiter from Packet 4; and, since Packet 10, § The out-of-band setters is mechanical — every announcer of a session variable is one the table names and each reader opens its transaction read-only, with App_Role_Cannot_Enumerate_Tenants, App_Role_Cannot_Enumerate_Host_Map and Tenant_A_Cannot_Repoint_Tenant_B_Host proving the role cannot read or repoint what the policies bar. Authentication and authorisation land in Phase 02b and Phase 03. Three sections are not enforced by anything today and are the document's own carve-out: § Transport, § HTTP Headers and § CORS — nothing sets HSTS, nosniff, a CSP or an origin policy, at the edge or in either app. Phase 11 § Secure headers owns them, at APISIX and in the ASP.NET layer beside it.
12 Infrastructure Active Compose stack, Makefile, CI workflow, pre-commit hooks and secret scanning all live since Phase 01.
13 Documentation Active Governs this corpus; the CI link audit walks changed Markdown.
14 Git Workflow Active Conventional Commits, hooks and required checks are live. Two branch-protection settings — Require approvals and Do not allow bypassing — are deferred by maintainer decision (2026-08-10) while the repository has one active contributor; the trigger and what activating them involves are recorded in CONTRIBUTING § Branch protection. P02d-1 repaired and re-read the live required-check list on 2026-09-14: all five active jobs, including the current meta name and Docker integration, are required with GitHub Actions attribution and strict up-to-date checking. P02d-4 activates the sixth openapi diff context after a successful real job and verifies every other setting unchanged. P02d-7 activates the seventh public demo + lighthouse context after complete real PR success and verifies all six prior contexts, strict mode and every unrelated setting preserved; its CI and exit closeout records the receipts. CONTRIBUTING § Branch protection owns the current list; the P02d-4 delivery record records activation verification.
15 Performance Active P02d-7 delivers the always-running public demo + lighthouse job under ADR-0056: nine seeded pages, 27 audits, actual-page/source/census controls and hard normalized gzip/CLS/automated-accessibility checks. Timing medians are advisory. The Step 4 record owns real CI and required-check receipts. Production p95/field telemetry/load tests remain Phase 11.
16 Accessibility Active P6 delivers catalog/course/lesson and controlled-state semantics, all 31 active recommended jsx-a11y rules at error severity with per-rule actual-config dirty/clean controls, and real product HTML/Flight proofs. The packet closeout records keyboard/focus/reflow/contrast observations and the maintainer's passing VoiceOver smoke. This is enforcement for delivered public-page scope, not a full WCAG audit. P02d-7 adds hard Lighthouse automated accessibility and a narrow library browser walkthrough; full Playwright Test/axe remains Phase 06.
17 Code Review Active Applied to every pull request merged so far; the zero-tolerance blocker list is in live use.
18 Audit Coverage Active Packet 9 lit the write path under ADR-0033 and ADR-0044: classification at step 3, MUST rows on the business transaction before COMMIT, audit_log / audit_config with their append-only layers, and the catalogue ↔ matrix join enforced in both directions and per request/concrete integration-handler identity by AuditCoverageTests; actual API/Seeder catalogue parity includes Off coordinators. Retention and partitioning are Phase 11's.
19 Permissions Adopted No permission key, policy or role exists. Lands in Phase 03.
20 Infrastructure Stack Active ISecretProvider shipped in Packet 3, the foundation ports and their defaults in Packet 5, and the entitlement socket in Packet 9; DeploymentMode branching happens once, at the composition root. P02b-2 delivers producer dispatch; P02b-3 Steps 1–5 add durable acceptance, shared inbox and independently scheduled default consumers. The Step 6 record owns current enforcement and review status. Packet 10 made the bans this document states mechanical: no module reaches a cache client, a Hub namespace, audit_log or platform_entitlement_cache, and every entitlement key a call site names is a registry member. The adapters themselves — Dapr, Kafka, Valkey, Vault, APISIX — arrive on ADR-0035's triggers, which is the model rather than a gap in it.
21 Architecture Tests Catalogue Active The catalogue's own § Implemented today carries the counts, and The_Catalogue_Counts_Its_Own_Rules recomputes them, so this row does not keep a third copy. Rules run in the architecture assembly and beside it in the unit, integration and frontend suites — each where it can actually fail, against an applied schema, a real host or a real ESLint configuration. Every_Implemented_Rule_Names_A_Test_That_Exists holds each Implemented entry to a method of that name. No_Architecture_Test_Is_Skippable governs backend suites; P02d-5 delivers G38(d)'s guarded frontend runner and planted skip/todo, empty-report and omitted-workspace controls.

P02d-4's accepted decision package adds bootstrap, exact locale/public response, read-only mode, SDK and OpenAPI proof obligations to the relevant Active standards. Step 1 delivers host provenance/admission and read-only transaction proofs. Step 2 adds site bootstrap, all-status no-store/HEAD behavior and Off/dispatch/controller guards. Step 3 adds Education eligibility, scoped seek pages and bounded content/label projections with HTTP/database proof. Step 4 adds served/snapshot equality, recursive DTO guards, generated SDK/drift and breaking-policy CI controls.

Twenty-one Active, one Adopted. Packet 10 moved five: the frontend rules, the one- app rule, the locale invariants, the out-of-band setters and the port bans are all mechanical now, and a standard whose rules a test enforces is Active by this document's own definition. P02d-6 promotes Accessibility with blocking lint/semantic controls and recorded manual evidence. P02d-7 promotes Performance with the hard checks above; this does not make advisory or future production measurements enforced. Permissions remains Adopted until Phase 03 ships permission keys and enforcement.

Relationship to ADRs

Document type Purpose
ADR (docs/decisions/) A one-time decision with status, context, decision, consequences. Immutable history, corrected only by the two bounded mechanisms in 13-documentation.md § Correcting and Amending ADRs.
Standard (docs/standards/) An ongoing rule that the team applies day to day. Editable as the team learns.

When a standard is established, an ADR records the moment of adoption. The ADR then points at the standard for the living detail.

Tooling

Where a standard can be enforced by automation, it must be:

  • Roslyn analyzers / .editorconfig for backend.
  • ESLint / TypeScript strict mode for frontend.
  • Custom architecture tests (NetArchTest / ArchUnitNET) for module-boundary rules.
  • Test conventions enforced by CI.
  • Commit / PR rules enforced by GitHub Actions and CODEOWNERS.

Manual-only rules are flagged in each document.