@regardio/dev 1.24.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +2 -2
  2. package/dist/bin/ship/hotfix.bin.mjs +140 -0
  3. package/dist/bin/ship/production.bin.mjs +120 -0
  4. package/dist/bin/ship/staging.bin.mjs +70 -0
  5. package/dist/bin/ship/utils-BQ-JZ2D5.mjs +45 -0
  6. package/dist/playwright/index.d.mts +24 -0
  7. package/dist/playwright/index.mjs +61 -0
  8. package/dist/vitest/node.d.mts +22 -0
  9. package/dist/vitest/node.mjs +28 -0
  10. package/dist/vitest/react.d.mts +22 -0
  11. package/dist/vitest/react.mjs +28 -0
  12. package/docs/en/README.md +95 -0
  13. package/docs/en/agents.md +57 -0
  14. package/docs/en/standards/api.md +324 -0
  15. package/docs/en/standards/coding.md +144 -0
  16. package/docs/en/standards/commits.md +111 -0
  17. package/docs/en/standards/documentation.md +173 -0
  18. package/docs/en/standards/naming.md +180 -0
  19. package/docs/en/standards/principles.md +84 -0
  20. package/docs/en/standards/react.md +246 -0
  21. package/docs/en/standards/sql.md +258 -0
  22. package/docs/en/standards/testing.md +139 -0
  23. package/docs/en/standards/writing.md +119 -0
  24. package/docs/en/tools/biome.md +89 -0
  25. package/docs/en/tools/commitlint.md +92 -0
  26. package/docs/en/tools/dependencies.md +116 -0
  27. package/docs/en/tools/husky.md +90 -0
  28. package/docs/en/tools/markdownlint.md +84 -0
  29. package/docs/en/tools/playwright.md +117 -0
  30. package/docs/en/tools/releases.md +242 -0
  31. package/docs/en/tools/typescript.md +89 -0
  32. package/docs/en/tools/vitest.md +146 -0
  33. package/package.json +57 -70
  34. package/src/biome/preset.json +3 -0
  35. package/templates/changeset/README.md +14 -0
  36. package/templates/changeset/config.json +11 -0
  37. package/templates/github/release.yml +77 -0
  38. package/dist/bin/exec/clean.d.ts +0 -3
  39. package/dist/bin/exec/clean.d.ts.map +0 -1
  40. package/dist/bin/exec/clean.js +0 -25
  41. package/dist/bin/exec/clean.test.d.ts +0 -2
  42. package/dist/bin/exec/clean.test.d.ts.map +0 -1
  43. package/dist/bin/exec/clean.test.js +0 -45
  44. package/dist/bin/exec/husky.d.ts +0 -3
  45. package/dist/bin/exec/husky.d.ts.map +0 -1
  46. package/dist/bin/exec/husky.js +0 -9
  47. package/dist/bin/exec/p.d.ts +0 -3
  48. package/dist/bin/exec/p.d.ts.map +0 -1
  49. package/dist/bin/exec/p.js +0 -8
  50. package/dist/bin/exec/s.d.ts +0 -3
  51. package/dist/bin/exec/s.d.ts.map +0 -1
  52. package/dist/bin/exec/s.js +0 -8
  53. package/dist/bin/exec/tsc.d.ts +0 -3
  54. package/dist/bin/exec/tsc.d.ts.map +0 -1
  55. package/dist/bin/exec/tsc.js +0 -8
  56. package/dist/bin/lint/biome.d.ts +0 -3
  57. package/dist/bin/lint/biome.d.ts.map +0 -1
  58. package/dist/bin/lint/biome.js +0 -8
  59. package/dist/bin/lint/commit.d.ts +0 -3
  60. package/dist/bin/lint/commit.d.ts.map +0 -1
  61. package/dist/bin/lint/commit.js +0 -8
  62. package/dist/bin/lint/md.d.ts +0 -3
  63. package/dist/bin/lint/md.d.ts.map +0 -1
  64. package/dist/bin/lint/md.js +0 -16
  65. package/dist/bin/lint/package.d.ts +0 -4
  66. package/dist/bin/lint/package.d.ts.map +0 -1
  67. package/dist/bin/lint/package.js +0 -81
  68. package/dist/bin/lint/package.test.d.ts +0 -2
  69. package/dist/bin/lint/package.test.d.ts.map +0 -1
  70. package/dist/bin/lint/package.test.js +0 -65
  71. package/dist/bin/ship/hotfix.d.ts +0 -3
  72. package/dist/bin/ship/hotfix.d.ts.map +0 -1
  73. package/dist/bin/ship/hotfix.js +0 -141
  74. package/dist/bin/ship/production.d.ts +0 -3
  75. package/dist/bin/ship/production.d.ts.map +0 -1
  76. package/dist/bin/ship/production.js +0 -124
  77. package/dist/bin/ship/staging.d.ts +0 -3
  78. package/dist/bin/ship/staging.d.ts.map +0 -1
  79. package/dist/bin/ship/staging.js +0 -51
  80. package/dist/bin/ship/utils.d.ts +0 -9
  81. package/dist/bin/ship/utils.d.ts.map +0 -1
  82. package/dist/bin/ship/utils.js +0 -63
  83. package/dist/bin/ship/utils.test.d.ts +0 -2
  84. package/dist/bin/ship/utils.test.d.ts.map +0 -1
  85. package/dist/bin/ship/utils.test.js +0 -127
  86. package/dist/config.test.d.ts +0 -2
  87. package/dist/config.test.d.ts.map +0 -1
  88. package/dist/config.test.js +0 -101
  89. package/dist/playwright/index.d.ts +0 -10
  90. package/dist/playwright/index.d.ts.map +0 -1
  91. package/dist/playwright/index.js +0 -42
  92. package/dist/playwright/index.test.d.ts +0 -2
  93. package/dist/playwright/index.test.d.ts.map +0 -1
  94. package/dist/playwright/index.test.js +0 -55
  95. package/dist/testing/setup-react.d.ts +0 -2
  96. package/dist/testing/setup-react.d.ts.map +0 -1
  97. package/dist/testing/setup-react.js +0 -1
  98. package/dist/vitest/node.d.ts +0 -22
  99. package/dist/vitest/node.d.ts.map +0 -1
  100. package/dist/vitest/node.js +0 -16
  101. package/dist/vitest/react.d.ts +0 -17
  102. package/dist/vitest/react.d.ts.map +0 -1
  103. package/dist/vitest/react.js +0 -12
  104. package/src/bin/exec/clean.test.ts +0 -63
  105. package/src/bin/exec/clean.ts +0 -36
  106. package/src/bin/exec/husky.ts +0 -14
  107. package/src/bin/exec/p.ts +0 -13
  108. package/src/bin/exec/s.ts +0 -13
  109. package/src/bin/exec/tsc.ts +0 -13
  110. package/src/bin/lint/biome.ts +0 -13
  111. package/src/bin/lint/commit.ts +0 -13
  112. package/src/bin/lint/md.ts +0 -28
  113. package/src/bin/lint/package.test.ts +0 -83
  114. package/src/bin/lint/package.ts +0 -108
  115. package/src/bin/ship/hotfix.ts +0 -241
  116. package/src/bin/ship/production.ts +0 -240
  117. package/src/bin/ship/staging.ts +0 -108
  118. package/src/bin/ship/utils.test.ts +0 -178
  119. package/src/bin/ship/utils.ts +0 -109
  120. package/src/config.test.ts +0 -129
  121. package/src/markdownlint/markdownlint-cli2.jsonc +0 -9
  122. package/src/playwright/index.test.ts +0 -73
  123. package/src/playwright/index.ts +0 -63
  124. package/src/templates/release.yml +0 -128
  125. package/src/testing/setup-react.ts +0 -8
  126. package/src/vitest/node.ts +0 -25
  127. package/src/vitest/react.ts +0 -19
  128. /package/{src → templates}/sqlfluff/setup.cfg +0 -0
