@cratis/pi 7.36.1 → 7.38.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 +109 -0
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +81 -0
- package/package/corpus/harnesses/copilot/instructions/capability-is-not-authority.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.csharp.instructions.md +92 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.instructions.md +83 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.typescript.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/components.instructions.md +300 -0
- package/package/corpus/harnesses/copilot/instructions/concepts.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/cratis-folder-is-configuration.instructions.md +29 -0
- package/package/corpus/harnesses/copilot/instructions/csharp.instructions.md +357 -0
- package/package/corpus/harnesses/copilot/instructions/dialogs.instructions.md +278 -0
- package/package/corpus/harnesses/copilot/instructions/documentation-structure-and-formatting.instructions.md +162 -0
- package/package/corpus/harnesses/copilot/instructions/documentation.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/editing-cratis-docs.instructions.md +74 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.instructions.md +84 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.specs.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/elixir.instructions.md +50 -0
- package/package/corpus/harnesses/copilot/instructions/exit-codes-and-wrappers.instructions.md +34 -0
- package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +55 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-quality.instructions.md +60 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-testing.instructions.md +127 -0
- package/package/corpus/harnesses/copilot/instructions/git-commits.instructions.md +139 -0
- package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +87 -0
- package/package/corpus/harnesses/copilot/instructions/glossary.instructions.md +63 -0
- package/package/corpus/harnesses/copilot/instructions/go-engineering.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/go.instructions.md +116 -0
- package/package/corpus/harnesses/copilot/instructions/guards-and-fuses.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/java.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/kotlin.instructions.md +175 -0
- package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +56 -0
- package/package/corpus/harnesses/copilot/instructions/new-repository-intake.instructions.md +35 -0
- package/package/corpus/harnesses/copilot/instructions/orleans.instructions.md +97 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +403 -0
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +176 -0
- package/package/corpus/harnesses/copilot/instructions/react.instructions.md +203 -0
- package/package/corpus/harnesses/copilot/instructions/reactors.instructions.md +336 -0
- package/package/corpus/harnesses/copilot/instructions/rtk.instructions.md +48 -0
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +147 -0
- package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +133 -0
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +281 -0
- package/package/corpus/harnesses/copilot/instructions/specs.typescript.instructions.md +141 -0
- package/package/corpus/harnesses/copilot/instructions/storybook.instructions.md +85 -0
- package/package/corpus/harnesses/copilot/instructions/typescript.instructions.md +151 -0
- package/package/corpus/harnesses/copilot/instructions/verification-discipline.instructions.md +27 -0
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +414 -0
- package/package/corpus/harnesses/copilot/instructions/web-fetching.instructions.md +12 -0
- package/package/corpus/harnesses/copilot/instructions/writing-correct-examples.instructions.md +57 -0
- package/package/corpus/harnesses/copilot/instructions/writing-cratis-docs.instructions.md +72 -0
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +6 -0
- package/package/corpus/product-sync.json +7 -3
- package/package/corpus/rules/orleans.md +4 -0
- package/package/corpus/rules/profiles.md +3 -3
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +17 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/verification.json +12 -0
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +5 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +3 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +2 -2
- package/package/corpus/skills/cratis-components-common-controls/SKILL.md +149 -0
- package/package/corpus/skills/cratis-components-common-controls/verification.json +15 -0
- package/package/corpus/skills/cratis-components-data-surfaces/SKILL.md +185 -0
- package/package/corpus/skills/cratis-components-data-surfaces/verification.json +16 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/SKILL.md +125 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/verification.json +15 -0
- package/package/corpus/skills/cratis-orleans-jobs/SKILL.md +115 -0
- package/package/corpus/skills/cratis-orleans-jobs/references/state-machines.md +64 -0
- package/package/corpus/skills/cratis-orleans-jobs/verification.json +20 -0
- package/package/corpus/skills/cratis-screenplay-discovery/SKILL.md +11 -6
- package/package/corpus/skills/cratis-screenplay-discovery/references/facilitation.md +9 -5
- package/package/corpus/skills/cratis-screenplay-discovery/references/human-workshop.md +1 -1
- package/package/corpus/skills/cratis-screenplay-discovery/references/plotting-and-handoff.md +28 -4
- package/package/corpus/skills/cratis-screenplay-discovery/references/storming-loop.md +2 -1
- package/package/corpus/skills/cratis-screenplay-discovery/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +23 -29
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +15 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/model-notes.md +208 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +20 -1
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +19 -36
- package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +26 -0
- package/package/corpus/skills/cratis-screenplay-model-review/verification.json +7 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +14 -16
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/core-rules-digest.md +8 -3
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/handoff-template.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/intake.md +21 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/reasoning-notes.md +10 -6
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/stop-or-assume.md +32 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/worked-example.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +4 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1716 -183
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +40 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +6 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +26 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +3 -0
- package/package/profile-catalog.json +6 -2
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.{ts,tsx}"
|
|
3
|
+
profile: application
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.tsx"
|
|
7
|
+
---
|
|
8
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
9
|
+
|
|
10
|
+
# Frontend Code Quality
|
|
11
|
+
|
|
12
|
+
The frontend is software engineering, held to the same bar as the backend — and it is the part most prone to silent degradation under iterative AI editing: orphaned code, duplicated components, sprawling files, shipped placeholders, untested state machines hidden in JSX. This rule is the maintainability contract that prevents that. It complements [react.md](./react.instructions.md) (architecture), [typescript.md](./typescript.instructions.md) (style), and [frontend-testing.md](./frontend-testing.instructions.md) (specs).
|
|
13
|
+
|
|
14
|
+
Aim for CUPID code: **Composable**, **Unix-like** (small focused pieces), **Predictable**, **Idiomatic**, **Domain-based**.
|
|
15
|
+
|
|
16
|
+
## Non-negotiables
|
|
17
|
+
|
|
18
|
+
1. **Never ship placeholder or dead UI as if it were real.** "Coming soon", lorem-ipsum, fake counts, and stubbed fixtures must not reach a screen backing a feature the code actually implements. An empty state is the *designed* empty state for that surface, not a build-time apology. When you implement the real behavior, delete the placeholder and its strings in the same change.
|
|
19
|
+
2. **Components stay small and single-responsibility.** A `// Section` comment inside a component means that section is its own component. A presentational `.tsx` over ~150 lines, or with more than one clear responsibility, is decomposed. Parent owns state; children receive props.
|
|
20
|
+
3. **State logic lives in a view model, not the component.** Per [react.md](./react.instructions.md): extract a `withViewModel` view model (or a tested state module) as soon as a component has 3+ `useState`, any `useCallback`/state-syncing `useEffect`, or derived values. `useCallback`/`useMemo` inside a `withViewModel` component is a smell. Never store React state in a view model; never call hooks inside one — inject the Cratis abstraction. A view model must be constructible in a spec without React.
|
|
21
|
+
4. **Reuse before rewrite.** Check the shared component surface and existing style constants before adding a component or a long class string. Never duplicate a primitive that already exists; prefer a Cratis Components wrapper or an existing shared component over a new one.
|
|
22
|
+
5. **Test behavior, not implementation trivia.** Non-trivial view-model/helper logic gets BDD specs (see [frontend-testing.md](./frontend-testing.instructions.md)). Storybook is visual coverage, not a replacement for behavioral specs.
|
|
23
|
+
6. **No `any`, descriptive names, no orphaned code.** `unknown` or a real type, never `any`. After editing a `.tsx`, read past its last closing `};` to confirm no stale `return (...)` block, duplicate body, or unreachable code remains. Comment only a non-obvious *why* — no "what"/provenance comments.
|
|
24
|
+
|
|
25
|
+
## Smell guards
|
|
26
|
+
|
|
27
|
+
Stop-and-fix signals during implementation and review:
|
|
28
|
+
|
|
29
|
+
| Smell | Why it matters | Preferred move |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| New component added without checking the shared component surface and `@cratis/components` | Duplicates fragment the design system | Reuse an existing primitive/wrapper, or document the missing one |
|
|
32
|
+
| Slice-local helper promoted to the shared component folder after one use | Freezes a one-off as a shared API | Keep it beside the slice until a second real consumer appears |
|
|
33
|
+
| A vendor UI control (any `primereact/*`, `@mui/*`, … import) where a Cratis component exists | Bypasses Arc binding, the overlay environment and validation; adds a second focus owner | Use the `@cratis/components` subpath component |
|
|
34
|
+
| Import from the `@cratis/components` root barrel in new code | Pulls optional-peer-heavy exports, hides intent | Import from subpaths (`CommandDialog`, `DataPage`, `DataTables`, `Dialogs`, `Dropdown`, `Toolbar`, `Common`) |
|
|
35
|
+
| Component API has many boolean visual flags (`primary`, `selected`, `muted`, …) | State combinations become undefined | One typed `variant`/`tone` union, or split components |
|
|
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
|
+
| 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
|
+
| `style={{ ... }}` with static token names | Bypasses theming and review | Move to `className`; keep `style` only for runtime values |
|
|
39
|
+
|
|
40
|
+
## Definition of done for a frontend change
|
|
41
|
+
|
|
42
|
+
- [ ] No placeholder/stub copy ships for behavior that is actually implemented.
|
|
43
|
+
- [ ] Each component is single-responsibility; oversized components were split.
|
|
44
|
+
- [ ] State/logic crossing the [react.md](./react.instructions.md) thresholds lives in a view model.
|
|
45
|
+
- [ ] Reused existing primitives/wrappers instead of duplicating.
|
|
46
|
+
- [ ] No smell guard above is triggered without a short, intentional reason.
|
|
47
|
+
- [ ] File read past its final `};` — no orphaned code.
|
|
48
|
+
- [ ] New/changed reusable or stateful components have Storybook stories.
|
|
49
|
+
- [ ] New/changed view-model/helper behavior has BDD specs (see [frontend-testing.md](./frontend-testing.instructions.md)).
|
|
50
|
+
- [ ] Lint, conditional test, and build pass (the project's frontend gates).
|
|
51
|
+
|
|
52
|
+
## Review discipline
|
|
53
|
+
|
|
54
|
+
Treat the frontend gates as seriously as the backend build/test gates — a green lint is the floor, not the goal. For any non-trivial frontend change, run a correctness review (and a reuse/dead-code pass) before completing it. Degradation accrues one "it lints, ship it" at a time.
|
|
55
|
+
|
|
56
|
+
## See also
|
|
57
|
+
|
|
58
|
+
- [react.md](./react.instructions.md) — MVVM, Cratis Components, queries/commands.
|
|
59
|
+
- [typescript.md](./typescript.instructions.md) — style, type discipline.
|
|
60
|
+
- [frontend-testing.md](./frontend-testing.instructions.md) — BDD Vitest specs and layout.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/for_*/**/*.{ts,tsx}"
|
|
3
|
+
profile: application
|
|
4
|
+
paths:
|
|
5
|
+
- "**/for_*/**/*.ts"
|
|
6
|
+
- "**/for_*/**/*.tsx"
|
|
7
|
+
---
|
|
8
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
9
|
+
|
|
10
|
+
# Frontend Testing
|
|
11
|
+
|
|
12
|
+
Frontend tests are specifications. The `for_/when_/and_` shape is intentionally the same BDD language as the backend Cratis Specifications: one subject, one context, clear setup, small assertions, no framework noise. When in doubt, mirror the structure in the Cratis Components source before inventing a new style.
|
|
13
|
+
|
|
14
|
+
The quality goal is CUPID code: frontend logic that cannot be tested in isolation is usually doing too much or depending on the wrong abstraction.
|
|
15
|
+
|
|
16
|
+
## Frameworks
|
|
17
|
+
|
|
18
|
+
- [Vitest](https://vitest.dev/) for running tests; Mocha-style `describe`/`it`/`beforeEach` structure.
|
|
19
|
+
- [SinonJS](https://sinonjs.org) for mocking/stubbing.
|
|
20
|
+
- [Chai](https://www.chaijs.com) — **always the `.should` fluent interface**, never `expect()`. `result.should.equal(expected)` reads as a sentence and matches the project's prose preference.
|
|
21
|
+
|
|
22
|
+
## Location and naming
|
|
23
|
+
|
|
24
|
+
Tests live next to the unit they specify:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
<Slice>/
|
|
28
|
+
<Subject>.ts
|
|
29
|
+
for_<Subject>/
|
|
30
|
+
when_<context>/
|
|
31
|
+
and_<extra_context>.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Shape | Use for |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `for_<Subject>/when_<context>/and_<extra>.ts` | default — view models, helpers with branches, component specs |
|
|
37
|
+
| `for_<Subject>/when_<context>.ts` | tiny pure-helper spec with no useful extra context |
|
|
38
|
+
|
|
39
|
+
snake_case file/folder names so the path reads as a scenario sentence. Use `should` in `it()` descriptions (spaces, not underscores — TS specs read as human sentences). Don't add `.test.ts`/`.spec.ts` files unless the config changes intentionally.
|
|
40
|
+
|
|
41
|
+
## BDD structure
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { describe, beforeEach, it } from 'vitest';
|
|
45
|
+
import { MyViewModel } from '../../MyViewModel';
|
|
46
|
+
|
|
47
|
+
describe('when filtering active candidates', () => {
|
|
48
|
+
let viewModel: MyViewModel;
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
viewModel = new MyViewModel();
|
|
52
|
+
viewModel.setSearch('senior');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should keep candidates matching the search text', () => {
|
|
56
|
+
viewModel.filteredItems.should.have.lengthOf(1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `describe('when ...')` names the scenario, not the class.
|
|
62
|
+
- `beforeEach` does the arrange/act once for the context; each `it('should ...')` asserts one observable outcome.
|
|
63
|
+
|
|
64
|
+
## What to test
|
|
65
|
+
|
|
66
|
+
Behavior that can regress without TypeScript catching it:
|
|
67
|
+
|
|
68
|
+
- View-model state transitions, computed getters, filtering/sorting/selection, wizard steps, validation state, command-value derivation.
|
|
69
|
+
- Pure helpers: parsing, formatting, path lookup, grouping, boundary cases.
|
|
70
|
+
- Command orchestration outside `CommandDialog`: handling of unauthorized / invalid / exception / success outcomes.
|
|
71
|
+
- React component rendering only when markup, wrapper behavior, disabled state, or integration with a wrapped component is the point.
|
|
72
|
+
|
|
73
|
+
**Do not test:** generated Cratis proxies; framework/Cratis Components internals; CSS pixel-perfection; trivial presentational pass-throughs with no branch/derived behavior.
|
|
74
|
+
|
|
75
|
+
## View models
|
|
76
|
+
|
|
77
|
+
Plain TypeScript classes constructible directly in a spec:
|
|
78
|
+
|
|
79
|
+
- No React hooks; no direct `window`/`localStorage`/`location`/timers/network — inject the Cratis/browser abstraction (see [react.md](./react.instructions.md)).
|
|
80
|
+
- Derived values are getters, not `useMemo`.
|
|
81
|
+
- Commands are methods with explicit inputs, not inline closures hidden in JSX.
|
|
82
|
+
|
|
83
|
+
Pass small typed fakes or `sinon.stub()` instances for dependencies; avoid broad harnesses until a second spec reuses them.
|
|
84
|
+
|
|
85
|
+
## Component specs
|
|
86
|
+
|
|
87
|
+
The environment is `node`. For behavior that doesn't need browser events, use server-rendered markup:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import React from 'react';
|
|
91
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
92
|
+
import { vi } from 'vitest';
|
|
93
|
+
|
|
94
|
+
vi.mock('@cratis/components/Dialogs', () => ({
|
|
95
|
+
Dialog: (props: { buttons?: React.ReactNode; children?: React.ReactNode }) =>
|
|
96
|
+
React.createElement('div', null, props.children, props.buttons),
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
describe('when rendered while busy', () => {
|
|
100
|
+
let html: string;
|
|
101
|
+
beforeEach(() => { html = renderToStaticMarkup(React.createElement(MyDialog, { isBusy: true })); });
|
|
102
|
+
it('should disable the confirm button', () => { html.should.include('disabled'); });
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- Mock only the external component boundary needed; prefer typed mock props over `any`.
|
|
107
|
+
- Add jsdom / Testing Library only when behavior truly requires DOM events; don't make it the default.
|
|
108
|
+
- Avoid snapshots — they hide behavior and make refactors noisy.
|
|
109
|
+
|
|
110
|
+
## Stories vs tests
|
|
111
|
+
|
|
112
|
+
Storybook covers visual states, documentation, and manual verification; Vitest covers behavior. A component with non-trivial logic usually needs both — Storybook for variants/interactive state, Vitest for view-model/helper behavior and narrow rendered-component behavior.
|
|
113
|
+
|
|
114
|
+
## Async and time
|
|
115
|
+
|
|
116
|
+
Use `vi.useFakeTimers()` for time-dependent behavior; restore with `vi.useRealTimers()` in `afterEach`. Never depend on the real current date, random values, network, or browser storage. For suspense-query tests, clear the Arc query caches in teardown.
|
|
117
|
+
|
|
118
|
+
## Gate
|
|
119
|
+
|
|
120
|
+
Run the project's frontend test gate (e.g. `yarn test`) for frontend changes; it complements lint and build, it does not replace them.
|
|
121
|
+
|
|
122
|
+
## See also
|
|
123
|
+
|
|
124
|
+
- [react.md](./react.instructions.md) — MVVM, view-model testability, Arc hooks.
|
|
125
|
+
- [frontend-quality.md](./frontend-quality.instructions.md) — the engineering bar these specs serve.
|
|
126
|
+
- [specs.md](./specs.instructions.md) — the backend BDD language this mirrors.
|
|
127
|
+
- skill: **cratis-application-react-specifications** — the step-by-step workflow that applies these conventions.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*"
|
|
3
|
+
---
|
|
4
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
5
|
+
|
|
6
|
+
# How to Write Git Commits
|
|
7
|
+
|
|
8
|
+
Commits are the permanent record of how the codebase evolved. Each commit should tell a clear story: *what* changed and *why*. A reviewer reading `git log --oneline` should understand the arc of the work without opening any diffs.
|
|
9
|
+
|
|
10
|
+
## Never rewrite history
|
|
11
|
+
|
|
12
|
+
**Committed history is append-only. Never rewrite it — under any circumstances, on any branch, including your own.**
|
|
13
|
+
|
|
14
|
+
These commands are **forbidden** unless the human explicitly asks for that specific command on that specific branch in that specific message:
|
|
15
|
+
|
|
16
|
+
| Forbidden | Why |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `git rebase` (any form, incl. `-i`, `--onto`, `--autosquash`) | rewrites every replayed commit; the originals become unreachable |
|
|
19
|
+
| `git commit --amend` | replaces the tip commit; the original is unreachable immediately |
|
|
20
|
+
| `git reset --hard`, `git reset` onto an earlier commit | discards commits and, with `--hard`, uncommitted work too |
|
|
21
|
+
| `git push --force`, `git push --force-with-lease`, `git push -f` | destroys the remote's copy — the one backup that survives local loss |
|
|
22
|
+
| `git branch -D` / `git branch -d` on a branch holding unmerged commits | strands those commits with no ref; `git gc` then deletes them |
|
|
23
|
+
| `git checkout`/`git switch` away while another agent's commits sit only on this branch | the commits leave with the branch and the working tree silently reverts |
|
|
24
|
+
| `git filter-branch`, `git filter-repo`, history-rewriting scripts | rewrites the entire history graph |
|
|
25
|
+
| `git gc --prune=now`, `git reflog expire` | destroys the recovery path for anything already stranded |
|
|
26
|
+
| `git merge --squash`, `gh pr merge --squash`, `--rebase`, the **Squash and merge** / **Rebase and merge** buttons | collapses or replays the branch's commits into new ones; every original commit becomes unreachable and the branch's real history is destroyed at the moment of merge |
|
|
27
|
+
|
|
28
|
+
**Merging is part of this rule, and it is the easiest place to get it wrong.** A squash merge feels
|
|
29
|
+
like an integration step rather than a rewrite, which is exactly why it slips past: the branch is
|
|
30
|
+
deleted straight afterwards, so the commits it collapsed have no ref left and `gc` eventually takes
|
|
31
|
+
them. **Always merge with a true merge commit** — `git merge --no-ff`, or `gh pr merge --merge`.
|
|
32
|
+
Never `--squash`, never `--rebase`, and never the equivalent buttons in the GitHub UI.
|
|
33
|
+
|
|
34
|
+
If a repository's settings only allow squash or rebase merges, that is a **setting to raise with the
|
|
35
|
+
owner, not a licence to squash.** Stop and ask.
|
|
36
|
+
|
|
37
|
+
**This is not stylistic.** A commit can exist as an object (`git cat-file -t <sha>` succeeds) while being absent from every branch *and* from the working tree — reachable only through `git reflog`, and only until `gc` runs. Work has already been lost in this repository exactly this way: a branch checkout carried another session's commit away, the branch was deleted, and the file changes silently reverted in the tree. It was recovered from the reflog by luck, because someone asked the right question in time.
|
|
38
|
+
|
|
39
|
+
### What to do instead
|
|
40
|
+
|
|
41
|
+
- **Made a mistake in the last commit?** Add a new commit that corrects it. The wrong state stays in history, and that is fine — history is a record of what happened, not a curated story of what you wish had happened.
|
|
42
|
+
- **Need to undo a commit?** `git revert <sha>` — it records the undo as a new commit and loses nothing.
|
|
43
|
+
- **Messy commits before a PR?** Leave them. A reviewer reading a coherent series of small commits is better served than by one squashed blob, and the project does not require a linear history.
|
|
44
|
+
- **Landing a PR?** `gh pr merge --merge` (a real merge commit). **Not `--squash`, not `--rebase`.** The commits on the branch are the record of how the change was actually built; squashing throws that away permanently in exchange for a tidier `main`, which is not a trade this project makes.
|
|
45
|
+
- **Need someone else's changes?** `git merge`, never `git rebase`.
|
|
46
|
+
- **Need to move a commit to another branch?** `git cherry-pick` — it copies, leaving the original reachable.
|
|
47
|
+
- **Working alongside another agent or session?** Use `git worktree add` so each has its own checkout and branch. Never two sessions committing in one working tree.
|
|
48
|
+
|
|
49
|
+
### Before you finish
|
|
50
|
+
|
|
51
|
+
Verify your own commits are still reachable on the branch **and** that their content is still in the working tree — those are two different things. `git log --oneline -5` plus a `grep` for something the commit introduced. If a commit has gone missing, `git reflog` is the recovery tool: find the SHA, `git tag` it immediately so `gc` cannot take it, then `git cherry-pick` it back.
|
|
52
|
+
|
|
53
|
+
## Logical Grouping
|
|
54
|
+
|
|
55
|
+
Every commit must be a **single logical unit of work**. Group related changes together; separate unrelated changes into distinct commits.
|
|
56
|
+
|
|
57
|
+
### What belongs in one commit
|
|
58
|
+
|
|
59
|
+
- A bug fix and the spec that proves it.
|
|
60
|
+
- A new file and the changes to existing files needed to integrate it (imports, registrations, wiring).
|
|
61
|
+
- A refactor that moves or renames code — only the mechanical transformation, nothing else.
|
|
62
|
+
- An interface change together with all implementation updates required to compile.
|
|
63
|
+
|
|
64
|
+
### What does NOT belong in one commit
|
|
65
|
+
|
|
66
|
+
- A bug fix mixed with an unrelated feature.
|
|
67
|
+
- Source code changes mixed with unrelated spec additions for a different area.
|
|
68
|
+
- Formatting or style cleanups bundled with behavioral changes.
|
|
69
|
+
- Multiple independent fixes or features squashed into a single commit.
|
|
70
|
+
|
|
71
|
+
### Deciding where to split
|
|
72
|
+
|
|
73
|
+
Ask: "If I needed to revert this commit, would I lose exactly one coherent change?" If reverting would undo two unrelated things, it should be two commits.
|
|
74
|
+
|
|
75
|
+
Common split points:
|
|
76
|
+
|
|
77
|
+
1. **Infrastructure / plumbing first** — interface additions, new types, or schema changes that later commits build on.
|
|
78
|
+
2. **Core logic second** — the behavioral change that uses the new infrastructure.
|
|
79
|
+
3. **Specs / tests third** — the specs that prove the behavioral change works. Specs may also be combined with the core logic commit when they are tightly coupled (e.g., a TDD red-green cycle or a bug fix with its regression test).
|
|
80
|
+
4. **Integration or wiring last** — connecting the new behavior to the rest of the system (DI registration, routing, UI hookup).
|
|
81
|
+
|
|
82
|
+
When a task produces both source fixes and new integration specs, prefer separate commits for the source changes and the specs — unless the specs are inseparable from the fix (e.g., a single bug fix + its regression test).
|
|
83
|
+
|
|
84
|
+
## Commit Messages
|
|
85
|
+
|
|
86
|
+
### Format
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
<imperative summary of what this commit does>
|
|
90
|
+
|
|
91
|
+
<optional body — why the change was made, context, trade-offs>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **Subject line**: imperative mood, present tense. Start with a verb: `Add`, `Fix`, `Remove`, `Rename`, `Extract`, `Update`, `Support`.
|
|
95
|
+
- **No period** at the end of the subject line.
|
|
96
|
+
- **72-character limit** on the subject line. If you cannot describe the change in 72 characters, the commit is probably too large — split it.
|
|
97
|
+
- **Body**: separated from the subject by a blank line. Explain *why*, not *what* (the diff shows the what). Use bullet points for multi-part changes.
|
|
98
|
+
|
|
99
|
+
### Good examples
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
Fix duplicate key crash in IdentityStorage.Populate
|
|
103
|
+
|
|
104
|
+
The upsert used InsertOne which threw on existing identities.
|
|
105
|
+
Replace with ReplaceOne using upsert: true.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Add type-safe event migration API with expression-based builders
|
|
110
|
+
|
|
111
|
+
Introduce EventTypeMigration<TUpgrade, TPrevious> base class with
|
|
112
|
+
typed property builders for Split, Join, Rename, and DefaultValue
|
|
113
|
+
operations. Migrators are discovered automatically by convention.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
Add integration specs for observer replay on redaction
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Bad examples
|
|
121
|
+
|
|
122
|
+
- `Fix stuff` — meaningless.
|
|
123
|
+
- `WIP` — never commit work-in-progress; stage and commit when the unit is complete.
|
|
124
|
+
- `Add files` — says nothing about what or why.
|
|
125
|
+
- `Fix bug and add new feature and update docs` — three unrelated things.
|
|
126
|
+
- `Changed Observer.Handling.cs` — describes a file, not a behavior.
|
|
127
|
+
|
|
128
|
+
## When to Commit
|
|
129
|
+
|
|
130
|
+
- **After each logical unit passes the build** — `dotnet build` with zero errors and zero warnings, or `yarn compile` with zero errors.
|
|
131
|
+
- **Before starting a different kind of work** — about to switch from fixing a bug to adding a feature? Commit the bug fix first.
|
|
132
|
+
- **After completing specs for a change** — if the specs are a separate commit from the source change.
|
|
133
|
+
- **Never commit code that does not compile.** Every commit must be a buildable, working state of the codebase.
|
|
134
|
+
|
|
135
|
+
## Staging Discipline
|
|
136
|
+
|
|
137
|
+
- Use `git add <specific files>` rather than `git add .` or `git add -A`. Only stage files that belong to the current logical unit.
|
|
138
|
+
- Review `git diff --cached` before committing to verify nothing unrelated was staged.
|
|
139
|
+
- If you realize mid-commit that unrelated changes are mixed in, unstage them and commit only the related subset.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: ".github/workflows/**"
|
|
3
|
+
paths:
|
|
4
|
+
- ".github/workflows/**"
|
|
5
|
+
---
|
|
6
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
7
|
+
|
|
8
|
+
# GitHub Actions workflows: capacity, cost, and release discipline
|
|
9
|
+
|
|
10
|
+
> **Why this rule exists.** On 2026-08-25 the organization's hosted runners starved for
|
|
11
|
+
> most of a working day: every repository ran its scheduled jobs at the same minute,
|
|
12
|
+
> hour-long jobs with no timeout occupied the shared concurrency pool, and a
|
|
13
|
+
> comment-triggered assistant booted a runner for every comment in two repositories.
|
|
14
|
+
> The remediation that followed is encoded here so it stays true.
|
|
15
|
+
|
|
16
|
+
## The budget you are spending
|
|
17
|
+
|
|
18
|
+
- The whole organization shares **one hosted-runner concurrency pool** (20 concurrent
|
|
19
|
+
jobs on the Free plan, 5 for macOS). Every queued job competes with every repository.
|
|
20
|
+
- **Public repositories** run free on hosted runners; **private repositories bill every
|
|
21
|
+
minute** — Linux 1×, Windows 2×, macOS 10× — against a small monthly allowance.
|
|
22
|
+
- The organization runs its own scale set (`cratis-arc`). Jobs there cost nothing and
|
|
23
|
+
do not touch the hosted pool.
|
|
24
|
+
|
|
25
|
+
## Scheduling
|
|
26
|
+
|
|
27
|
+
- **Never schedule on the top of the hour**, and never copy another repository's cron.
|
|
28
|
+
`0 6 * * *` in 36 repositories produced a 270-job stampede into 20 slots. Pick a
|
|
29
|
+
deterministic offset unique to the repository (minute 1–59, spread across 03:00–07:00 UTC).
|
|
30
|
+
- A scheduled workflow that exists to keep coverage (nightly full matrix, health probe)
|
|
31
|
+
should run the *narrowest* thing that preserves the claim it exists to make.
|
|
32
|
+
- **A scheduled workflow whose subject no longer exists is deleted, not left running.**
|
|
33
|
+
It still queues into the shared pool every day, and a scheduled no-op is the worst
|
|
34
|
+
kind: it never fails, so nothing ever draws attention to it. Deleting it is also the
|
|
35
|
+
only way to stop handing it whatever secrets it was passed — `Cratis/AI` ran a daily
|
|
36
|
+
package update against a repository with no package manifest of any kind, checking out
|
|
37
|
+
the full history under an organization-wide write PAT to find nothing to update.
|
|
38
|
+
|
|
39
|
+
## Every job, always
|
|
40
|
+
|
|
41
|
+
- **`timeout-minutes` on every job.** The default is 6 hours; one hung job holds a
|
|
42
|
+
pool slot for all of it. Tiers that work: quick checks 15, builds 30–45,
|
|
43
|
+
publish/release 60, integration/benchmarks 120.
|
|
44
|
+
- **A `concurrency` group on every pull-request verification workflow** with
|
|
45
|
+
`cancel-in-progress: true`, keyed `${{ github.workflow }}-${{ github.ref }}`.
|
|
46
|
+
**Never** cancel-in-progress on publish, release, or deploy workflows — a cancelled
|
|
47
|
+
half-publish is worse than a queued one.
|
|
48
|
+
|
|
49
|
+
## Matrices and operating systems
|
|
50
|
+
|
|
51
|
+
- Run the full OS matrix on `schedule`/`workflow_dispatch`; run **Linux only on pull
|
|
52
|
+
request syncs**. Windows bills double and macOS ten-fold, and per-PR duplication has
|
|
53
|
+
to earn that cost with unique signal. (Measured before adopting this: 75 dual-OS runs
|
|
54
|
+
of one private repository, zero Windows-only failures.)
|
|
55
|
+
- Skip the expensive job entirely for documentation-only changes: a cheap change-detection
|
|
56
|
+
job (`git diff --name-only HEAD^1 HEAD` on the merge commit) gating the build job.
|
|
57
|
+
|
|
58
|
+
## Runners
|
|
59
|
+
|
|
60
|
+
- Private-repository jobs belong on the organization scale set: `runs-on: cratis-arc`,
|
|
61
|
+
or a repository variable such as `${{ vars.<REPO>_RUNNER || 'ubuntu-latest' }}` so
|
|
62
|
+
routing changes without a code change.
|
|
63
|
+
- On self-hosted runners, `actions/setup-dotnet` cannot write `/usr/share/dotnet`.
|
|
64
|
+
Set `DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet` on the setup step.
|
|
65
|
+
- **Never** route `pull_request`-triggered jobs of a *public* repository to self-hosted
|
|
66
|
+
runners — that hands code execution on our infrastructure to any fork.
|
|
67
|
+
|
|
68
|
+
## Automation that writes back
|
|
69
|
+
|
|
70
|
+
- A bot that pushes to a branch must **retry with `git pull --rebase`** — the branch
|
|
71
|
+
moves while the job runs, and a plain push loses the whole run to a non-fast-forward.
|
|
72
|
+
- A bot's pre-push verification build must match the strictest configuration any CI in
|
|
73
|
+
the organization applies to the same commit (build `Release` when consumers enforce
|
|
74
|
+
analyzers there). Do not add `[skip ci]` to bot commits as a load optimization: the
|
|
75
|
+
downstream CI run is the only check that sees the merged result.
|
|
76
|
+
- Comment-triggered agent workflows must gate on the trigger phrase in the job's `if:`
|
|
77
|
+
(for example `contains(github.event.comment.body, '@claude')`) so a runner only
|
|
78
|
+
starts when the agent is actually addressed.
|
|
79
|
+
|
|
80
|
+
## Releases
|
|
81
|
+
|
|
82
|
+
- A pull request that changes nothing outward-facing — workflow edits, CI configuration,
|
|
83
|
+
documentation — carries the **`no-release`** label, never `patch`. See
|
|
84
|
+
[`pull-requests.md`](./pull-requests.instructions.md). Merging config-only work under `patch` cut
|
|
85
|
+
four unintended releases on 2026-08-25.
|
|
86
|
+
- Every repository's release-intent gate must accept `no-release`; a gate that only
|
|
87
|
+
accepts `major`/`minor`/`patch` forces exactly that mistake.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*"
|
|
3
|
+
---
|
|
4
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
5
|
+
|
|
6
|
+
# Cratis Glossary
|
|
7
|
+
|
|
8
|
+
One precise line per load-bearing term, so the same word means the same thing everywhere. When a rule or skill uses one of these, this is the definition it intends.
|
|
9
|
+
|
|
10
|
+
## Events & streams
|
|
11
|
+
|
|
12
|
+
- **Event** — an immutable, past-tense **fact** that something happened; an `[EventType]` record. Never mutated; lives in the log forever.
|
|
13
|
+
- **Event source** — the entity an event stream belongs to (the stream key); identified by an `EventSourceId<T>`.
|
|
14
|
+
- **Event source id** — the strongly-typed identity of an event source (`EventSourceId<T>`). Implicit in the event context — **never** an event payload property.
|
|
15
|
+
- **Event stream** — the ordered events for one event source within an event sequence.
|
|
16
|
+
- **Event sequence** — a named, append-ordered log; `EventSequenceId` (the event **log**, **outbox**, **inbox**).
|
|
17
|
+
- **Event log** — the default event sequence where domain events are appended.
|
|
18
|
+
- **Outbox / inbox** — cross-service event sequences: a producer appends its public contract event to the outbox; a consumer observes the inbox.
|
|
19
|
+
- **EventContext** — metadata traveling with an event (event source id, sequence number, occurred, causation/correlation, subject).
|
|
20
|
+
|
|
21
|
+
## Read side
|
|
22
|
+
|
|
23
|
+
- **Read model** — a `[ReadModel]` record holding queryable derived state, built by a projection or reducer; exposes `static` query methods.
|
|
24
|
+
- **Projection** — declares how a read model is built by consuming **events** (model-bound attributes, or fluent `IProjectionFor<T>`); pure events→state, no side effects, never reads other read models.
|
|
25
|
+
- **Reducer** — `IReducerFor<T>`; a "current state + event → next state" read-model builder; the last-resort escape hatch when projections can't express the transition.
|
|
26
|
+
- **Reactor** — `IReactor`; observes events and produces **side effects** (notifications, commands, follow-up events). The "if this then that".
|
|
27
|
+
- **Observer** — Chronicle's umbrella for anything consuming an event sequence (projection, reducer, reactor); carries subscription / replay / **quarantine** state. Projection replay and catch-up each walk the sequence in one globally ordered job step; a new catch-up failure stops that step. Full replay is inactive and completion does not resume a quarantined observer (Chronicle v19.50.1, `Documentation/concepts/observers.mdx`, `Observation/Jobs/{CatchUpObserver,ReplayObserver}.cs` under `Source/Kernel/Core/`).
|
|
28
|
+
- **Sink** — where a read model is persisted (MongoDB, EF Core).
|
|
29
|
+
- **AutoMap** — Chronicle's on-by-default mapping of matching event→read-model property names; **never call `.AutoMap()`**.
|
|
30
|
+
- **Query** — a `static` method on a `[ReadModel]`; returns derived state, **snapshot** (one-shot) or **observable** (live).
|
|
31
|
+
|
|
32
|
+
## Write side
|
|
33
|
+
|
|
34
|
+
- **Command** — a `[Command]` record expressing an imperative **intent**; its public `Handle()` produces event(s) (or a response).
|
|
35
|
+
- **Provide()** — the command method that fetches/computes data after validation/authorization and before `Handle()`; may short-circuit with a `ValidationResult`.
|
|
36
|
+
- **Causation chain** — the ordered links saying how an append came about (root process → command → …), each carrying properties. A command records its **name and its property values** there, so an event says what the command was asked to do; the chain lives in the event log and is as permanent as the events.
|
|
37
|
+
- **Constraint** — `IConstraint`; an **append-time** invariant (uniqueness / concurrency) enforced at the event-store level.
|
|
38
|
+
- **DCB (Dynamic Consistency Boundary)** — enforcing a state-dependent rule under concurrency over explicitly enrolled event-state reads: stream decisions/exact scopes or protected, admitted projection decision reads. Plain injected models and `Result<TEvent, ValidationResult>` alone are advisory, not a guard.
|
|
39
|
+
- **Consistency boundary** — the scope within which an invariant holds atomically (an event source, a routed stream, or the event state enrolled by an admitted protected decision read).
|
|
40
|
+
- **EventForEventSourceId** — self-describing wrapper to append an event to a **specific** (cross-stream) event source, from a command `Handle()` or from a reactor handler (reactor support has shipped since Chronicle 15.35). It carries the event stream type and id, source type, subject, occurred time, tags and causation, so it is also how a reactor sets those explicitly.
|
|
41
|
+
- **ReactorDelivery** — the identity of one delivery of one event to one reactor partition; declare it as a handler parameter and Chronicle passes it in. Stable across a replay and across recovering a failed partition, so a receipt kept under its `Id` is what makes a side effect survive re-delivery. An identity, not a guarantee — Chronicle does not know whether the effect ran.
|
|
42
|
+
|
|
43
|
+
## Structure & types
|
|
44
|
+
|
|
45
|
+
- **Slice** — the vertical unit of one behavior (command + events + projection + read model + component + specs), created/changed/deleted together.
|
|
46
|
+
- **Slice types** — **State Change** (command → events), **State View** (events → read model), **Automation** (events → side effect, including a follow-up command or event — a Screenplay `produces` / `invokes`), **Translation** (data from *outside* the model's own facts → our own facts: an external system's or another service's data recorded as events we own, keeping the external fact distinct from the local one). A reactor that turns our own event into a follow-up event or command is an Automation, not a Translation.
|
|
47
|
+
- **Concept** — a `ConceptAs<T>`, a strongly-typed wrapper over a primitive domain value (never raw `Guid`/`string`/`int`).
|
|
48
|
+
- **EventSourceId<T>** — the strongly-typed event-source identity; derive entity identities from this, not `ConceptAs<Guid>`.
|
|
49
|
+
- **Proxy** — the generated TypeScript command/query client, produced from C# on a **Debug** build; carries a `// @generated by Cratis` header and is never hand-edited.
|
|
50
|
+
|
|
51
|
+
## Compliance & multi-tenancy
|
|
52
|
+
|
|
53
|
+
- **Subject / `[Subject]`** — the natural person a piece of PII belongs to (for GDPR erasure); defaults to the `EventSourceId<T>` identity.
|
|
54
|
+
- **`[PII]`** — marks an inherently personal value so Chronicle can manage/erase it.
|
|
55
|
+
- **`[NotAudited]`** — an **Arc** marking (`Cratis.Arc.Chronicle.Commands`, not a Chronicle type) for a value that is secret but *not* personal data (password, token, API key), so it is never written to the causation chain. Withholds only; it does not encrypt the value, so pair it with `[Encrypted]` for an operational secret. It never enrolls a value in erasure the way Chronicle's `[PII]` does.
|
|
56
|
+
- **`[Encrypted]`** — a Chronicle marking (`Cratis.Chronicle.ProtectedValues`, Chronicle 19.32.0+) that encrypts an **operational secret** (API key, token, a company's bank account number) at rest on the event, without erasure. It does not keep a command input off the causation chain, so a secret gets `[Encrypted]` **plus** `[NotAudited]`. Never combine it with `[PII]` (`CHR0053`); never put it on an event source id (`CHR0052`).
|
|
57
|
+
- **`[OnceOnly]`** — marks a reactor handler (or the whole reactor class) as non-replayable: Chronicle skips it for every event arriving as part of a **replay** — observer rewind, redaction, revision. Replay-exclusion only, not exactly-once, not a per-event-source run limit or deduplication; ordinary catch-up, crash redelivery and failed-partition recovery can run the handler again (Chronicle v19.50.1, `Documentation/code-analysis/CHR0022.mdx`). Use a `ReactorDelivery` receipt for that case.
|
|
58
|
+
- **Namespace / tenant** — Chronicle isolates tenants by **namespace**; each namespace has its own events, observers, and read models.
|
|
59
|
+
|
|
60
|
+
## Profiles
|
|
61
|
+
|
|
62
|
+
- **Application profile** — building an app *on* Cratis (event-sourced CQRS, vertical slices, MVVM frontend).
|
|
63
|
+
- **Framework profile** — contributing to a Cratis framework repo *itself* (Arc, Chronicle, Fundamentals, Components — libraries). See `framework.md`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
profile: framework
|
|
3
|
+
applyTo: "**/*.go,go.mod,go.sum,**/go.mod,**/go.sum"
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*.go"
|
|
6
|
+
- "go.mod"
|
|
7
|
+
- "go.sum"
|
|
8
|
+
- "**/go.mod"
|
|
9
|
+
- "**/go.sum"
|
|
10
|
+
---
|
|
11
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
12
|
+
|
|
13
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
14
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
15
|
+
|
|
16
|
+
# Go framework parity and module releases
|
|
17
|
+
|
|
18
|
+
`cratis/engineering/go` composes the Go language profile. Repository-owned
|
|
19
|
+
context, not this rule, defines module paths, layout, quality-gate commands,
|
|
20
|
+
release configuration and the exact C# parity target.
|
|
21
|
+
|
|
22
|
+
## Preserve the product contract
|
|
23
|
+
|
|
24
|
+
Before changing a Go port, inspect its owning C# API, implementation and tests
|
|
25
|
+
at the parity target revision. Translate concepts idiomatically, not literally:
|
|
26
|
+
context plus errors, constructors/options, explicit registration, named values
|
|
27
|
+
and deliberate stream lifetimes. Do not change sibling implementations to
|
|
28
|
+
accommodate the port.
|
|
29
|
+
|
|
30
|
+
Preserve routes, verbs, camelCase JSON where contracted, envelopes, UUID byte
|
|
31
|
+
order/encoding, protobuf fields and RPCs, tenancy, event ordering, retry and
|
|
32
|
+
checkpoint semantics. Record each deliberate divergence in repository-owned
|
|
33
|
+
parity documentation with source path/revision, Go surface, status, executable
|
|
34
|
+
evidence and caller migration implications. A compilation check is not wire,
|
|
35
|
+
streaming or lifecycle parity. Unsupported behavior fails explicitly, never as
|
|
36
|
+
no-op success. Keep a limited conformance result limited; do not claim whole-product parity.
|
|
37
|
+
|
|
38
|
+
## Release compatibility
|
|
39
|
+
|
|
40
|
+
- Review signatures, interface growth, public struct comparability, wrapped
|
|
41
|
+
errors, defaults and wire behavior. API-diff tools supplement consumer tests.
|
|
42
|
+
- Experimental v0 breaking APIs require a **minor** bump and migration notes,
|
|
43
|
+
not a patch. From v1, incompatible APIs need a planned major version.
|
|
44
|
+
- Apply exactly one release-intent label under the repository's PR policy.
|
|
45
|
+
Check how its configured `Cratis/release-action` maps v0 changes before release.
|
|
46
|
+
- Root modules use immutable `vX.Y.Z` tags created through the configured
|
|
47
|
+
release-action workflow. Do not create duplicate manual releases or move tags.
|
|
48
|
+
Nested modules need their own explicitly reviewed tag policy.
|
|
49
|
+
- Plan `/v2` migrations across the module directive, imports, examples, consumers
|
|
50
|
+
and parallel support. Neither v0 nor v1 gets a `/v0` or `/v1` suffix.
|
|
51
|
+
- Correct a bad release with a new release and, when appropriate, documented
|
|
52
|
+
retractions in a newer published version. Retraction does not erase downloads
|
|
53
|
+
or prohibit an explicit request for the old version.
|
|
54
|
+
- Verify release consumption without local workspace/replacement assistance.
|
|
55
|
+
Source/tag availability, module download, consumer compilation and proxy/pkg.go.dev
|
|
56
|
+
indexing are separate results. Indexing delay never justifies retagging.
|
|
57
|
+
|
|
58
|
+
Adapted from the repository-owned
|
|
59
|
+
[Chronicle.Go parity rule](https://github.com/Cratis/Chronicle.Go/blob/v0.9.1/.cratis/ai/rules/go-cratis-parity.md)
|
|
60
|
+
and [repository-owned module release guidance](https://github.com/Cratis/Chronicle.Go/tree/v0.9.1/.cratis/ai/skills),
|
|
61
|
+
with repository-specific paths and commands removed.
|