@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.
@@ -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. Use repository-owned documentation as canonical
94
- project context when that migration is active, `.agents/skills/` for private or
95
- repository-specific local workflows, and repository-owned documentation only as the
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. Use repository-owned documentation as canonical
93
- project context when that migration is active, `.agents/skills/` for private or
94
- repository-specific local workflows, and repository-owned documentation only as the
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
- - Read repository-owned documentation as the canonical project-specific context when it
51
- exists.
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,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the canonical Cratis/AI repository for the full license text.
@@ -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
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "7.88.0",
3
+ "version": "7.90.0",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [