@cratis/pi 7.88.0 → 7.90.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/package/corpus/harnesses/copilot/instructions/ai-distribution.instructions.md +3 -4
- package/package/corpus/harnesses/copilot/instructions/authoring-skills.instructions.md +3 -1
- package/package/corpus/harnesses/copilot/instructions/frontend-quality.instructions.md +1 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +22 -0
- package/package/corpus/harnesses/copilot/instructions/reactors.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +7 -0
- package/package/corpus/mcp-servers.json +8 -0
- package/package/corpus/rules/ai-distribution.md +3 -4
- package/package/corpus/rules/authoring-skills.md +3 -1
- package/package/corpus/rules/frontend-quality.md +1 -0
- package/package/corpus/rules/general.md +2 -4
- package/package/corpus/rules/profiles.md +22 -0
- package/package/corpus/rules/reactors.md +2 -0
- package/package/corpus/rules/vertical-slices.md +7 -0
- package/package/corpus/skills/cratis-engineering-regression-cluster/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-regression-cluster/SKILL.md +73 -0
- package/package/profile-catalog.json +8 -0
- package/package.json +1 -1
|
@@ -90,10 +90,9 @@ Cratis behavior. The `engineering-` prefix identifies the maintainer audience;
|
|
|
90
90
|
it does not imply confidential package contents or a private registry.
|
|
91
91
|
|
|
92
92
|
The consuming repository owns its project facts, confidential behavior, local
|
|
93
|
-
skills, and minimal host bootstraps.
|
|
94
|
-
|
|
95
|
-
repository-specific local workflows
|
|
96
|
-
documented legacy context fallback. Never merge, overwrite, or remove these
|
|
93
|
+
skills, and minimal host bootstraps. Project-specific context is the repository's own
|
|
94
|
+
documentation plus `.cratis/ai/rules/project.md`; `.agents/skills/` holds private or
|
|
95
|
+
repository-specific local workflows. Never merge, overwrite, or remove these
|
|
97
96
|
local files as a side effect of installing, updating, rolling back, or
|
|
98
97
|
uninstalling shared AI capabilities.
|
|
99
98
|
|
|
@@ -85,7 +85,9 @@ When changing a description, run trigger evaluations before and after when autho
|
|
|
85
85
|
|
|
86
86
|
## 11. Always-on content
|
|
87
87
|
|
|
88
|
-
Keep `general.md` to facts needed in every session, at most 200 lines. Warn above 24,000 bytes and above 91,000 combined bytes for rules without `paths`; line length alone can hide context bloat. Review globally applied Copilot instructions above 4,000 characters. These budgets leave room for source and task context, rather than promising a token count.
|
|
88
|
+
Keep `general.md` to facts needed in every session, at most 200 lines. Warn above 24,000 bytes and above 91,000 combined bytes for rules without `paths`; line length alone can hide context bloat. Review globally applied Copilot instructions above 4,000 characters. These budgets leave room for source and task context, rather than promising a token count. The corpus verification caps the rule bytes each harness loads at session start and fails above a cap: displace or demote text to an on-demand skill, or raise the cap in a reviewed change.
|
|
89
|
+
|
|
90
|
+
Every proposed rule names its enforcement: analyzer-enforceable, CI-enforceable or judgment-shaped. The first two ship as the analyzer, lint or CI check, with at most a one-line pointer in prose; only judgment-shaped rules are written out, because prose does not enforce itself and costs context in every session that loads it.
|
|
89
91
|
|
|
90
92
|
Use path-scoped rules for file-specific invariants and skills for particular operations. Hooks and CI enforce mechanical constraints; natural-language rules do not replace permission controls.
|
|
91
93
|
|
|
@@ -36,6 +36,7 @@ Stop-and-fix signals during implementation and review:
|
|
|
36
36
|
| A reusable component reaches into domain strings, generated proxies, identity, query, command, or navigation | It is no longer a primitive | Move it to the slice, or make it explicitly Cratis-aware and test/story it as such |
|
|
37
37
|
| Class/style string copied 3× or grown hard to scan | Styling changes become search-and-replace bugs | Extract to a shared style constant or a focused child component |
|
|
38
38
|
| `style={{ ... }}` with static token names | Bypasses theming and review | Move to `className`; keep `style` only for runtime values |
|
|
39
|
+
| A domain constant, threshold or eligibility rule in a `.ts`/`.tsx` file (`canApprove`, `maxAttempts = 2`) deciding whether an action shows | The client re-derives what the server knows, so the two drift and the UI breaks silently | Project the affordance as a read-model property server-side and render it. Judgment-shaped; no gate |
|
|
39
40
|
|
|
40
41
|
## Definition of done for a frontend change
|
|
41
42
|
|
|
@@ -200,6 +200,7 @@ language policy always follows the consuming repository's `go.mod` minimum.
|
|
|
200
200
|
| `cratis/content` | Release notes, social feed posts, voice review, content length, and developer marketing and distribution |
|
|
201
201
|
| `cratis/review` | Code review, performance, security |
|
|
202
202
|
| `cratis/codescene` | Opt-in CodeScene Code Health change gates, refactoring, debt prioritization and risk-based testing; requires an account and available MCP tools; each user registers the MCP server (see below) |
|
|
203
|
+
| `cratis/playwright` | Opt-in Playwright browser automation MCP server declaration for exploratory checks; carries no skills or rules and each user registers the server (see below) |
|
|
203
204
|
| `cratis/studio` | Studio MCP safety guidance |
|
|
204
205
|
| `cratis/cli` | CLI operations |
|
|
205
206
|
| `cratis/cli/terminal-workbench` | Human-operated terminal Workbench |
|
|
@@ -227,6 +228,27 @@ CodeScene needs an account and OAuth `login`; without an account or available
|
|
|
227
228
|
CodeScene tools, skip its steps and say so rather than blocking contributors.
|
|
228
229
|
The MCP catalog has no install-hints field and does not install the executable.
|
|
229
230
|
|
|
231
|
+
### Browser automation with Playwright
|
|
232
|
+
|
|
233
|
+
Select `cratis/playwright` explicitly to declare the `playwright` stdio server
|
|
234
|
+
(`npx -y @playwright/mcp@0.0.83 --headless --isolated`) in `mcp-servers.json`.
|
|
235
|
+
The flags keep the browser headless and isolated: no persistent profile, so no
|
|
236
|
+
personal cookies or logins are reused. The profile carries no skills or rules, and
|
|
237
|
+
no other profile composes it.
|
|
238
|
+
|
|
239
|
+
Today `cratis ai install` registers only Screenplay and Pi's `cratis-mcp` extension
|
|
240
|
+
loads only Screenplay, so Playwright, like CodeScene, is not registered for you.
|
|
241
|
+
Each user registers it in their own user-level MCP configuration, never in committed
|
|
242
|
+
project files. For Claude Code:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
claude mcp add --scope user playwright -- npx -y @playwright/mcp@0.0.83 --headless --isolated
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Pi users register it through a Pi MCP adapter or extension of their choice. Use it
|
|
249
|
+
for exploratory browser checks; repeatable checks stay in the repository's own
|
|
250
|
+
Playwright tests. Without the server registered, skip browser steps and say so.
|
|
251
|
+
|
|
230
252
|
### Event modeling with Screenplay
|
|
231
253
|
|
|
232
254
|
**Model first.** Decide opt-in per `general.md` "Model first"; where `cratis-screenplay-modeling-lifecycle` is installed, run its `scripts/model-first-status.mjs` for the decision, model root and drafts. An accepted model under the root covering the scope is the contract and the source
|
|
@@ -228,6 +228,8 @@ findings). Each reactor class should have a focused purpose. Multiple handler me
|
|
|
228
228
|
7. **No state** — Reactors should be stateless. Inject dependencies via primary constructor, but do not store mutable state on the class.
|
|
229
229
|
8. **A new side-effect reactor must not act on history.** A reactor that is new in a release (or has a new id) catches up from the start of the event log when it first registers, and `[OnceOnly]` does not cover that first catch-up because it is not a replay. If its handlers cause side effects (jobs, AI calls, external calls, notifications), start the observer at the tail on first registration or make the handler ignore facts that predate its registration, and specify that history causes nothing.
|
|
230
230
|
9. **An unattended, irreversible pass reads the log, not the sink — and carries a fuse.** A reactor that deletes, revokes, bills or notifies *because a read model says something is absent* is trusting a materialized sink that is only as current as its observer: a paused partition, a replay in progress or a lagging projection makes "absent" true of the sink and false of the world. Decide from a **`[Passive]`** read model (computed on demand from the events at the moment the reactor runs) or from the events themselves. And the code shape cannot tell an **empty subject set** ("nothing to consider" — fail-safe) from an **empty qualifying set** ("nothing survived the filter" — fail-destructive: every subject is about to be acted on), so an unattended pass states its expected population, refuses when the count is implausible, and caps how many subjects one run may touch — the same discipline as [guards-and-fuses.md](./guards-and-fuses.instructions.md), applied to a reactor. A sweep that decides from a read model also refuses while that model's observer is replaying or behind, because a rebuilding model reads as "nothing was ever processed"; specify the replaying case.
|
|
231
|
+
10. **Provider clients stay behind a translation or adapter reactor.** A `[Command]` or `IReactor` in a domain slice that injects or imports a provider client (GitHub, payment, mail SDK) is a finding: put the client behind one adapter slice and let domain slices work with our events and commands. Judgment-shaped today; a candidate for a namespace-boundary analyzer.
|
|
232
|
+
11. **A reactor must not react to its own consequences.** A reactor that posts comments or creates artifacts must guard against re-triggering on what it created (marker, author check, or event-type filter), or one input loops. Judgment-shaped; no gate.
|
|
231
233
|
|
|
232
234
|
## Failure transitions **[convention]**
|
|
233
235
|
|
|
@@ -245,6 +245,7 @@ Apply authorization attributes on the `[Command]` record or query method. **[con
|
|
|
245
245
|
- **Events are not read models** — never append events for totals, counts, statuses, search indexes, or dashboards. Project those from source events into read models.
|
|
246
246
|
- **Never duplicate one fact as an aggregate event *plus* a per-item fanout.** If a command fans out per item (one `RequestSentToPartner` per partner), don't also emit an aggregate event carrying the whole list. Keep the fanout as the single source of truth and project any aggregate from it; pick per-item vs. single-with-list event from how consumers read it — never both.
|
|
247
247
|
- **Command rejection normally appends no event.** Validation and business-rule rejections are command results, not facts. Add a rejection/failed event only when the business must audit or react to that failure later.
|
|
248
|
+
- **A settings event records the decision, not the whole blob.** Split composite configuration into one event per concern; a whole-object `…Set` event is last-writer-wins CRUD and turns every new field into a schema change. The tell is a `…Set`/`…Updated` event carrying a full settings record. Judgment-shaped; no gate.
|
|
248
249
|
- **One event name = one meaning.** Don't reuse a vague event for several domain facts; if facts can occur, be absent, or have consumers independently, split them into separate event types.
|
|
249
250
|
- **Never persist clock-relative state.** "Overdue", "expired", "due in 3 days" are true *now* and false later, and a projection only moves when an event arrives — so a projected `IsOverdue` is wrong the moment the clock crosses the line with no event to flip it. Store the **deadline** (a `DateOnly`/`DateTimeOffset` the business supplied) and compute the comparison at the query or UI edge, refreshing when time alone changes the answer. Only when *crossing* the deadline is itself a business fact (a fee is charged, a reminder goes out) does a scheduled process append an event for it.
|
|
250
251
|
|
|
@@ -308,6 +309,8 @@ query reference for versioned behavior and composition trade-offs.
|
|
|
308
309
|
|
|
309
310
|
Ladder, first that fits: (1) direct read-model injection — guarded under read protection `On`, advisory under `Off`; keyed by the command's own event-source id, or by `[LookUpBy]`; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
|
|
310
311
|
|
|
312
|
+
**[convention] One read model per consumer scenario, and dispatch never reads a UI model.** When a second consumer needs different semantics (board vs list vs count vs control flow), add a projection instead of extending the existing model; a command, reactor or job deciding what to do reads a model built for that decision, never a screen's listing model. The tell is a read model many slices import or one that projects many unrelated slices. Judgment-shaped; no gate.
|
|
313
|
+
|
|
311
314
|
**[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
|
|
312
315
|
compute a passive read model; any leftover documents are not maintained state.
|
|
313
316
|
Read through Chronicle by key or supported injection. **[convention]** Never
|
|
@@ -420,6 +423,10 @@ Redaction is a system-event request that removes payload availability from the e
|
|
|
420
423
|
|
|
421
424
|
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint, see the `cratis-chronicle-decision-reads` skill's `references/choosing-how-a-rule-holds.md`), or react to its events. **Never** instantiate or DI another slice's command/handler/service.
|
|
422
425
|
|
|
426
|
+
**[convention] Prompts are content.** In an AI-backed feature, keep prompt bodies as resources (embedded files/templates) assembled by a typed composer, not as string literals in classes; the tell is a class dominated by multi-line prompt text. Judgment-shaped; no gate.
|
|
427
|
+
|
|
428
|
+
**[convention] The third copy of a job shape is the deadline for the abstraction.** Two similar jobs or decision flows are fine; when a third appears, extract the shared template instead of copying again. Judgment-shaped; no gate.
|
|
429
|
+
|
|
423
430
|
**DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
|
|
424
431
|
enrolled in guarded decisions, not every model a command reads. Use stream
|
|
425
432
|
reads/exact scopes or read-model parameters under read protection `On`
|
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
"command": "cs-mcp",
|
|
17
17
|
"args": [],
|
|
18
18
|
"description": "Review Code Health changes and prioritize maintainability risks with CodeScene; requires a CodeScene account and OAuth login."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "playwright",
|
|
22
|
+
"profiles": ["cratis/playwright"],
|
|
23
|
+
"transport": "stdio",
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "@playwright/mcp@0.0.83", "--headless", "--isolated"],
|
|
26
|
+
"description": "Drive a headless, isolated browser with Playwright for exploratory checks; requires Node.js and downloads the package and a browser on first use."
|
|
19
27
|
}
|
|
20
28
|
]
|
|
21
29
|
}
|
|
@@ -89,10 +89,9 @@ Cratis behavior. The `engineering-` prefix identifies the maintainer audience;
|
|
|
89
89
|
it does not imply confidential package contents or a private registry.
|
|
90
90
|
|
|
91
91
|
The consuming repository owns its project facts, confidential behavior, local
|
|
92
|
-
skills, and minimal host bootstraps.
|
|
93
|
-
|
|
94
|
-
repository-specific local workflows
|
|
95
|
-
documented legacy context fallback. Never merge, overwrite, or remove these
|
|
92
|
+
skills, and minimal host bootstraps. Project-specific context is the repository's own
|
|
93
|
+
documentation plus `.cratis/ai/rules/project.md`; `.agents/skills/` holds private or
|
|
94
|
+
repository-specific local workflows. Never merge, overwrite, or remove these
|
|
96
95
|
local files as a side effect of installing, updating, rolling back, or
|
|
97
96
|
uninstalling shared AI capabilities.
|
|
98
97
|
|
|
@@ -84,7 +84,9 @@ When changing a description, run trigger evaluations before and after when autho
|
|
|
84
84
|
|
|
85
85
|
## 11. Always-on content
|
|
86
86
|
|
|
87
|
-
Keep `general.md` to facts needed in every session, at most 200 lines. Warn above 24,000 bytes and above 91,000 combined bytes for rules without `paths`; line length alone can hide context bloat. Review globally applied Copilot instructions above 4,000 characters. These budgets leave room for source and task context, rather than promising a token count.
|
|
87
|
+
Keep `general.md` to facts needed in every session, at most 200 lines. Warn above 24,000 bytes and above 91,000 combined bytes for rules without `paths`; line length alone can hide context bloat. Review globally applied Copilot instructions above 4,000 characters. These budgets leave room for source and task context, rather than promising a token count. The corpus verification caps the rule bytes each harness loads at session start and fails above a cap: displace or demote text to an on-demand skill, or raise the cap in a reviewed change.
|
|
88
|
+
|
|
89
|
+
Every proposed rule names its enforcement: analyzer-enforceable, CI-enforceable or judgment-shaped. The first two ship as the analyzer, lint or CI check, with at most a one-line pointer in prose; only judgment-shaped rules are written out, because prose does not enforce itself and costs context in every session that loads it.
|
|
88
90
|
|
|
89
91
|
Use path-scoped rules for file-specific invariants and skills for particular operations. Hooks and CI enforce mechanical constraints; natural-language rules do not replace permission controls.
|
|
90
92
|
|
|
@@ -35,6 +35,7 @@ Stop-and-fix signals during implementation and review:
|
|
|
35
35
|
| A reusable component reaches into domain strings, generated proxies, identity, query, command, or navigation | It is no longer a primitive | Move it to the slice, or make it explicitly Cratis-aware and test/story it as such |
|
|
36
36
|
| Class/style string copied 3× or grown hard to scan | Styling changes become search-and-replace bugs | Extract to a shared style constant or a focused child component |
|
|
37
37
|
| `style={{ ... }}` with static token names | Bypasses theming and review | Move to `className`; keep `style` only for runtime values |
|
|
38
|
+
| A domain constant, threshold or eligibility rule in a `.ts`/`.tsx` file (`canApprove`, `maxAttempts = 2`) deciding whether an action shows | The client re-derives what the server knows, so the two drift and the UI breaks silently | Project the affordance as a read-model property server-side and render it. Judgment-shaped; no gate |
|
|
38
39
|
|
|
39
40
|
## Definition of done for a frontend change
|
|
40
41
|
|
|
@@ -47,10 +47,8 @@ repository. Individual projects need extra context that does not belong here,
|
|
|
47
47
|
such as product composition, approved environment names, directions for
|
|
48
48
|
obtaining credentials, and other local conventions ("Product policy" above).
|
|
49
49
|
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
- Read repository-owned documentation only as the documented legacy fallback when
|
|
53
|
-
repository-owned documentation does not exist; never merge both contexts.
|
|
50
|
+
- Project-specific context is the repository's own documentation plus
|
|
51
|
+
`.cratis/ai/rules/project.md`.
|
|
54
52
|
- Project context may explain which approved secret mechanism or local setup to
|
|
55
53
|
use, but it must never contain credential values, tokens, keys, passwords, or
|
|
56
54
|
other secrets.
|
|
@@ -199,6 +199,7 @@ language policy always follows the consuming repository's `go.mod` minimum.
|
|
|
199
199
|
| `cratis/content` | Release notes, social feed posts, voice review, content length, and developer marketing and distribution |
|
|
200
200
|
| `cratis/review` | Code review, performance, security |
|
|
201
201
|
| `cratis/codescene` | Opt-in CodeScene Code Health change gates, refactoring, debt prioritization and risk-based testing; requires an account and available MCP tools; each user registers the MCP server (see below) |
|
|
202
|
+
| `cratis/playwright` | Opt-in Playwright browser automation MCP server declaration for exploratory checks; carries no skills or rules and each user registers the server (see below) |
|
|
202
203
|
| `cratis/studio` | Studio MCP safety guidance |
|
|
203
204
|
| `cratis/cli` | CLI operations |
|
|
204
205
|
| `cratis/cli/terminal-workbench` | Human-operated terminal Workbench |
|
|
@@ -226,6 +227,27 @@ CodeScene needs an account and OAuth `login`; without an account or available
|
|
|
226
227
|
CodeScene tools, skip its steps and say so rather than blocking contributors.
|
|
227
228
|
The MCP catalog has no install-hints field and does not install the executable.
|
|
228
229
|
|
|
230
|
+
### Browser automation with Playwright
|
|
231
|
+
|
|
232
|
+
Select `cratis/playwright` explicitly to declare the `playwright` stdio server
|
|
233
|
+
(`npx -y @playwright/mcp@0.0.83 --headless --isolated`) in `mcp-servers.json`.
|
|
234
|
+
The flags keep the browser headless and isolated: no persistent profile, so no
|
|
235
|
+
personal cookies or logins are reused. The profile carries no skills or rules, and
|
|
236
|
+
no other profile composes it.
|
|
237
|
+
|
|
238
|
+
Today `cratis ai install` registers only Screenplay and Pi's `cratis-mcp` extension
|
|
239
|
+
loads only Screenplay, so Playwright, like CodeScene, is not registered for you.
|
|
240
|
+
Each user registers it in their own user-level MCP configuration, never in committed
|
|
241
|
+
project files. For Claude Code:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
claude mcp add --scope user playwright -- npx -y @playwright/mcp@0.0.83 --headless --isolated
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Pi users register it through a Pi MCP adapter or extension of their choice. Use it
|
|
248
|
+
for exploratory browser checks; repeatable checks stay in the repository's own
|
|
249
|
+
Playwright tests. Without the server registered, skip browser steps and say so.
|
|
250
|
+
|
|
229
251
|
### Event modeling with Screenplay
|
|
230
252
|
|
|
231
253
|
**Model first.** Decide opt-in per `general.md` "Model first"; where `cratis-screenplay-modeling-lifecycle` is installed, run its `scripts/model-first-status.mjs` for the decision, model root and drafts. An accepted model under the root covering the scope is the contract and the source
|
|
@@ -227,6 +227,8 @@ findings). Each reactor class should have a focused purpose. Multiple handler me
|
|
|
227
227
|
7. **No state** — Reactors should be stateless. Inject dependencies via primary constructor, but do not store mutable state on the class.
|
|
228
228
|
8. **A new side-effect reactor must not act on history.** A reactor that is new in a release (or has a new id) catches up from the start of the event log when it first registers, and `[OnceOnly]` does not cover that first catch-up because it is not a replay. If its handlers cause side effects (jobs, AI calls, external calls, notifications), start the observer at the tail on first registration or make the handler ignore facts that predate its registration, and specify that history causes nothing.
|
|
229
229
|
9. **An unattended, irreversible pass reads the log, not the sink — and carries a fuse.** A reactor that deletes, revokes, bills or notifies *because a read model says something is absent* is trusting a materialized sink that is only as current as its observer: a paused partition, a replay in progress or a lagging projection makes "absent" true of the sink and false of the world. Decide from a **`[Passive]`** read model (computed on demand from the events at the moment the reactor runs) or from the events themselves. And the code shape cannot tell an **empty subject set** ("nothing to consider" — fail-safe) from an **empty qualifying set** ("nothing survived the filter" — fail-destructive: every subject is about to be acted on), so an unattended pass states its expected population, refuses when the count is implausible, and caps how many subjects one run may touch — the same discipline as [guards-and-fuses.md](./guards-and-fuses.md), applied to a reactor. A sweep that decides from a read model also refuses while that model's observer is replaying or behind, because a rebuilding model reads as "nothing was ever processed"; specify the replaying case.
|
|
230
|
+
10. **Provider clients stay behind a translation or adapter reactor.** A `[Command]` or `IReactor` in a domain slice that injects or imports a provider client (GitHub, payment, mail SDK) is a finding: put the client behind one adapter slice and let domain slices work with our events and commands. Judgment-shaped today; a candidate for a namespace-boundary analyzer.
|
|
231
|
+
11. **A reactor must not react to its own consequences.** A reactor that posts comments or creates artifacts must guard against re-triggering on what it created (marker, author check, or event-type filter), or one input loops. Judgment-shaped; no gate.
|
|
230
232
|
|
|
231
233
|
## Failure transitions **[convention]**
|
|
232
234
|
|
|
@@ -244,6 +244,7 @@ Apply authorization attributes on the `[Command]` record or query method. **[con
|
|
|
244
244
|
- **Events are not read models** — never append events for totals, counts, statuses, search indexes, or dashboards. Project those from source events into read models.
|
|
245
245
|
- **Never duplicate one fact as an aggregate event *plus* a per-item fanout.** If a command fans out per item (one `RequestSentToPartner` per partner), don't also emit an aggregate event carrying the whole list. Keep the fanout as the single source of truth and project any aggregate from it; pick per-item vs. single-with-list event from how consumers read it — never both.
|
|
246
246
|
- **Command rejection normally appends no event.** Validation and business-rule rejections are command results, not facts. Add a rejection/failed event only when the business must audit or react to that failure later.
|
|
247
|
+
- **A settings event records the decision, not the whole blob.** Split composite configuration into one event per concern; a whole-object `…Set` event is last-writer-wins CRUD and turns every new field into a schema change. The tell is a `…Set`/`…Updated` event carrying a full settings record. Judgment-shaped; no gate.
|
|
247
248
|
- **One event name = one meaning.** Don't reuse a vague event for several domain facts; if facts can occur, be absent, or have consumers independently, split them into separate event types.
|
|
248
249
|
- **Never persist clock-relative state.** "Overdue", "expired", "due in 3 days" are true *now* and false later, and a projection only moves when an event arrives — so a projected `IsOverdue` is wrong the moment the clock crosses the line with no event to flip it. Store the **deadline** (a `DateOnly`/`DateTimeOffset` the business supplied) and compute the comparison at the query or UI edge, refreshing when time alone changes the answer. Only when *crossing* the deadline is itself a business fact (a fee is charged, a reminder goes out) does a scheduled process append an event for it.
|
|
249
250
|
|
|
@@ -307,6 +308,8 @@ query reference for versioned behavior and composition trade-offs.
|
|
|
307
308
|
|
|
308
309
|
Ladder, first that fits: (1) direct read-model injection — guarded under read protection `On`, advisory under `Off`; keyed by the command's own event-source id, or by `[LookUpBy]`; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
|
|
309
310
|
|
|
311
|
+
**[convention] One read model per consumer scenario, and dispatch never reads a UI model.** When a second consumer needs different semantics (board vs list vs count vs control flow), add a projection instead of extending the existing model; a command, reactor or job deciding what to do reads a model built for that decision, never a screen's listing model. The tell is a read model many slices import or one that projects many unrelated slices. Judgment-shaped; no gate.
|
|
312
|
+
|
|
310
313
|
**[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
|
|
311
314
|
compute a passive read model; any leftover documents are not maintained state.
|
|
312
315
|
Read through Chronicle by key or supported injection. **[convention]** Never
|
|
@@ -419,6 +422,10 @@ Redaction is a system-event request that removes payload availability from the e
|
|
|
419
422
|
|
|
420
423
|
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint, see the `cratis-chronicle-decision-reads` skill's `references/choosing-how-a-rule-holds.md`), or react to its events. **Never** instantiate or DI another slice's command/handler/service.
|
|
421
424
|
|
|
425
|
+
**[convention] Prompts are content.** In an AI-backed feature, keep prompt bodies as resources (embedded files/templates) assembled by a typed composer, not as string literals in classes; the tell is a class dominated by multi-line prompt text. Judgment-shaped; no gate.
|
|
426
|
+
|
|
427
|
+
**[convention] The third copy of a job shape is the deadline for the abstraction.** Two similar jobs or decision flows are fine; when a third appears, extract the shared template instead of copying again. Judgment-shaped; no gate.
|
|
428
|
+
|
|
422
429
|
**DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
|
|
423
430
|
enrolled in guarded decisions, not every model a command reads. Use stream
|
|
424
431
|
reads/exact scopes or read-model parameters under read protection `On`
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-engineering-regression-cluster
|
|
3
|
+
description: Stop fixing instances when one surface keeps breaking - name the failure class, add the gate that owns it, prove the gate fails, then fix the instance. Use when a third bug, regression, revert or review finding lands on the same screen, endpoint, command, workflow or module, when the same kind of fix keeps being reapplied, or when asked why something keeps breaking. Not for a first or isolated bug (fix it with a regression specification), for proving a consolidation dropped nothing (use cratis-engineering-consolidation-guard), or for a boundary that reports success after failing (use cratis-engineering-effect-boundaries).
|
|
4
|
+
license: LICENSE
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# The third issue on one surface names a failure class
|
|
8
|
+
|
|
9
|
+
Each bug fixed alone adds one specification for one symptom. When the same
|
|
10
|
+
**surface** (one screen, endpoint, command, query, workflow, module or rule
|
|
11
|
+
area) breaks a third time, the instances share a cause that no specification
|
|
12
|
+
owns yet, and the fourth instance is already on its way. Stop fixing instances:
|
|
13
|
+
name the **failure class**, give it an **owning gate**, then fix the instance.
|
|
14
|
+
|
|
15
|
+
## Count first
|
|
16
|
+
|
|
17
|
+
Before fixing an issue, count the earlier ones on its surface:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gh issue list --state all --search "<surface> in:title,body" --limit 50
|
|
21
|
+
git log --oneline -- <paths of the surface>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Count bugs, regressions, reverts and review findings, open or closed. Fewer
|
|
25
|
+
than three: fix the instance with a regression specification. Three or more:
|
|
26
|
+
follow the steps below before writing the fix.
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
1. **Name the failure class** in one sentence that states the property being
|
|
31
|
+
violated, not the symptom. Each earlier issue must be an instance of it, and
|
|
32
|
+
a reader must be able to recognize the next one by it. "Owner filter shows
|
|
33
|
+
archived items" is a symptom; "list filters are not exercised through the
|
|
34
|
+
rendered list by any test" is a class.
|
|
35
|
+
2. **Choose the owning gate by enforcement kind**, preferring the earliest that
|
|
36
|
+
can catch the class: an analyzer (compile time), a lint rule, a CI check or
|
|
37
|
+
verification script, or one specification suite that covers every member of
|
|
38
|
+
the class (every filter, every endpoint), not one specification per bug. A
|
|
39
|
+
class that only judgment can catch gets a review checklist entry, and the
|
|
40
|
+
choice is stated as judgment-shaped; see the enforcement rule in
|
|
41
|
+
[authoring-skills.md](../../rules/authoring-skills.md).
|
|
42
|
+
3. **Prove the gate fails** on the current broken instance, or on the earlier
|
|
43
|
+
instances with their fixes reverted, before fixing anything. A gate that
|
|
44
|
+
passes on the broken code owns nothing; follow
|
|
45
|
+
[guards-and-fuses.md](../../rules/guards-and-fuses.md) so it cannot pass
|
|
46
|
+
vacuously.
|
|
47
|
+
4. **Register the class.** When the repository keeps a failure-class table or
|
|
48
|
+
gate inventory, add one row: class, owning gate, where it runs. Otherwise
|
|
49
|
+
put the class name in the gate's failure message and the specification or
|
|
50
|
+
check name, so the next failure names its class.
|
|
51
|
+
5. **Fix the instance**; the gate turns green. Run it with the repository's
|
|
52
|
+
normal gates, and make it a required CI check when it lives in CI.
|
|
53
|
+
6. **Close the loop.** Link the earlier issues to the class in the pull request
|
|
54
|
+
comment, and leave a prose rule only for the judgment-shaped part.
|
|
55
|
+
|
|
56
|
+
## Report
|
|
57
|
+
|
|
58
|
+
State the surface and the issues counted, the class sentence, the gate and
|
|
59
|
+
where it runs, that it failed before the fix and passes after, and anything
|
|
60
|
+
left judgment-shaped.
|
|
61
|
+
|
|
62
|
+
## Pitfalls
|
|
63
|
+
|
|
64
|
+
- **A fourth instance specification.** Copying the third fix's specification
|
|
65
|
+
for the new symptom covers the symptom, not the class.
|
|
66
|
+
- **A prose rule instead of a gate.** Rules nobody enforces were already in
|
|
67
|
+
place for the earlier instances; text costs context and catches nothing.
|
|
68
|
+
- **A class named after the symptom.** If the next instance would not be
|
|
69
|
+
recognized by the sentence, the class is still too narrow.
|
|
70
|
+
- **A gate that never ran red.** Without the failing run, nothing shows it can
|
|
71
|
+
catch the class.
|
|
72
|
+
- **A loose surface.** Three unrelated bugs in one large module are not a
|
|
73
|
+
cluster; narrow the surface until the issues share a cause.
|
|
@@ -629,6 +629,13 @@
|
|
|
629
629
|
"language-agnostic"
|
|
630
630
|
]
|
|
631
631
|
},
|
|
632
|
+
{
|
|
633
|
+
"id": "cratis/playwright",
|
|
634
|
+
"availableTargets": [],
|
|
635
|
+
"languages": [
|
|
636
|
+
"language-agnostic"
|
|
637
|
+
]
|
|
638
|
+
},
|
|
632
639
|
{
|
|
633
640
|
"id": "cratis/components",
|
|
634
641
|
"availableTargets": [
|
|
@@ -986,6 +993,7 @@
|
|
|
986
993
|
"cratis-engineering-decision-record",
|
|
987
994
|
"cratis-engineering-docs-authoring",
|
|
988
995
|
"cratis-engineering-effect-boundaries",
|
|
996
|
+
"cratis-engineering-regression-cluster",
|
|
989
997
|
"cratis-release-notes",
|
|
990
998
|
"cratis-technical-examples"
|
|
991
999
|
],
|