@regardio/dev 2.4.1 → 2.6.0
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.
- package/README.md +1 -1
- package/dist/bin/ship/hotfix.bin.mjs +23 -25
- package/dist/bin/ship/production.bin.mjs +24 -26
- package/dist/bin/ship/staging.bin.mjs +1 -1
- package/dist/bin/ship/{utils-rnvdLrkF.mjs → utils-sL-BT4MX.mjs} +11 -3
- package/docs/en/README.md +11 -3
- package/docs/en/documentation.md +170 -0
- package/docs/en/principles.md +83 -0
- package/docs/en/tools/biome.md +6 -18
- package/docs/en/tools/husky.md +3 -6
- package/docs/en/tools/markdownlint.md +3 -12
- package/docs/en/tools/releases.md +137 -160
- package/docs/en/tools/vitest.md +1 -1
- package/docs/en/writing.md +118 -0
- package/package.json +3 -3
- package/templates/actions/forgejo-release.yml +116 -0
- package/templates/actions/github-release.yml +93 -0
- package/templates/agents/CLAUDE.md +37 -0
- package/templates/versionrc/.versionrc.json +42 -10
- package/templates/github/release.yml +0 -68
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Extend the shared configs:
|
|
|
47
47
|
|
|
48
48
|
## Documentation
|
|
49
49
|
|
|
50
|
-
Documentation sits alongside the code
|
|
50
|
+
Documentation sits alongside the code in `docs/en/`, organised by topic. It covers cross-project principles and writing standards as well as code-level standards and toolchain configuration.
|
|
51
51
|
|
|
52
52
|
### Code standards
|
|
53
53
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git, c as
|
|
2
|
+
import { a as git, c as runQualityChecks, i as getWorkspacePackages, n as chooseForEach, o as gitRead, s as runPackageRelease, t as branchExists } from "./utils-sL-BT4MX.mjs";
|
|
3
3
|
//#region src/bin/ship/hotfix.ts
|
|
4
4
|
/**
|
|
5
5
|
* ship-hotfix: Manage hotfix branches based on production code.
|
|
@@ -52,6 +52,8 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
52
52
|
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
+
console.log("\nFetching latest state from origin...");
|
|
56
|
+
git("fetch", "origin");
|
|
55
57
|
console.log("\nRunning quality checks...");
|
|
56
58
|
try {
|
|
57
59
|
runQualityChecks();
|
|
@@ -61,27 +63,21 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
61
63
|
}
|
|
62
64
|
console.log("✅ Quality checks passed");
|
|
63
65
|
const packages = getWorkspacePackages(cwd);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
runScript(`release:${highestBump}`);
|
|
80
|
-
} catch {
|
|
81
|
-
console.log("No release script found - skipping versioning (non-publishing repo)");
|
|
82
|
-
}
|
|
83
|
-
console.log("\nFetching latest state from origin...");
|
|
84
|
-
git("fetch", "origin");
|
|
66
|
+
const bumps = [];
|
|
67
|
+
if (packages.length > 0) {
|
|
68
|
+
console.log("\nSelect a version bump for each package (or skip):");
|
|
69
|
+
bumps.push(...chooseForEach(packages));
|
|
70
|
+
if (bumps.length === 0) {
|
|
71
|
+
console.log("\nNo packages selected for release. Aborted.");
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
console.log("\nBumping versions and updating CHANGELOGs...");
|
|
75
|
+
for (const { package: pkg, bump } of bumps) try {
|
|
76
|
+
runPackageRelease(pkg.path, bump);
|
|
77
|
+
} catch {
|
|
78
|
+
console.log(`No versioning configured for ${pkg.name} — skipping.`);
|
|
79
|
+
}
|
|
80
|
+
} else console.log("No packages configured for versioning — skipping.");
|
|
85
81
|
console.log("\nMerging hotfix into production...");
|
|
86
82
|
git("checkout", "production");
|
|
87
83
|
git("pull", "--ff-only", "origin", "production");
|
|
@@ -101,9 +97,11 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
101
97
|
try {
|
|
102
98
|
git("push", "origin", "--delete", currentBranch);
|
|
103
99
|
} catch {}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
if (bumps.length > 0) {
|
|
101
|
+
const shipped = bumps.map((b) => b.package.name).join(", ");
|
|
102
|
+
console.log(`\n✅ Hotfix ${shipped} shipped to production → staging → main.`);
|
|
103
|
+
console.log("CI will handle the rest.");
|
|
104
|
+
} else console.log("\n✅ Hotfix shipped to production → staging → main.");
|
|
107
105
|
console.log("You are on main and ready to keep working.");
|
|
108
106
|
process.exit(0);
|
|
109
107
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git, c as
|
|
2
|
+
import { a as git, c as runQualityChecks, i as getWorkspacePackages, n as chooseForEach, o as gitRead, r as confirm, s as runPackageRelease, t as branchExists } from "./utils-sL-BT4MX.mjs";
|
|
3
3
|
//#region src/bin/ship/production.ts
|
|
4
4
|
/**
|
|
5
5
|
* ship-production: Promote main to production following the GitLab workflow.
|
|
@@ -51,29 +51,25 @@ function runShipProduction(cwd = process.cwd()) {
|
|
|
51
51
|
}
|
|
52
52
|
console.log("✅ Quality checks passed");
|
|
53
53
|
const packages = getWorkspacePackages(cwd);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
runScript(`release:${highestBump}`);
|
|
74
|
-
} catch {
|
|
75
|
-
console.log("No release script found - skipping versioning (non-publishing repo)");
|
|
76
|
-
}
|
|
54
|
+
const bumps = [];
|
|
55
|
+
if (packages.length > 0) {
|
|
56
|
+
console.log("\nSelect a version bump for each package (or skip):");
|
|
57
|
+
bumps.push(...chooseForEach(packages));
|
|
58
|
+
if (bumps.length === 0) {
|
|
59
|
+
console.log("\nNo packages selected for release. Aborted.");
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
if (!confirm(`\nShip to production?\n${bumps.map((b) => ` ${b.package.name} → ${b.bump}`).join("\n")}\n`)) {
|
|
63
|
+
console.log("Aborted.");
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
console.log("\nBumping versions and updating CHANGELOGs...");
|
|
67
|
+
for (const { package: pkg, bump } of bumps) try {
|
|
68
|
+
runPackageRelease(pkg.path, bump);
|
|
69
|
+
} catch {
|
|
70
|
+
console.log(`No versioning configured for ${pkg.name} — skipping.`);
|
|
71
|
+
}
|
|
72
|
+
} else if (!confirm("\nNo packages configured for versioning — ship commits to production?\n")) process.exit(0);
|
|
77
73
|
console.log("\nMerging main into production...");
|
|
78
74
|
git("checkout", "production");
|
|
79
75
|
git("pull", "--ff-only", "origin", "production");
|
|
@@ -86,8 +82,10 @@ function runShipProduction(cwd = process.cwd()) {
|
|
|
86
82
|
git("push", "origin", "staging");
|
|
87
83
|
git("checkout", "main");
|
|
88
84
|
git("push", "--follow-tags", "origin", "main");
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
if (bumps.length > 0) {
|
|
86
|
+
const shipped = bumps.map((b) => b.package.name).join(", ");
|
|
87
|
+
console.log(`\n✅ Shipped ${shipped} to production. CI will handle the rest.`);
|
|
88
|
+
} else console.log("\n✅ Commits shipped to production.");
|
|
91
89
|
console.log("You are on main and ready to keep working.");
|
|
92
90
|
}
|
|
93
91
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git,
|
|
2
|
+
import { a as git, c as runQualityChecks, o as gitRead, t as branchExists } from "./utils-sL-BT4MX.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/bin/ship/staging.ts
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFileSync, execSync } from "node:child_process";
|
|
3
|
-
import { closeSync, openSync, readSync } from "node:fs";
|
|
3
|
+
import { closeSync, existsSync, openSync, readSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
4
5
|
//#region src/bin/ship/utils.ts
|
|
5
6
|
/**
|
|
6
7
|
* Shared utilities for ship-staging, ship-production, and ship-hotfix.
|
|
@@ -19,6 +20,13 @@ const runScript = (script) => {
|
|
|
19
20
|
console.log(`$ pnpm ${script}`);
|
|
20
21
|
execSync(`pnpm ${script}`, { stdio: "inherit" });
|
|
21
22
|
};
|
|
23
|
+
const runPackageRelease = (pkgPath, bump) => {
|
|
24
|
+
console.log(`$ commit-and-tag-version --release-as ${bump}`);
|
|
25
|
+
execSync(`pnpm exec commit-and-tag-version --release-as ${bump}`, {
|
|
26
|
+
cwd: pkgPath,
|
|
27
|
+
stdio: "inherit"
|
|
28
|
+
});
|
|
29
|
+
};
|
|
22
30
|
const runQualityChecks = () => {
|
|
23
31
|
runScript("lint");
|
|
24
32
|
runScript("typecheck");
|
|
@@ -35,7 +43,7 @@ const getWorkspacePackages = (cwd = process.cwd()) => {
|
|
|
35
43
|
cwd,
|
|
36
44
|
encoding: "utf-8"
|
|
37
45
|
});
|
|
38
|
-
return JSON.parse(raw).filter((p) => p.
|
|
46
|
+
return JSON.parse(raw).filter((p) => p.name !== void 0 && existsSync(join(p.path, ".versionrc.json"))).map(({ name, path }) => ({
|
|
39
47
|
name,
|
|
40
48
|
path
|
|
41
49
|
}));
|
|
@@ -120,4 +128,4 @@ const choose = (prompt, options, ttyPath = "/dev/tty") => {
|
|
|
120
128
|
return chosen.value;
|
|
121
129
|
};
|
|
122
130
|
//#endregion
|
|
123
|
-
export { git as a,
|
|
131
|
+
export { git as a, runQualityChecks as c, getWorkspacePackages as i, chooseForEach as n, gitRead as o, confirm as r, runPackageRelease as s, branchExists as t };
|
package/docs/en/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
|
|
3
3
|
title: "@regardio/dev Documentation"
|
|
4
|
-
description: "Index for the @regardio/dev toolchain — code-level standards and the tools that enforce them."
|
|
4
|
+
description: "Index for the @regardio/dev toolchain — principles, code-level standards, and the tools that enforce them."
|
|
5
5
|
publishedAt: 2026-04-17
|
|
6
6
|
language: "en"
|
|
7
7
|
status: "published"
|
|
@@ -9,9 +9,17 @@ status: "published"
|
|
|
9
9
|
|
|
10
10
|
# Documentation
|
|
11
11
|
|
|
12
|
-
Index for the `@regardio/dev` toolchain.
|
|
12
|
+
Index for the `@regardio/dev` toolchain. All Regardio standards ship with this package and are available at `node_modules/@regardio/dev/docs/en/` after install.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Principles & writing
|
|
15
|
+
|
|
16
|
+
Cross-project standards that carry across every Regardio codebase, regardless of language or framework:
|
|
17
|
+
|
|
18
|
+
| Document | Description |
|
|
19
|
+
|----------|-------------|
|
|
20
|
+
| [Principles](./principles.md) | Code quality, architecture, security, maintainability, collaboration, implementation workflow |
|
|
21
|
+
| [Writing](./writing.md) | Voice, tone, and language — English and German |
|
|
22
|
+
| [Documentation](./documentation.md) | Shape of documents, frontmatter, cross-references, tense and stance |
|
|
15
23
|
|
|
16
24
|
## Code standards
|
|
17
25
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
title: "Documentation"
|
|
4
|
+
description: "How Regardio shapes its documents — a thin shared surface with room for each document to take the form its content asks for."
|
|
5
|
+
publishedAt: 2026-04-17
|
|
6
|
+
order: 1
|
|
7
|
+
language: "en"
|
|
8
|
+
status: "published"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
A Regardio document is read for answers about the system, for judgement about what the project does and why, and as the specification its tests are measured against. These readings 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.
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
Every Regardio document carries a small shared surface. Underneath, it takes whichever of a few shapes fits the content it carries.
|
|
20
|
+
|
|
21
|
+
### Shared surface
|
|
22
|
+
|
|
23
|
+
Every document has the same top:
|
|
24
|
+
|
|
25
|
+
- **Frontmatter** that identifies the document and lets tooling index it
|
|
26
|
+
- **A title** as a heading or implicit from frontmatter
|
|
27
|
+
- **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
|
|
28
|
+
|
|
29
|
+
Every document has the same bottom:
|
|
30
|
+
|
|
31
|
+
- **Cross-references** to documents the reader is likely to want next, either inline in the prose or collected under `## Related` at the end
|
|
32
|
+
|
|
33
|
+
Between the top and the bottom, the document takes the shape its content asks for.
|
|
34
|
+
|
|
35
|
+
### Shapes the body takes
|
|
36
|
+
|
|
37
|
+
A few shapes recur. Pick the one the content fits; do not pad the content into a shape it resists.
|
|
38
|
+
|
|
39
|
+
**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.
|
|
40
|
+
|
|
41
|
+
**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.
|
|
42
|
+
|
|
43
|
+
**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.
|
|
44
|
+
|
|
45
|
+
**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.
|
|
46
|
+
|
|
47
|
+
**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.
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
### Frontmatter
|
|
52
|
+
|
|
53
|
+
Frontmatter is the part tooling reads. Keep it stable.
|
|
54
|
+
|
|
55
|
+
| Field | Required | Notes |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `title` | yes | Noun phrase naming the document. Decision records may prefix `"ADR: "`. |
|
|
58
|
+
| `description` | yes | One sentence. What this document is for, without hedging. |
|
|
59
|
+
| `publishedAt` | yes | ISO date (`YYYY-MM-DD`) the document was first accepted. |
|
|
60
|
+
| `status` | yes | `"draft"`, `"published"`, `"superseded"`. |
|
|
61
|
+
| `language` | yes | `"en"`, `"de"`. |
|
|
62
|
+
| `order` | no | Integer, when a sibling set has a meaningful reading order. |
|
|
63
|
+
| `kind` | no | `"adr"`, `"entity"`, `"concept"`, `"architecture"`, `"guide"`, `"use-case"`, `"reference"`. Lets renderers pick the right treatment. |
|
|
64
|
+
| `area` | no | Names the implementation the document belongs to. |
|
|
65
|
+
| `supersedes` | no | Filename (without extension) of the document this one replaces. |
|
|
66
|
+
| `supersededBy` | no | Filename of the document that replaced this one. |
|
|
67
|
+
|
|
68
|
+
### Tense and stance
|
|
69
|
+
|
|
70
|
+
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.
|
|
71
|
+
|
|
72
|
+
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.
|
|
73
|
+
|
|
74
|
+
### What shows up where
|
|
75
|
+
|
|
76
|
+
- **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.
|
|
77
|
+
- **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.
|
|
78
|
+
- **Procedural steps** belong in runbooks and READMEs. A domain spec does not read like a cookbook.
|
|
79
|
+
|
|
80
|
+
### Cross-references
|
|
81
|
+
|
|
82
|
+
Links carry a short descriptor of what the link leads to, not just a filename:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
The [Channel](../entities/channel.md) is the publication destination;
|
|
86
|
+
the [Publishing Architecture](../architecture/publishing-architecture.md)
|
|
87
|
+
describes how callers reach it.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`## Related` at the end of a document lists the next pages a reader is likely to want.
|
|
91
|
+
|
|
92
|
+
### Voice
|
|
93
|
+
|
|
94
|
+
The [Writing](./writing.md) standard covers voice, tone, and language. This document relies on it rather than repeating it.
|
|
95
|
+
|
|
96
|
+
## Alternatives Considered
|
|
97
|
+
|
|
98
|
+
### A single ADR skeleton for every document
|
|
99
|
+
|
|
100
|
+
**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.
|
|
101
|
+
|
|
102
|
+
### No shared shape at all
|
|
103
|
+
|
|
104
|
+
**Dismissed because** indexing and cross-referencing need something predictable to bind to, 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.
|
|
105
|
+
|
|
106
|
+
### Separate templates per document kind
|
|
107
|
+
|
|
108
|
+
**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.
|
|
109
|
+
|
|
110
|
+
## Operational Rules
|
|
111
|
+
|
|
112
|
+
### Frontmatter is complete
|
|
113
|
+
|
|
114
|
+
Every document carries `title`, `description`, `publishedAt`, `status`, and `language`. Tooling that indexes or lists documents relies on these five.
|
|
115
|
+
|
|
116
|
+
### Opening names the subject
|
|
117
|
+
|
|
118
|
+
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.
|
|
119
|
+
|
|
120
|
+
### Shape follows content
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
### Tense is present, stance is observational
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
### Reasoning is preserved, not rewritten
|
|
129
|
+
|
|
130
|
+
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.
|
|
131
|
+
|
|
132
|
+
### Code and names earn their place
|
|
133
|
+
|
|
134
|
+
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.
|
|
135
|
+
|
|
136
|
+
### Tests can point at a document
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
### One subject per document
|
|
141
|
+
|
|
142
|
+
A document names one entity, one concept, one decision, one scenario. When a draft sprawls across several, the move is to split it.
|
|
143
|
+
|
|
144
|
+
## Consequences
|
|
145
|
+
|
|
146
|
+
### Positive
|
|
147
|
+
|
|
148
|
+
- Documents read naturally for their content. ADRs feel like ADRs; reference catalogues feel like reference catalogues; introductions stay short.
|
|
149
|
+
- Readers find a predictable frontmatter and opening, and the body of each document carries its reasoning in the form that fits.
|
|
150
|
+
- The docs stay honest. Observational prose about what the system does leaves room for the reader to form a judgement, rather than prescribing one.
|
|
151
|
+
- Tests bind to the prose that describes behaviour, wherever in the document that prose lives.
|
|
152
|
+
|
|
153
|
+
### Negative
|
|
154
|
+
|
|
155
|
+
- "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.
|
|
156
|
+
- 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.
|
|
157
|
+
|
|
158
|
+
### Mitigations
|
|
159
|
+
|
|
160
|
+
- 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.
|
|
161
|
+
- 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.
|
|
162
|
+
|
|
163
|
+
## Related
|
|
164
|
+
|
|
165
|
+
- [Writing](./writing.md) — Voice, tone, language
|
|
166
|
+
- [Principles](./principles.md) — Shared principles
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
**License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
title: "Principles"
|
|
4
|
+
description: "The shared ground Regardio projects stand on — 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
|
+
---
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
Seven clusters, a handful of items each.
|
|
15
|
+
|
|
16
|
+
## Code quality
|
|
17
|
+
|
|
18
|
+
- Readable code over clever code
|
|
19
|
+
- Consistent naming within each language — `camelCase` in TypeScript, `snake_case` in SQL
|
|
20
|
+
- Small functions with a single responsibility
|
|
21
|
+
- Explicit choices over implicit defaults
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
- Modules decouple from each other; dependencies are deliberate
|
|
26
|
+
- Duplication resolves into an abstraction only when the pattern is clear
|
|
27
|
+
- Interfaces describe what a module does, not how it does it
|
|
28
|
+
- Concerns separate along the seams the domain suggests
|
|
29
|
+
|
|
30
|
+
## Error handling
|
|
31
|
+
|
|
32
|
+
- Input is validated early; failures surface with a clear message
|
|
33
|
+
- Validation covers correctness and security in the same pass
|
|
34
|
+
- Logic stays separated from side effects so it remains testable
|
|
35
|
+
- Dependencies can fail; the code degrades gracefully when they do
|
|
36
|
+
|
|
37
|
+
## Performance
|
|
38
|
+
|
|
39
|
+
- Data structures match the shape of the work
|
|
40
|
+
- Measurement comes before optimisation
|
|
41
|
+
- Memory, connections, and file handles are released on the path that acquired them
|
|
42
|
+
- Scale is considered at design time, not retrofitted
|
|
43
|
+
|
|
44
|
+
## Security
|
|
45
|
+
|
|
46
|
+
- Client data is untrusted by default; server-side validation is the line
|
|
47
|
+
- Privileges stay narrow; access is opened deliberately
|
|
48
|
+
- Defence is layered; a single check never stands alone
|
|
49
|
+
- Openness is an opt-in, not the default posture
|
|
50
|
+
|
|
51
|
+
## Maintainability
|
|
52
|
+
|
|
53
|
+
- Patterns repeat across the codebase so that reading one teaches reading the rest
|
|
54
|
+
- Commits are atomic and speak to one change at a time
|
|
55
|
+
- Refactoring is continuous; debt is paid down rather than accumulated
|
|
56
|
+
|
|
57
|
+
## Collaboration
|
|
58
|
+
|
|
59
|
+
- Every change passes through review
|
|
60
|
+
- Code quality is shared, not owned
|
|
61
|
+
- Decisions that involve a real trade-off leave a trace in the project's `docs/en` tree
|
|
62
|
+
|
|
63
|
+
## Implementation workflow
|
|
64
|
+
|
|
65
|
+
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:
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
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.
|
|
69
|
+
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.
|
|
70
|
+
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.
|
|
71
|
+
5. **Stop and reconsider when complexity grows.** Difficulty that keeps growing despite good preparation is a signal. Back out, simplify, or question the approach.
|
|
72
|
+
6. **Document intent, not mechanics.** Comments explain *why*; the code explains *what*. Existing context is checked before new prose is added.
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- [Writing](./writing.md) — Voice, tone, language
|
|
77
|
+
- [Documentation](./documentation.md) — How documents are shaped
|
|
78
|
+
- [Coding](./standards/coding.md) — TypeScript and general patterns
|
|
79
|
+
- [Testing](./standards/testing.md) — Testing philosophy
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
**License**: [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) © Regardio
|
package/docs/en/tools/biome.md
CHANGED
|
@@ -11,7 +11,7 @@ area: "dev"
|
|
|
11
11
|
|
|
12
12
|
# Biome
|
|
13
13
|
|
|
14
|
-
[Biome](https://biomejs.dev/) is the linter and formatter every Regardio package reaches for. Configuration is centralised through `@regardio/dev
|
|
14
|
+
[Biome](https://biomejs.dev/) is the linter and formatter every Regardio package reaches for. Configuration is centralised through `@regardio/dev`.
|
|
15
15
|
|
|
16
16
|
## Configuration
|
|
17
17
|
|
|
@@ -29,34 +29,22 @@ A `biome.jsonc` at the package root is all it takes:
|
|
|
29
29
|
```json
|
|
30
30
|
{
|
|
31
31
|
"scripts": {
|
|
32
|
-
"fix": "
|
|
33
|
-
"
|
|
34
|
-
"lint": "exec-s lint:md lint:biome",
|
|
35
|
-
"lint:biome": "lint-biome check ."
|
|
32
|
+
"fix:biome": "biome check --write --unsafe .",
|
|
33
|
+
"lint:biome": "biome check ."
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
```
|
|
39
37
|
|
|
40
|
-
## CLI wrapper
|
|
41
|
-
|
|
42
|
-
Use `lint-biome` rather than `biome` directly — the wrapper keeps behaviour consistent across the monorepo.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
lint-biome check . # Check for issues
|
|
46
|
-
lint-biome check --write . # Fix the auto-fixable
|
|
47
|
-
lint-biome format . # Format only
|
|
48
|
-
```
|
|
49
|
-
|
|
50
38
|
## `package.json` handling
|
|
51
39
|
|
|
52
|
-
The Biome preset leaves `package.json` alone. For those files, `
|
|
40
|
+
The Biome preset leaves `package.json` alone. For those files, use `sort-package-json`:
|
|
53
41
|
|
|
54
42
|
1. Sorts `package.json` using the well-known field order
|
|
55
43
|
2. Fixes export-condition order (`types` before `default` for TypeScript)
|
|
56
44
|
|
|
57
45
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
pnpm sort-package-json # Sort package.json in current directory
|
|
47
|
+
pnpm sort-package-json path/to/pkg # Sort a specific package.json
|
|
60
48
|
```
|
|
61
49
|
|
|
62
50
|
It runs automatically as part of `pnpm fix`.
|
package/docs/en/tools/husky.md
CHANGED
|
@@ -20,7 +20,7 @@ Husky configures itself through the `prepare` script:
|
|
|
20
20
|
```json
|
|
21
21
|
{
|
|
22
22
|
"scripts": {
|
|
23
|
-
"prepare": "
|
|
23
|
+
"prepare": "husky"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
```
|
|
@@ -35,7 +35,8 @@ Validates commit messages against the conventional-commit format:
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
#!/bin/sh
|
|
38
|
-
|
|
38
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
39
|
+
commitlint --edit $1
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
### `pre-commit` (optional)
|
|
@@ -47,10 +48,6 @@ Runs linting before a commit lands:
|
|
|
47
48
|
pnpm lint
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
## CLI wrapper
|
|
51
|
-
|
|
52
|
-
`exec-husky` takes the place of `husky` directly — the wrapper handles monorepo scenarios that bare Husky doesn't.
|
|
53
|
-
|
|
54
51
|
## Bypassing hooks
|
|
55
52
|
|
|
56
53
|
In the rare case where a hook truly has to be skipped:
|
|
@@ -11,7 +11,7 @@ area: "dev"
|
|
|
11
11
|
|
|
12
12
|
# Markdownlint
|
|
13
13
|
|
|
14
|
-
Markdownlint checks Markdown structure and formatting. The rules live in `@regardio/dev
|
|
14
|
+
Markdownlint checks Markdown structure and formatting. The rules live in `@regardio/dev`.
|
|
15
15
|
|
|
16
16
|
## Configuration
|
|
17
17
|
|
|
@@ -28,21 +28,12 @@ A `.markdownlint.json` at the package root:
|
|
|
28
28
|
```json
|
|
29
29
|
{
|
|
30
30
|
"scripts": {
|
|
31
|
-
"fix:md": "
|
|
32
|
-
"lint:md": "
|
|
31
|
+
"fix:md": "markdownlint-cli2 --fix \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\"",
|
|
32
|
+
"lint:md": "markdownlint-cli2 \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\""
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
## CLI wrapper
|
|
38
|
-
|
|
39
|
-
Use `lint-md` rather than `markdownlint-cli2` directly:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
lint-md "**/*.md" # Check all Markdown files
|
|
43
|
-
lint-md --fix "**/*.md" # Auto-fix what can be auto-fixed
|
|
44
|
-
```
|
|
45
|
-
|
|
46
37
|
## Key rules
|
|
47
38
|
|
|
48
39
|
| Rule | Description |
|