@@ -0,0 +1,173 @@
1
+ ---
2
+
3
+ title: "Documentation Standard"
4
+ description: "How Regardio documents its work — for agents to navigate, for humans to reason with, for tests to build on."
5
+ publishedAt: 2026-04-17
6
+ order: 1
7
+ language: "en"
8
+ status: "published"
9
+ ---
10
+
11
+ ## Context
12
+
13
+ Regardio documentation is read by three audiences at once. Agents use it to answer questions about the system and to write code against a known contract. Humans use it to build a judgement of their own about what the project does and why. Tests use it as the specification the implementation is measured against.
14
+
15
+ Those audiences do not all need the same shape on every page. An architectural decision reads best as a short chain of reasoning — context, alternatives, what was chosen and why. A naming reference reads best as a catalogue. A quick introduction to a tool reads best as a few paragraphs and a short example. Forcing every document into one template serves the template rather than the content, and what the reader comes away with is ceremony instead of understanding.
16
+
17
+ What the documentation needs is enough predictability for tooling to rely on — a consistent frontmatter, a recognisable title, a place to find related reading — and enough freedom for each document to take the shape its content asks for.
18
+
19
+ ## Decision
20
+
21
+ Every Regardio document carries a small shared surface. Underneath, it takes whichever of a few shapes fits the content it carries.
22
+
23
+ ### Shared surface
24
+
25
+ Every document has the same top:
26
+
27
+ - **Frontmatter** that identifies the document and lets tooling index it
28
+ - **A title** as a heading or implicit from frontmatter
29
+ - **An opening that names what the document is for** — one or two sentences, before any sub-headings, so that a reader landing cold knows where they are
30
+
31
+ Every document has the same bottom:
32
+
33
+ - **Cross-references** to documents the reader is likely to want next, either inline in the prose or collected under `## Related` at the end
34
+
35
+ Between the top and the bottom, the document takes the shape its content asks for.
36
+
37
+ ### Shapes the body takes
38
+
39
+ A few shapes recur. Pick the one the content fits; do not pad the content into a shape it resists.
40
+
41
+ **Decision record.** When the document captures a choice with real trade-offs, the ADR shape carries the reasoning well: *Status → Context → Decision → Alternatives Considered → Operational Rules → Consequences*. Operational Rules are the part tests bind to. The shape is an option for decision-heavy documents, not a requirement for every document.
42
+
43
+ **Reference catalogue.** When the content is a set of rules, names, or mappings that readers look up rather than read end-to-end, a catalogue of short sections with examples is the honest form. Naming conventions, file-layout rules, linter settings, and configuration tables read this way.
44
+
45
+ **Concept or entity note.** When the document describes a thing in the domain — a Channel, a Piece, a Plan — a short run of paragraphs that names the thing, its role, and its relations is usually enough. Two or three headings if the thing has parts worth naming separately.
46
+
47
+ **Quick introduction.** When the document introduces a tool or a workflow, a few paragraphs and a small example are enough. The reader needs to know what the thing is, when to reach for it, and where to go next. No ADR skeleton is required for a tool page.
48
+
49
+ **Warm reasoning.** When the document is working something out — a use case, a walkthrough, an explanation of why a piece of the domain behaves the way it does — prose that follows the thought is the right form. Headings appear where they help the reader keep their place, not because structure is owed.
50
+
51
+ A document can borrow from more than one shape. An architectural document might open with warm reasoning and close with Operational Rules. An entity note might include a short alternatives paragraph when the entity's shape had genuine contenders. The shapes are orientation, not slots.
52
+
53
+ ### Frontmatter
54
+
55
+ Frontmatter is the part tooling reads. Keep it stable.
56
+
57
+ | Field | Required | Notes |
58
+ |---|---|---|
59
+ | `title` | yes | Noun phrase naming the document. Decision records may prefix `"ADR: "`. |
60
+ | `description` | yes | One sentence. What this document is for, without hedging. |
61
+ | `publishedAt` | yes | ISO date (`YYYY-MM-DD`) the document was first accepted. |
62
+ | `status` | yes | `"draft"`, `"published"`, `"superseded"`. |
63
+ | `language` | yes | `"en"`, `"de"`. |
64
+ | `order` | no | Integer, when a sibling set has a meaningful reading order. |
65
+ | `kind` | no | `"adr"`, `"entity"`, `"concept"`, `"architecture"`, `"guide"`, `"use-case"`, `"reference"`. Lets agents and renderers pick the right treatment. |
66
+ | `area` | no | `"ensemble"`, `"supabase"`, `"connect"`, `"instrument"`, `"dev"`. Names the implementation the document belongs to. |
67
+ | `supersedes` | no | Filename (without extension) of the document this one replaces. |
68
+ | `supersededBy` | no | Filename of the document that replaced this one. |
69
+
70
+ ### Tense and stance
71
+
72
+ Documents describe what the system does, in the present tense, observed rather than advertised. "The publication function returns pieces ordered by `sort_order`." Not "The publication function will return…" and not "We should implement…". The tense holds whether the behaviour is currently built or still being specified; the `status` frontmatter carries the difference.
73
+
74
+ The stance is observational. A Regardio document is not a pitch. It notices how the system fits together and what follows from that. Reliability, safety, transparency, usefulness, and care for the people the software serves show up through how the reasoning is laid out, not by being claimed. A reader who finishes a document should be able to make their own judgement about whether the system is sound — that is what the prose is for.
75
+
76
+ ### What shows up where
77
+
78
+ - **Code snippets** appear where they clarify a contract, a data shape, or a naming pattern. Reference catalogues quite properly carry several; decision records rarely need any. A snippet never stands in for the reasoning around it.
79
+ - **Names** (files, functions, columns, handles) appear where they are contracts readers rely on. They do not appear as a substitute for describing what something does.
80
+ - **Procedural steps** belong in runbooks and READMEs. A domain spec does not read like a cookbook.
81
+
82
+ ### Cross-references
83
+
84
+ Links carry a short descriptor of what the link leads to, not just a filename:
85
+
86
+ ```markdown
87
+ The [Channel](../entities/channel.md) is the publication destination;
88
+ the [Publishing Architecture](../architecture/publishing-architecture.md)
89
+ describes how callers reach it.
90
+ ```
91
+
92
+ `## Related` at the end of a document lists the next pages a reader is likely to want.
93
+
94
+ ### Voice
95
+
96
+ The [Writing](./writing.md) standard covers voice, tone, and language. This document relies on it rather than repeating it.
97
+
98
+ ## Alternatives Considered
99
+
100
+ ### A single ADR skeleton for every document
101
+
102
+ **Dismissed because** it presses reference catalogues and tool introductions into a shape that does not suit them. The skeleton adds ceremony where the content is already clear, and readers learn to skim past sections that carry no weight. The skeleton remains available for documents whose content earns it.
103
+
104
+ ### No shared shape at all
105
+
106
+ **Dismissed because** agents and tooling need something predictable to index against, and readers benefit from landing on any document and knowing roughly where to look. A thin shared surface — frontmatter, opening line, closing references — is enough.
107
+
108
+ ### Separate templates per document kind
109
+
110
+ **Dismissed because** the kinds blur at the edges. An entity note sometimes carries a decision; a use case sometimes carries a catalogue. A small set of recognisable shapes that documents can borrow from works better than a closed list of templates.
111
+
112
+ ## Operational Rules
113
+
114
+ ### Frontmatter is complete
115
+
116
+ Every document carries `title`, `description`, `publishedAt`, `status`, and `language`. Tooling that indexes or lists documents relies on these five.
117
+
118
+ ### Opening names the subject
119
+
120
+ Before the first sub-heading, a reader can tell what the document is for. If the opening does not make this clear, the document is not ready.
121
+
122
+ ### Shape follows content
123
+
124
+ The body takes the shape the content asks for. A decision record uses the ADR skeleton if that skeleton helps the reasoning; a reference uses a catalogue; an introduction stays short. Where a document borrows from more than one shape, it does so in service of the reader, not in service of the template.
125
+
126
+ ### Tense is present, stance is observational
127
+
128
+ Documents describe the system as it is, in the present tense. Not-yet-built behaviour is flagged through `status`, not through hedged tense. The prose observes rather than promotes.
129
+
130
+ ### Reasoning is preserved, not rewritten
131
+
132
+ When a decision is revisited, the existing document is superseded. The old reasoning stays readable so that future readers can see what was known at the time. An in-place rewrite that changes direction without supersession loses the history.
133
+
134
+ ### Code and names earn their place
135
+
136
+ A snippet appears because it clarifies a contract the prose cannot carry alone. A specific name appears because callers rely on it. Both are tools for the reader, not decoration.
137
+
138
+ ### Tests can point at a document
139
+
140
+ A behaviour worth testing has a place in a document that names it. The document does not need a section labelled "Operational Rules" for this — it needs prose clear enough that a test can quote it and both the test author and the reviewer know what is being verified.
141
+
142
+ ### One subject per document
143
+
144
+ A document names one entity, one concept, one decision, one scenario. When a draft sprawls across several, the move is to split it.
145
+
146
+ ## Consequences
147
+
148
+ ### Positive
149
+
150
+ - Documents read naturally for their content. ADRs feel like ADRs; reference catalogues feel like reference catalogues; introductions stay short.
151
+ - Agents and humans find a predictable frontmatter and opening, and the body of each document carries its reasoning in the form that fits.
152
+ - The docs stay honest. Observational prose about what the system does leaves room for the reader to form a judgement, rather than prescribing one.
153
+ - Tests bind to the prose that describes behaviour, wherever in the document that prose lives.
154
+
155
+ ### Negative
156
+
157
+ - "Shape follows content" asks for judgement. Contributors unsure of the right shape need a reference document to look at; the existing documents serve that role.
158
+ - Without a single template, reviewers sometimes have to say "this would read better as a catalogue" or "this would read better as an ADR". That conversation is part of the standard, not a cost around it.
159
+
160
+ ### Mitigations
161
+
162
+ - New documents are often patterned on a nearby existing one. A contributor writing a new entity note copies the shape of an adjacent entity note; a contributor writing an ADR copies an adjacent ADR.
163
+ - Reviewers point at this document when a draft has picked a shape that resists its content, and help the author find the form the content already has.
164
+
165
+ ## Related
166
+
167
+ - [Writing](./writing.md) — Voice, tone, language
168
+ - [AI Agent Guidelines](../agents.md) — How agents use these documents
169
+ - [Principles](./principles.md) — Shared development principles
170
+
171
+ ---
172
+
173
+ **License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio
@@ -0,0 +1,180 @@
1
+ ---
2
+
3
+ title: "Naming"
4
+ description: "Naming patterns across TypeScript, SQL, CSS, Git, and configuration — each language in its own idiom, aligned across the seams."
5
+ publishedAt: 2026-04-17
6
+ order: 4
7
+ language: "en"
8
+ status: "published"
9
+ kind: "reference"
10
+ area: "dev"
11
+ ---
12
+
13
+ A name is the shortest documentation a thing gets. The same concept can appear in TypeScript, a SQL column, a CSS class, and a branch; when the words line up across those surfaces, the concept stays recognisable. The convention is to match each language's native idiom and keep the words in the same order across languages.
14
+
15
+ ## General
16
+
17
+ - Names carry their purpose — `getUserById` not `getUsrByID`
18
+ - Consistent patterns within each language
19
+ - Abbreviations only when they are genuinely universal (`id`, `url`, `http`)
20
+ - Domain language where domain words exist
21
+
22
+ ## TypeScript and JavaScript
23
+
24
+ ### Variables and functions
25
+
26
+ `camelCase`:
27
+
28
+ ```typescript
29
+ const userName = 'alice';
30
+ function calculateTotal(items: Item[]): number { }
31
+ async function fetchUserProfile(userId: string): Promise<User> { }
32
+ ```
33
+
34
+ ### Types, interfaces, classes
35
+
36
+ `PascalCase`:
37
+
38
+ ```typescript
39
+ interface UserProfile { id: string; displayName: string; createdAt: Date; }
40
+ type RequestStatus = 'pending' | 'success' | 'error';
41
+ class PaymentProcessor { }
42
+ ```
43
+
44
+ ### Constants
45
+
46
+ `UPPER_SNAKE_CASE`:
47
+
48
+ ```typescript
49
+ const MAX_RETRY_ATTEMPTS = 3;
50
+ const API_BASE_URL = 'https://api.example.com';
51
+ ```
52
+
53
+ ### React components
54
+
55
+ `PascalCase` for components, `camelCase` for props:
56
+
57
+ ```typescript
58
+ interface ButtonProps { variant: 'primary' | 'secondary'; onClick: () => void; }
59
+ function ActionButton({ variant, onClick }: ButtonProps) { }
60
+ ```
61
+
62
+ ### Files and directories
63
+
64
+ - Lowercase `kebab-case`
65
+ - Tests end in `.test.ts`
66
+ - File names match the concept they export
67
+
68
+ ## SQL
69
+
70
+ ### Tables and columns
71
+
72
+ `snake_case`, tables in the singular:
73
+
74
+ ```sql
75
+ create table member (
76
+ id uuid primary key,
77
+ display_name text not null,
78
+ created_at timestamptz default now()
79
+ );
80
+ ```
81
+
82
+ Column suffixes:
83
+
84
+ - `id` — primary key
85
+ - `{referenced_table}_id` — foreign key
86
+ - `{field}_intl` — internationalised text (`jsonb`, keys are locale codes)
87
+ - `{event}_at` — timestamps
88
+ - `deleted_at` — soft-delete marker
89
+
90
+ ### Functions
91
+
92
+ Pattern: `{domain}_{verb}` or `{domain}_{verb}_{target}`
93
+
94
+ ```sql
95
+ create function util_generate_slug(_input text) ...
96
+ create function task_get_status(_task_id uuid) ...
97
+ ```
98
+
99
+ Standard verbs: `get`, `list`, `create`, `update`, `delete`, `check`, `is`, `has`, `set`, `generate`.
100
+
101
+ ### Parameters and variables
102
+
103
+ Prefix `_` for parameters, `v_` for local variables:
104
+
105
+ ```sql
106
+ create function process_order(_order_id uuid)
107
+ returns void
108
+ language plpgsql
109
+ as $func$
110
+ declare
111
+ v_total numeric;
112
+ begin
113
+ -- body
114
+ end;
115
+ $func$;
116
+ ```
117
+
118
+ ### Objects bound to a table
119
+
120
+ - Check constraints — `chk_{table}_{field}_{purpose}`
121
+ - Unique constraints — `uq_{table}_{field}`
122
+ - Indexes — `idx_{table}_{field}`; unique as `idx_unique_{table}_{field}`
123
+ - Triggers — `trg_{table}_{purpose}`
124
+ - RLS policies — `pol_{table}_{operation}`
125
+
126
+ ## CSS
127
+
128
+ `kebab-case`; BEM-style modifiers where useful:
129
+
130
+ ```css
131
+ .user-profile { }
132
+ .action-button--primary { }
133
+
134
+ :root {
135
+ --color-primary: #007bff;
136
+ --spacing-md: 1rem;
137
+ }
138
+ ```
139
+
140
+ ## Git
141
+
142
+ ### Branches
143
+
144
+ `kebab-case` with a type prefix:
145
+
146
+ ```bash
147
+ feature/user-authentication
148
+ fix/login-redirect-loop
149
+ docs/api-documentation
150
+ ```
151
+
152
+ ### Commit subjects
153
+
154
+ Conventional Commits, imperative mood — see [Commits](./commits.md).
155
+
156
+ ## Configuration
157
+
158
+ - JSON / JSONC keys — `camelCase`
159
+ - Environment variables — `UPPER_SNAKE_CASE`
160
+ - Package names — scoped, `kebab-case` (`@regardio/react`, `@regardio/ensemble-supabase`)
161
+
162
+ ```json
163
+ { "compilerOptions": { "strictNullChecks": true } }
164
+ ```
165
+
166
+ ```bash
167
+ DATABASE_URL=postgres://localhost:5432/mydb
168
+ NODE_ENV=production
169
+ ```
170
+
171
+ ## Related
172
+
173
+ - [Coding](./coding.md) — TypeScript and general patterns
174
+ - [SQL](./sql.md) — PostgreSQL naming, structure, and access
175
+ - [Commits](./commits.md) — Branch and commit naming
176
+ - [Writing](./writing.md) — Voice, tone, language
177
+
178
+ ---
179
+
180
+ **License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio
@@ -0,0 +1,84 @@
1
+ ---
2
+
3
+ title: "Principles"
4
+ description: "The shared ground Regardio projects stand on — six clusters of principles that carry across languages and repos."
5
+ publishedAt: 2026-04-17
6
+ order: 3
7
+ language: "en"
8
+ status: "published"
9
+ kind: "reference"
10
+ area: "dev"
11
+ ---
12
+
13
+ Regardio spans several codebases and several languages. What keeps them legible to each other is a short list of principles held in common — enough shared ground that a contributor moving between projects finds the same habits in force, and enough room left for each codebase to speak its own idiom.
14
+
15
+ Six clusters, a handful of items each.
16
+
17
+ ## Code quality
18
+
19
+ - Readable code over clever code
20
+ - Consistent naming within each language — `camelCase` in TypeScript, `snake_case` in SQL
21
+ - Small functions with a single responsibility
22
+ - Explicit choices over implicit defaults
23
+
24
+ ## Architecture
25
+
26
+ - Modules decouple from each other; dependencies are deliberate
27
+ - Duplication resolves into an abstraction only when the pattern is clear
28
+ - Interfaces describe what a module does, not how it does it
29
+ - Concerns separate along the seams the domain suggests
30
+
31
+ ## Error handling
32
+
33
+ - Input is validated early; failures surface with a clear message
34
+ - Validation covers correctness and security in the same pass
35
+ - Logic stays separated from side effects so it remains testable
36
+ - Dependencies can fail; the code degrades gracefully when they do
37
+
38
+ ## Performance
39
+
40
+ - Data structures match the shape of the work
41
+ - Measurement comes before optimisation
42
+ - Resources — memory, connections, file handles — are released on the path that acquired them
43
+ - Scale is considered at design time, not retrofitted
44
+
45
+ ## Security
46
+
47
+ - Client data is untrusted by default; server-side validation is the line
48
+ - Privileges stay narrow; access is opened deliberately
49
+ - Defence is layered; a single check never stands alone
50
+ - Openness is an opt-in, not the default posture
51
+
52
+ ## Maintainability
53
+
54
+ - Patterns repeat across the codebase so that reading one teaches reading the rest
55
+ - Commits are atomic and speak to one change at a time
56
+ - Refactoring is continuous; debt is paid down rather than accumulated
57
+
58
+ ## Implementation workflow
59
+
60
+ Non-trivial work tends to follow this sequence — not as a ritual, but because skipping a step usually costs more later than it saves now:
61
+
62
+ 1. **Understand the business logic first.** The deepest defects come from misunderstood requirements. Read the relevant domain document; know what is needed now rather than what might be needed later.
63
+ 2. **Look for existing solutions.** Well-maintained libraries are evaluated before custom code is written. Dependencies are vetted for design quality, test coverage, and recent activity.
64
+ 3. **Write the tests as specification.** The behaviour a change produces is described as tests before the change is written. Tests are the contract; the code is measured against them.
65
+ 4. **Implement with reusability in mind, not reusability as the goal.** Duplicate until the pattern is clear, then extract. Wrong abstractions cost more than duplication.
66
+ 5. **Stop and reconsider when complexity grows.** Difficulty that keeps growing despite good preparation is a signal. Back out, simplify, or question the approach.
67
+ 6. **Document intent, not mechanics.** Comments explain *why*; the code explains *what*. Existing context is checked before new prose is added.
68
+
69
+ ## Collaboration
70
+
71
+ - Every change passes through review
72
+ - Code quality is shared, not owned
73
+ - Decisions that involve a real trade-off leave a trace in the project's `docs/en` tree
74
+
75
+ ## Related
76
+
77
+ - [Coding](./coding.md) — TypeScript and general patterns
78
+ - [Testing](./testing.md) — Testing philosophy
79
+ - [Documentation Standard](./documentation.md) — How documents are shaped
80
+ - [Writing](./writing.md) — Voice, tone, language
81
+
82
+ ---
83
+
84
+ **License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio
@@ -0,0 +1,246 @@
1
+ ---
2
+
3
+ title: "React"
4
+ description: "Component, hook, state, and performance patterns the Regardio React apps hold to."
5
+ publishedAt: 2026-04-17
6
+ order: 6
7
+ language: "en"
8
+ status: "published"
9
+ kind: "reference"
10
+ area: "dev"
11
+ ---
12
+
13
+ Regardio's user-facing surfaces — the Instrument app, the channel apps, the Storybook-hosted component packages — are all React. They share a design system through `@regardio/react` and styling through `@regardio/tailwind`. A contributor moving between them finds the same component shapes and the same decisions about where state lives. This page names those shared patterns.
14
+
15
+ ## TypeScript in React
16
+
17
+ ### Types
18
+
19
+ - Explicit types on function parameters and public return values
20
+ - `interface` for object shapes; `type` for unions
21
+ - Generic constraints when a type carries across boundaries
22
+
23
+ ```typescript
24
+ interface UserData {
25
+ id: string;
26
+ email: string;
27
+ displayName: string;
28
+ }
29
+
30
+ interface Repository<T extends { id: string }> {
31
+ findById(id: string): Promise<T | null>;
32
+ create(data: Pick<T, Exclude<keyof T, 'id'>>): Promise<T>;
33
+ }
34
+ ```
35
+
36
+ ### Naming
37
+
38
+ - `camelCase` — variables, functions, methods
39
+ - `PascalCase` — types, interfaces, classes, components
40
+ - `UPPER_SNAKE_CASE` — constants
41
+
42
+ ### Error handling
43
+
44
+ Result types at API boundaries; specific error types for known failure modes:
45
+
46
+ ```typescript
47
+ type Result<T, E = Error> =
48
+ | { success: true; data: T }
49
+ | { success: false; error: E };
50
+
51
+ async function fetchUser(id: string): Promise<Result<User>> {
52
+ try {
53
+ const data = await api.getUser(id);
54
+ return { success: true, data };
55
+ } catch (error) {
56
+ return { success: false, error: error as Error };
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Components
62
+
63
+ ### Structure
64
+
65
+ - Functional components with hooks
66
+ - One responsibility per component
67
+ - Composition over inheritance
68
+ - Explicit props interface
69
+
70
+ ```typescript
71
+ interface CardProps {
72
+ title: string;
73
+ description: string;
74
+ onSelect: (id: string) => void;
75
+ isSelected?: boolean;
76
+ }
77
+
78
+ export function Card({ title, description, onSelect, isSelected = false }: CardProps) {
79
+ const handleClick = useCallback(() => onSelect(title), [title, onSelect]);
80
+ return (
81
+ <div className={cn('card', { selected: isSelected })} onClick={handleClick}>
82
+ <h3>{title}</h3>
83
+ <p>{description}</p>
84
+ </div>
85
+ );
86
+ }
87
+ ```
88
+
89
+ ### Categories
90
+
91
+ - **UI components** — pure, reusable, no business logic
92
+ - **Feature components** — domain logic, backend integration
93
+ - **Page components** — route-level; compose features
94
+
95
+ ### File layout
96
+
97
+ - `components/ui/` — reusable UI components
98
+ - `components/features/` — feature-specific components
99
+ - `hooks/` — custom hooks
100
+ - `types/` — shared types
101
+ - `utils/` — framework-agnostic helpers
102
+
103
+ ### Props
104
+
105
+ - Pass only what the component uses
106
+ - `useCallback` for function props that cross memoised boundaries
107
+ - Sensible defaults; undefined handled gracefully
108
+
109
+ ## Hooks
110
+
111
+ - Dependencies declared in full for `useEffect`, `useMemo`, `useCallback`
112
+ - Reusable logic extracts into a custom hook, name prefixed `use`
113
+ - Cleanup releases whatever the hook set up
114
+
115
+ ```typescript
116
+ function useWebSocket(url: string) {
117
+ const [data, setData] = useState<unknown>(null);
118
+ const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected'>('connecting');
119
+
120
+ useEffect(() => {
121
+ const ws = new WebSocket(url);
122
+ ws.onopen = () => setStatus('connected');
123
+ ws.onmessage = (event) => setData(JSON.parse(event.data));
124
+ ws.onclose = () => setStatus('disconnected');
125
+ return () => ws.close();
126
+ }, [url]);
127
+
128
+ return { data, status };
129
+ }
130
+ ```
131
+
132
+ ### Event handling
133
+
134
+ - Typed with the React event they receive
135
+ - Forms prevent default and read values from controlled inputs
136
+ - Keyboard handlers accompany mouse handlers
137
+
138
+ ```typescript
139
+ function SearchForm({ onSubmit }: { onSubmit: (query: string) => void }) {
140
+ const [query, setQuery] = useState('');
141
+
142
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
143
+ e.preventDefault();
144
+ onSubmit(query);
145
+ };
146
+
147
+ return (
148
+ <form onSubmit={handleSubmit}>
149
+ <input
150
+ value={query}
151
+ onChange={(e) => setQuery(e.target.value)}
152
+ onKeyDown={(e) => e.key === 'Escape' && setQuery('')}
153
+ aria-label="Search"
154
+ />
155
+ <button type="submit">Search</button>
156
+ </form>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ## State
162
+
163
+ ### Local
164
+
165
+ - `useState` for simple component state
166
+ - `useReducer` when the shape grows beyond two or three related pieces
167
+ - Custom hooks for reusable state logic
168
+
169
+ ```typescript
170
+ type State = { items: Item[]; filter: string; sortBy: 'name' | 'date'; loading: boolean };
171
+ type Action =
172
+ | { type: 'SET_ITEMS'; items: Item[] }
173
+ | { type: 'SET_FILTER'; filter: string }
174
+ | { type: 'SET_SORT'; sortBy: 'name' | 'date' }
175
+ | { type: 'SET_LOADING'; loading: boolean };
176
+
177
+ function reducer(state: State, action: Action): State {
178
+ switch (action.type) {
179
+ case 'SET_ITEMS': return { ...state, items: action.items };
180
+ case 'SET_FILTER': return { ...state, filter: action.filter };
181
+ case 'SET_SORT': return { ...state, sortBy: action.sortBy };
182
+ case 'SET_LOADING': return { ...state, loading: action.loading };
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### Global
188
+
189
+ - Context for theme, session, user preferences
190
+ - A dedicated store for application state that genuinely spans the app
191
+ - React Query (or equivalent) for server state
192
+
193
+ ## Performance
194
+
195
+ - Code-split by route and feature; lazy-load what isn't needed yet
196
+ - `React.memo`, `useMemo`, `useCallback` where profiling points at a win — not as defensive decoration
197
+ - Bundle size monitored
198
+
199
+ ```typescript
200
+ const Dashboard = lazy(() => import('./pages/Dashboard'));
201
+
202
+ function App() {
203
+ return (
204
+ <Suspense fallback={<LoadingSpinner />}>
205
+ <Routes>
206
+ <Route path="/dashboard" element={<Dashboard />} />
207
+ </Routes>
208
+ </Suspense>
209
+ );
210
+ }
211
+ ```
212
+
213
+ ## Testing
214
+
215
+ Components are tested through the interface a user reaches — roles, labels, visible text, keyboard. Implementation-detail assertions (internal state, render counts) do not appear.
216
+
217
+ ```typescript
218
+ describe('SearchForm', () => {
219
+ it('calls onSubmit with query when form is submitted', async () => {
220
+ const mockOnSubmit = vi.fn();
221
+ render(<SearchForm onSubmit={mockOnSubmit} />);
222
+ const input = screen.getByRole('textbox', { name: /search/i });
223
+ await user.type(input, 'test query');
224
+ await user.click(screen.getByRole('button', { name: /search/i }));
225
+ expect(mockOnSubmit).toHaveBeenCalledWith('test query');
226
+ });
227
+ });
228
+ ```
229
+
230
+ ### Accessibility
231
+
232
+ - Keyboard navigation verified
233
+ - Screen-reader access through roles and labels
234
+ - Focus management checked on interactive flows
235
+ - ARIA attributes correct where they carry meaning
236
+
237
+ ## Related
238
+
239
+ - [Coding](./coding.md) — TypeScript patterns React code builds on
240
+ - [Testing](./testing.md) — Testing philosophy and layers
241
+ - [Principles](./principles.md) — Shared principles across the stack
242
+ - [Naming](./naming.md) — Names across languages
243
+
244
+ ---
245
+
246
+ **License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio