@cratis/pi 2.22.0 → 2.23.1
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/agents/backend-developer.md +6 -5
- package/package/corpus/agents/code-reviewer.md +7 -7
- package/package/corpus/agents/coordinator.md +5 -4
- package/package/corpus/agents/frontend-developer.md +34 -40
- package/package/corpus/agents/orchestrator.md +5 -4
- package/package/corpus/agents/performance-reviewer.md +5 -4
- package/package/corpus/agents/planner.md +5 -4
- package/package/corpus/agents/repository-investigation-reviewer.md +2 -1
- package/package/corpus/agents/repository-investigator.md +2 -1
- package/package/corpus/agents/security-reviewer.md +5 -4
- package/package/corpus/agents/slice-implementer.md +8 -2
- package/package/corpus/agents/spec-writer.md +6 -4
- package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +233 -10
- package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +52 -7
- package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
- package/package/corpus/hooks/README.md +29 -27
- package/package/corpus/hooks/agent-stop.md +1 -5
- package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
- package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
- package/package/corpus/prompts/ship-changes.prompt.md +4 -0
- package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
- package/package/corpus/rules/ai-distribution.md +60 -0
- package/package/corpus/rules/application-profile.md +73 -0
- package/package/corpus/rules/components.md +77 -44
- package/package/corpus/rules/dialogs.md +24 -20
- package/package/corpus/rules/efcore.md +35 -187
- package/package/corpus/rules/framework.md +1 -1
- package/package/corpus/rules/frontend-quality.md +1 -1
- package/package/corpus/rules/frontend-testing.md +4 -4
- package/package/corpus/rules/general.md +21 -162
- package/package/corpus/rules/guards-and-fuses.md +24 -10
- package/package/corpus/rules/local-work-artifacts.md +6 -0
- package/package/corpus/rules/new-repository-intake.md +34 -0
- package/package/corpus/rules/orleans.md +13 -27
- package/package/corpus/rules/profiles.md +6 -0
- package/package/corpus/rules/react.md +12 -12
- package/package/corpus/rules/reactors.md +28 -5
- package/package/corpus/rules/rtk.md +13 -3
- package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
- package/package/corpus/rules/specs.typescript.md +1 -1
- package/package/corpus/rules/storybook.md +2 -2
- package/package/corpus/rules/typescript.md +1 -0
- package/package/corpus/rules/verification-discipline.md +7 -1
- package/package/corpus/rules/vertical-slices.md +20 -15
- package/package/corpus/rules/writing-correct-examples.md +1 -0
- package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
- package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
- package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
- package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
- package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
- package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
- package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
- package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
- package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
- package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
- package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
- package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
- package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
- package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
- package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
- package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
- package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
- package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
- package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
- package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
- package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
- package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
- package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
- package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
- package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
- package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
- package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
- package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
- package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
- package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
- package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
- package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
- package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
- package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
- package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
- package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
- package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
- package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
- package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
- package/package.json +1 -1
- package/package/corpus/rules/terminal-commands.md +0 -19
|
@@ -23,6 +23,13 @@ export interface AgentConfig {
|
|
|
23
23
|
name: string;
|
|
24
24
|
description: string;
|
|
25
25
|
tools?: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Set when the agent declared a non-empty `tools` list of which nothing maps to a Pi tool. Such an
|
|
28
|
+
* agent must not launch: dropping the whole list would hand a deliberately restricted agent (a
|
|
29
|
+
* read-only reviewer) Pi's full default toolset. Mirrors Claude Code, which refuses to launch a
|
|
30
|
+
* subagent whose `tools` list resolves to no tool.
|
|
31
|
+
*/
|
|
32
|
+
toolsError?: string;
|
|
26
33
|
model?: string;
|
|
27
34
|
systemPrompt: string;
|
|
28
35
|
source: "package" | "user" | "project";
|
|
@@ -70,22 +77,59 @@ const TOOL_NAME_MAP: Record<string, string | null> = {
|
|
|
70
77
|
websearch: null,
|
|
71
78
|
};
|
|
72
79
|
|
|
80
|
+
/**
|
|
81
|
+
* The outcome of normalizing a frontmatter `tools` value.
|
|
82
|
+
*
|
|
83
|
+
* `tools` is the de-duplicated Pi allowlist, or `undefined` when the agent declared no tools at
|
|
84
|
+
* all (it then inherits Pi's full default toolset, exactly like an omitted `tools:` in Claude Code).
|
|
85
|
+
* `unresolved` lists every declared name that is neither a Pi built-in nor a known orchestration
|
|
86
|
+
* tool, so a caller can tell "nothing declared" apart from "declared, but nothing resolved".
|
|
87
|
+
*/
|
|
88
|
+
export interface NormalizedTools {
|
|
89
|
+
tools?: string[];
|
|
90
|
+
unresolved: string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
73
93
|
/**
|
|
74
94
|
* Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names.
|
|
75
95
|
* Accepts both YAML spellings in use (`tools: [Read, Bash]` and `tools: Read, Bash`).
|
|
76
|
-
* Returns `undefined` when nothing maps, so the subagent inherits Pi's full default
|
|
77
|
-
* toolset rather than being launched with an empty allowlist.
|
|
78
96
|
*/
|
|
79
|
-
export function
|
|
97
|
+
export function normalizeToolsDetailed(value: unknown): NormalizedTools {
|
|
80
98
|
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
81
|
-
const
|
|
99
|
+
const declared = raw
|
|
82
100
|
.filter((t): t is string => typeof t === "string")
|
|
83
|
-
.map((t) => t.trim()
|
|
84
|
-
.filter(Boolean)
|
|
101
|
+
.map((t) => t.trim())
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
const mapped = declared
|
|
104
|
+
.map((t) => t.toLowerCase())
|
|
85
105
|
.map((t) => (t in TOOL_NAME_MAP ? TOOL_NAME_MAP[t] : null))
|
|
86
106
|
.filter((t): t is string => typeof t === "string");
|
|
107
|
+
const unresolved = declared.filter((t) => !(t.toLowerCase() in TOOL_NAME_MAP));
|
|
87
108
|
const deduped = Array.from(new Set(mapped));
|
|
88
|
-
return deduped.length > 0 ? deduped : undefined;
|
|
109
|
+
return { tools: deduped.length > 0 ? deduped : undefined, unresolved };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names, or `undefined`
|
|
114
|
+
* when nothing maps. Prefer {@link normalizeToolsDetailed} where the difference between an omitted
|
|
115
|
+
* list and an unresolvable one matters — it always does when the result gates a launch.
|
|
116
|
+
*/
|
|
117
|
+
export function normalizeTools(value: unknown): string[] | undefined {
|
|
118
|
+
return normalizeToolsDetailed(value).tools;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The launch error for an agent whose declared tools resolve to nothing, or `undefined` when the
|
|
123
|
+
* agent either declared no tools or at least one of them resolved.
|
|
124
|
+
*/
|
|
125
|
+
export function toolsErrorFor(name: string, value: unknown): string | undefined {
|
|
126
|
+
const { tools, unresolved } = normalizeToolsDetailed(value);
|
|
127
|
+
if (tools !== undefined || unresolved.length === 0) return undefined;
|
|
128
|
+
return (
|
|
129
|
+
`Agent "${name}" declares tools that resolve to no Pi tool: ${unresolved.join(", ")}. ` +
|
|
130
|
+
`It would launch with every tool instead of the restriction it declares, so it is not launched. ` +
|
|
131
|
+
`Declare Pi-resolvable names (Read, Write, Edit, Bash, Grep, Glob, ls) in its frontmatter.`
|
|
132
|
+
);
|
|
89
133
|
}
|
|
90
134
|
|
|
91
135
|
function loadAgentsFromDir(dir: string, source: "package" | "user" | "project"): AgentConfig[] {
|
|
@@ -119,6 +163,7 @@ function loadAgentsFromDir(dir: string, source: "package" | "user" | "project"):
|
|
|
119
163
|
name: frontmatter.name,
|
|
120
164
|
description: frontmatter.description,
|
|
121
165
|
tools: normalizeTools(frontmatter.tools),
|
|
166
|
+
toolsError: toolsErrorFor(frontmatter.name, frontmatter.tools),
|
|
122
167
|
model: typeof frontmatter.model === "string" ? frontmatter.model.trim() : undefined,
|
|
123
168
|
systemPrompt: body,
|
|
124
169
|
source,
|
|
@@ -96,6 +96,20 @@ async function runSingleAgent(
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// A restricted agent whose restriction cannot be expressed in Pi is not launched at all: launching it
|
|
100
|
+
// with the full toolset would be the opposite of what its author declared (see AgentConfig.toolsError).
|
|
101
|
+
if (agent.toolsError) {
|
|
102
|
+
return {
|
|
103
|
+
agent: agentName,
|
|
104
|
+
agentSource: agent.source,
|
|
105
|
+
task,
|
|
106
|
+
exitCode: 1,
|
|
107
|
+
finalText: "",
|
|
108
|
+
stderr: agent.toolsError,
|
|
109
|
+
step,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
// An agent that pins no model inherits the dispatching session's model + thinking level.
|
|
100
114
|
const model = agent.model ?? dispatch.model;
|
|
101
115
|
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
@@ -13,24 +13,27 @@ Three layers:
|
|
|
13
13
|
| Quality gate | `Stop` | `scripts/cratis-quality-gate.sh` | one build/test run, only when relevant files changed | exits **2** — the turn does not end |
|
|
14
14
|
|
|
15
15
|
The Claude Code wiring that fires them is tracked here, in
|
|
16
|
-
[`settings.template.json`](./settings.template.json). Claude reads `.claude/settings.json
|
|
17
|
-
|
|
16
|
+
[`settings.template.json`](./settings.template.json). Claude reads `.claude/settings.json`. In a
|
|
17
|
+
repository set up with `cratis ai install`, that file is a **symlink** to this template and follows
|
|
18
|
+
every `cratis ai update`; there is nothing to copy. Where the corpus is present without the CLI,
|
|
19
|
+
activate the hooks by copying the template once:
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
cp .cratis/ai/hooks/settings.template.json .claude/settings.json
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
If you already have a `.claude/settings.json
|
|
24
|
-
than overwriting — the rest of that file is yours
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`scripts/validate-ai-setup.sh` checks the template against the script names this page documents.
|
|
25
|
+
If you already have a `.claude/settings.json` of your own, merge the template's `hooks` block into it
|
|
26
|
+
rather than overwriting — the rest of that file is yours — and re-copy after the template changes.
|
|
27
|
+
**Edit the template, never the copy or the managed file**: `.cratis/ai/` is the source of truth (see the
|
|
28
|
+
[corpus README](../README.md)), and `cratis ai status` reports a hand-edited managed file as drift.
|
|
28
29
|
|
|
29
30
|
The markdown files in this folder (`agent-stop.md`, `pre-commit.md`) remain *lifecycle guidance* —
|
|
30
31
|
they describe what a hook should do for tools that have no wiring yet.
|
|
31
32
|
|
|
32
|
-
> Hooks are the one surface with no folder adapter: Claude reads `.claude/settings.json
|
|
33
|
-
>
|
|
33
|
+
> Hooks are the one surface with no folder adapter: Claude Code reads `.claude/settings.json`;
|
|
34
|
+
> the Pi harness bridges the same three scripts to its own events through the `cratis-hooks`
|
|
35
|
+
> extension under `../harnesses/pi/extensions/`; Copilot would read `.github/hooks/*.json`, and no
|
|
36
|
+
> Copilot wiring ships yet.
|
|
34
37
|
|
|
35
38
|
## What is enforced
|
|
36
39
|
|
|
@@ -83,13 +86,16 @@ the managed gates running unchanged.
|
|
|
83
86
|
`general.md` and the steps in [`agent-stop.md`](./agent-stop.md) — Debug build, specs, Release
|
|
84
87
|
build (with `-p:CratisProxiesOutputPath=` per `general.md`, so the proxy generator does not
|
|
85
88
|
re-run and touch already-correct generated files), frontend lint / compile / compile-specs /
|
|
86
|
-
test
|
|
89
|
+
test.
|
|
87
90
|
|
|
88
|
-
## The
|
|
91
|
+
## The package drift guards
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
Three scripts sit outside the three layers and are not bound to a hook event: run
|
|
94
|
+
`scripts/validate-package-subpaths.sh` directly and it chains `validate-type-references.sh` and
|
|
95
|
+
`validate-package-imports.sh` over the same roots. They check the corpus text against the packages a
|
|
96
|
+
repository actually has installed, so they are meaningful only where `node_modules` or a NuGet cache
|
|
97
|
+
exists. The corpus's own structure, catalog and adapters are verified in the `Cratis/AI` repository
|
|
98
|
+
before anything is published; a consuming repository checks its installed copy with `cratis ai status`.
|
|
93
99
|
|
|
94
100
|
### Package subpath existence — `scripts/validate-package-subpaths.sh` (warn)
|
|
95
101
|
|
|
@@ -113,11 +119,9 @@ and the `ai-corpus` CI job checks out the tree and installs nothing — so faili
|
|
|
113
119
|
permanent no-op in CI while turning repos red locally for their own dependency pin. The warning
|
|
114
120
|
names the file, the line and the installed version, and leaves the judgement to a human.
|
|
115
121
|
|
|
116
|
-
>
|
|
117
|
-
>
|
|
118
|
-
>
|
|
119
|
-
> hooks do in a **consuming** repository. Here they are silent, which is the designed
|
|
120
|
-
> behavior, not a broken setup.
|
|
122
|
+
> Every `.cs` / `.ts` / `Source/**` reference below describes what the hooks do in a repository
|
|
123
|
+
> that has such a project. Where there is none — a documentation or corpus-only repository — the
|
|
124
|
+
> hooks are silent, which is the designed behavior, not a broken setup.
|
|
121
125
|
|
|
122
126
|
**Silent when it cannot judge.** No `jq`, no `node_modules`, a package this repository does not
|
|
123
127
|
depend on, or a package published without an `exports` map: skipped without a word. "Not installed"
|
|
@@ -137,8 +141,7 @@ specifiers, nothing else.
|
|
|
137
141
|
|
|
138
142
|
Run it standalone, optionally over other roots, and add `CRATIS_HOOKS_SUBPATH_REPORT=1` to see every
|
|
139
143
|
reference and how it resolved rather than only the failures. It invokes Tier 3 before its own gates
|
|
140
|
-
and Tier 2 after its own work, over the same roots, so
|
|
141
|
-
`validate-ai-setup.sh` gets all three.
|
|
144
|
+
and Tier 2 after its own work, over the same roots, so one call gets all three.
|
|
142
145
|
|
|
143
146
|
### Named import existence — `scripts/validate-package-imports.sh` (warn)
|
|
144
147
|
|
|
@@ -247,7 +250,7 @@ phrases and the guard stays quiet.
|
|
|
247
250
|
**Silent when it cannot judge.** No `Directory.Packages.props`, no local NuGet cache, or a cache
|
|
248
251
|
holding none of the pinned versions: skipped without a word. It needs no `jq` and no `node_modules`,
|
|
249
252
|
which is why Tier 1 invokes it *above* its own gates rather than beside the Tier 2 call — a backend-
|
|
250
|
-
only repository must still get this check. It adds about 1.4 s to
|
|
253
|
+
only repository must still get this check. It adds about 1.4 s to the chained run.
|
|
251
254
|
|
|
252
255
|
**The allowlist — `scripts/type-references-allowlist.txt`.** Thirteen entries, each with a written
|
|
253
256
|
justification: ASP.NET Core and BCL attributes that live in ref packs (which ship no XML docs at
|
|
@@ -288,15 +291,15 @@ gate script find it — `workingDirectoryFrom: ["*.slnx", "*.sln", "**/*.slnx",
|
|
|
288
291
|
`dotnet build` in whichever directory holds the repository's own solution, preferring one at the
|
|
289
292
|
root because the globs are tried in order. The frontend gates discover `package.json` the same way.
|
|
290
293
|
The same shipped file therefore activates in an application repository, activates in a framework
|
|
291
|
-
repository, and stays quiet in a
|
|
294
|
+
repository, and stays quiet in a repository that has no project at all.
|
|
292
295
|
|
|
293
296
|
**Overriding it, in order of increasing force.** Set `workingDirectory` on a gate to pin one of
|
|
294
297
|
several candidate projects; drop a `quality-gates.json` of your own in place of the shipped one; or
|
|
295
298
|
point `CRATIS_HOOKS_GATES` at a file anywhere. None of them requires forking the script.
|
|
296
299
|
|
|
297
300
|
**Profile note.** The C# patterns are application-profile and scoped to `Source/**/*.cs`, which is
|
|
298
|
-
the application source root [`../rules/general.md`](../rules/general.md) documents
|
|
299
|
-
|
|
301
|
+
the application source root [`../rules/general.md`](../rules/general.md) documents. A
|
|
302
|
+
framework-profile repository (Arc, Chronicle,
|
|
300
303
|
Fundamentals, Components — see [`../rules/framework.md`](../rules/framework.md)) has no vertical
|
|
301
304
|
slices and should disable them in its `cratis-patterns.local.json`; a repository whose application
|
|
302
305
|
source root is not `Source/` re-scopes the `paths` globs there too.
|
|
@@ -350,8 +353,7 @@ Each is an explicit, auditable opt-out — none of them is a default.
|
|
|
350
353
|
The scripts read hook JSON on stdin, so they are directly testable:
|
|
351
354
|
|
|
352
355
|
The pattern pass and the gate both read the repository they are pointed at, so testing them means
|
|
353
|
-
pointing them at a repository that *has* the thing under test
|
|
354
|
-
project, so run those two against a consuming checkout (or a scratch tree), and expect silence here.
|
|
356
|
+
pointing them at a repository that *has* the thing under test; where it is absent, expect silence.
|
|
355
357
|
|
|
356
358
|
```bash
|
|
357
359
|
# Pattern pass — expect exit 0, and JSON on stdout only when something matched.
|
|
@@ -10,11 +10,7 @@ When the agent finishes a session, verify the work against **fresh signals** bef
|
|
|
10
10
|
|
|
11
11
|
## Pick the path for this repository
|
|
12
12
|
|
|
13
|
-
- **
|
|
14
|
-
```
|
|
15
|
-
.cratis/ai/hooks/scripts/validate-ai-setup.sh
|
|
16
|
-
```
|
|
17
|
-
Stop only when it passes (symlinks/adapters healthy, frontmatter present, no broken cross-links). Skip the application gates below.
|
|
13
|
+
- **No .NET solution or frontend** — a documentation or corpus-only repository. Run that repository's own documentation and corpus checks instead of a code build, and `cratis ai status` when the change touched the installed AI corpus or its adapters (a hand-edited managed file is reported as drift). Skip the application gates below.
|
|
18
14
|
|
|
19
15
|
- **Application repo** — there is a .NET solution and/or a frontend. Run the application gates below.
|
|
20
16
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"pattern": "from[[:space:]]+['\"]primereact/dialog['\"]",
|
|
79
79
|
"paths": ["**/*.ts", "**/*.tsx"],
|
|
80
80
|
"exclude_paths": ["**/node_modules/**", "**/dist/**", "**/wwwroot/**", "**/obj/**", "**/bin/**"],
|
|
81
|
-
"message": "
|
|
81
|
+
"message": "Cratis Components 4 has no PrimeReact underneath — a `primereact/dialog` import is a leftover from Components 3. Use `CommandDialog` from `@cratis/components/CommandDialog` or `Dialog` from `@cratis/components/Dialogs`."
|
|
82
82
|
}
|
|
83
83
|
]
|
|
84
84
|
}
|
|
@@ -113,8 +113,7 @@ done
|
|
|
113
113
|
|
|
114
114
|
# Tier 2 over the same roots: a subpath that resolves says nothing about the *names* imported
|
|
115
115
|
# through it. Kept in its own script — a different question, a different corpus extraction and a
|
|
116
|
-
# different report variable — and invoked from here so
|
|
117
|
-
# gets both. Tested with -f, not -x, and run through `bash`: a checkout that lost the exec bit must
|
|
116
|
+
# different report variable — and invoked from here so one call to this script gets both. Tested with -f, not -x, and run through `bash`: a checkout that lost the exec bit must
|
|
118
117
|
# not silently drop the guard.
|
|
119
118
|
imports="$(dirname "${BASH_SOURCE[0]}")/validate-package-imports.sh"
|
|
120
119
|
if [[ -f "$imports" ]]; then bash "$imports" "$@" || true; fi
|
|
@@ -21,3 +21,7 @@ Invoking this prompt is direct authority for the standard branch, commit, push,
|
|
|
21
21
|
requested-label, merge, and branch-cleanup effects. Do not pause to ask for separate approval at
|
|
22
22
|
each step. Follow the repository's Git commit and pull-request rules, use a true merge commit,
|
|
23
23
|
and verify required checks before merging.
|
|
24
|
+
|
|
25
|
+
This prompt is for an explicit request to ship or land. If the user asked only to commit, only to
|
|
26
|
+
push, or only to open a PR, stop after that step: the narrower request does not become authority
|
|
27
|
+
for the rest of the chain because this prompt happens to be loaded.
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
agent: agent
|
|
3
|
-
description:
|
|
3
|
+
description: Check the installed Cratis AI corpus for drift, conflicts, and healthy harness adapters.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Verify AI Setup
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Check the repository's installed Cratis AI setup by running:
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
|
|
11
|
+
cratis ai status
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
It reports the configured harnesses, profiles and languages, the installed corpus revision against the
|
|
15
|
+
available one (`updateAvailable`), and every managed file that was modified locally — exiting non-zero
|
|
16
|
+
when there is any. (A user-owned path that collides with a managed one is reported and refused by
|
|
17
|
+
`cratis ai install` / `cratis ai update`, not by `status`.) Then confirm
|
|
18
|
+
the harness adapters this repository uses (`.claude/`, `.agents/`, `.github/`, `.pi/`, `.cursor/`,
|
|
19
|
+
`.opencode/` as applicable) still resolve into `.cratis/ai/` — a broken or dangling symlink is a setup
|
|
20
|
+
fault, not corpus drift.
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
If anything is reported:
|
|
23
|
+
|
|
24
|
+
1. List every finding with the exact file path.
|
|
25
|
+
2. Explain whether it is a hand-edited managed file, a pending update, or a broken adapter.
|
|
26
|
+
3. Propose the smallest safe fix. A managed file is never patched by hand — `cratis ai update`
|
|
27
|
+
replaces it (with `--force` only for content already recorded as Cratis-managed); a user-owned
|
|
28
|
+
file is the repository's and stays.
|
|
19
29
|
4. Apply fixes if requested.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/.cratis/**"
|
|
3
|
+
paths:
|
|
4
|
+
- "**/.cratis/**"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Shared AI Distribution
|
|
8
|
+
|
|
9
|
+
How the shared Cratis AI corpus reaches a repository, and what a consuming
|
|
10
|
+
repository must and must not do with it. The always-loaded instructions keep only
|
|
11
|
+
the two invariants; this rule carries the mechanics.
|
|
12
|
+
|
|
13
|
+
Do not copy or synchronize shared `.cratis/ai`, `.agents`, `.claude`, `.github`, or
|
|
14
|
+
`.pi` trees from one Cratis repository to another. A consuming repository must
|
|
15
|
+
never become an accidental source that republishes its local AI corpus.
|
|
16
|
+
|
|
17
|
+
Shared Cratis capabilities are authored and reviewed in `Cratis/AI` and reach a
|
|
18
|
+
repository through one of three channels, each with its own version semantics:
|
|
19
|
+
|
|
20
|
+
- **`cratis ai install` / `cratis ai update`** copy the corpus into `.cratis/ai/`
|
|
21
|
+
from the current `main` of `Cratis/AI` (or an explicit `--source` path), record
|
|
22
|
+
the source commit and every installed file's hash in `.cratis/ai.manifest.json`,
|
|
23
|
+
and create native harness adapters as symlinks into that managed copy. There is
|
|
24
|
+
no version pin on this channel: an update takes whatever `main` holds. Agents,
|
|
25
|
+
prompts, and hooks are always installed; rules are filtered by profile and
|
|
26
|
+
language; skills by the profile catalog; `harnesses/<harness>/` only for the
|
|
27
|
+
harnesses selected.
|
|
28
|
+
- **`@cratis/pi`** is the one versioned channel: an npm package cut per release,
|
|
29
|
+
pinned and rolled back by package version. It yields to a managed install when
|
|
30
|
+
`.cratis/ai.manifest.json` exists.
|
|
31
|
+
- **Native plugin marketplaces** (Claude Code, Codex, Copilot, Cursor) expose the
|
|
32
|
+
skills only, from the unpinned GitHub source.
|
|
33
|
+
|
|
34
|
+
Keep existing repository-local AI files in place while a replacement corpus is
|
|
35
|
+
under canary; do not restart legacy all-to-all propagation and do not delete
|
|
36
|
+
legacy adapters before reviewed retirement evidence exists.
|
|
37
|
+
|
|
38
|
+
Shared public product and `engineering-*` packages contain only public-safe
|
|
39
|
+
Cratis behavior. The `engineering-` prefix identifies the maintainer audience;
|
|
40
|
+
it does not imply confidential package contents or a private registry.
|
|
41
|
+
|
|
42
|
+
The consuming repository owns its project facts, confidential behavior, local
|
|
43
|
+
skills, and minimal host bootstraps. Use repository-owned documentation as canonical
|
|
44
|
+
project context when that migration is active, `.agents/skills/` for private or
|
|
45
|
+
repository-specific local workflows, and repository-owned documentation only as the
|
|
46
|
+
documented legacy context fallback. Never merge, overwrite, or remove these
|
|
47
|
+
local files as a side effect of installing, updating, rolling back, or
|
|
48
|
+
uninstalling shared AI capabilities.
|
|
49
|
+
|
|
50
|
+
Keep confidential and repository-specific behavior local. Generalize and remove
|
|
51
|
+
private facts before proposing a reusable improvement to `Cratis/AI`; never
|
|
52
|
+
reverse-sync a private repository's AI tree or generated adapters.
|
|
53
|
+
|
|
54
|
+
Update shared AI through the channel's own mechanism (`cratis ai update`, or the
|
|
55
|
+
`@cratis/pi` package version). Canary the change, observe its behavior and gates,
|
|
56
|
+
and roll back the same way — by package version where one exists, otherwise by
|
|
57
|
+
reinstalling from a known-good source commit with `--source`. Never patch managed
|
|
58
|
+
files under `.cratis/ai/`, generated adapters, or marketplace wrappers by hand;
|
|
59
|
+
`cratis ai status` reports such drift and `update` refuses it without `--force`.
|
|
60
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*"
|
|
3
|
+
profile: application
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Application profile
|
|
7
|
+
|
|
8
|
+
> The following — **Project Layout, Slice Types, Slice Naming, the Rules, and the Implementation Workflow** — applies when **building an application on Cratis**. If you are contributing to a Cratis framework repo, skip to **Framework profile** below and follow [framework.md](./framework.md).
|
|
9
|
+
|
|
10
|
+
## Project Layout (Cratis Application convention)
|
|
11
|
+
|
|
12
|
+
The framework discovers commands and read models by attributes and static methods — **the folder shape is a convention, not a requirement.** The house default: the domain hierarchy lives directly under the app source root (**no top-level `Features/` wrapper**), cross-cutting concerns (`Common/` concepts, `Identity/`, shared components) at the top level, and one folder per behavior — `<Module>/<Feature>/<Slice>/` holding `<Slice>.cs`, the slice's `.tsx`, and its `when_*/` specs. **The slice is the invariant unit** — created, renamed, and deleted as one. Namespace mirrors the path under the source root, dropping any level that isn't present. The full tree and the split rules are in [vertical-slices.md](./vertical-slices.md).
|
|
13
|
+
|
|
14
|
+
## Slice Types
|
|
15
|
+
|
|
16
|
+
Pick exactly one type per slice folder — determined by what the slice *does*.
|
|
17
|
+
|
|
18
|
+
| Type | What it does | Contents |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` read model for command-side decisions |
|
|
21
|
+
| **State View** | Projects events into a queryable read model | `[ReadModel]` + model-bound projection + static query method(s) |
|
|
22
|
+
| **Automation** | Reacts to events, calls external systems / `ICommandPipeline` | Reactor only |
|
|
23
|
+
| **Translation** | Reacts to events and appends follow-up events to another stream | Reactor only |
|
|
24
|
+
|
|
25
|
+
## Slice Naming (convention)
|
|
26
|
+
|
|
27
|
+
Commands are imperative intents (`Register`, `Create`); the slice folder is the action only, never repeating a noun the Feature already establishes. **`[EventType]` records are past-tense facts** and must be self-describing (`AuthorRegistered`, never `Created`) — this past-tense, one-purpose naming is a Chronicle framework recommendation. Static query methods are descriptive reads (`AllAuthors`, `AuthorById`, `AuthorsByName`).
|
|
28
|
+
|
|
29
|
+
## Rules
|
|
30
|
+
|
|
31
|
+
Tagged **[contract]** (framework-enforced) or **[convention]** (house default). Mechanics and examples live in `vertical-slices.md`.
|
|
32
|
+
|
|
33
|
+
1. **[contract] Model-bound — no controllers.** Commands are `[Command]` records with a public instance `Handle()` (Arc analyzers enforce this); queries are `static` methods on `[ReadModel]` records; projections/constraints/authorization use attributes. Arc generates the HTTP surface. Drop to fluent (`IProjectionFor<T>`, `IConstraint`) only when model-bound can't express the rule.
|
|
34
|
+
2. **[contract] Command validation & data flow.** Put command rejection in `CommandValidator<T>`, global value invariants in `ConceptValidator<T>`, and fetched/computed handler data in **`Provide()`** (runs after validation/authorization; may short-circuit with `ValidationResult.Error` / `Result<TProvided, ValidationResult>`). Keep `Handle()` focused on event construction. For a state-dependent rule that must hold **under concurrency**, inject the read model into `Handle()` and return a typed error via `Result<TEvent, ValidationResult>`. **Throwing from `Provide()`/`Handle()` is an exception (HTTP 500), not a validation rejection** — throw only for genuinely exceptional conditions, never for normal business rejection.
|
|
35
|
+
3. **[contract] Event-source id resolution:** `ICanProvideEventSourceId` first; else the first qualifying property in declaration order — an `EventSourceId`/`EventSourceId<T>`-derived value *or* a `[Key]` property (no priority between them; two candidates is `ARCCHR0002`); else Arc generates one. A value actually used as a Chronicle stream identity derives from `EventSourceId<T>` with the underlying `IComparable` primitive — never `ConceptAs<Guid>` for that stream identity. `NotSet`, `New()`, and primitive→derived-id operators are optional domain/API conveniences, not Chronicle requirements; typed empty/zero values are real specified stream IDs, not `EventSourceId.Unspecified`.
|
|
36
|
+
4. **[contract] Events never carry the event-source id** — it is implicit in the event context.
|
|
37
|
+
5. **[contract] `[Key]` / `[Subject]` are distinct.** `[Key]` is for event-source/read-model/projection key resolution; `[Subject]` is compliance identity only. Don't put either on an `EventSourceId<T>` value (it already is both); use them only for non-`EventSourceId<T>` values.
|
|
38
|
+
6. **[contract] Avoid nullable event properties** — Chronicle's analyzer warns on them. Model optional facts as a separate event; resolve nullable command inputs to a non-null sentinel before constructing the event.
|
|
39
|
+
7. **[contract] `[EventType]` takes no arguments for new events** — the type name is the identifier. Use `generation:`/id only when evolving an existing contract; schema changes get a new generation + an `EventTypeMigration<TUpgrade,TPrevious>` (never edit stored-event semantics silently). An enum that only gains a member, or has one renamed, is the exception — Chronicle accepts that in place; a *removed* or *renumbered* member still needs a generation, plus a value map saying what the old values became.
|
|
40
|
+
8. **[convention] Every `[EventType]` has an XML `<summary>`** — a Cratis C# documentation convention (not a Chronicle rule); events live in the log forever, so record why they exist.
|
|
41
|
+
9. **[convention] `[ReadModel]` properties carry no default values** except semantically meaningful enum initial states and `[SetValue<T>]`-driven `bool` flags. False defaults hide missing projection wiring.
|
|
42
|
+
10. **[contract] AutoMap is on by default — never call `.AutoMap()`.** Match property names so AutoMap wires them; diverge with `[SetFrom<T>]` / fluent `.Set().To()` only for genuine name differences. Re-enable `.AutoMap()` only inside a scope where it was disabled with `.NoAutoMap()`.
|
|
43
|
+
11. **[contract] Projections consume events and event context — never other read models.** Default to model-bound attributes; use fluent `IProjectionFor<T>` for joins/nested/context/transforms; use a reducer when the model is "current state + event → next state" (a valid style, not a failure mode).
|
|
44
|
+
12. **[contract] Model-bound query custom paths use `[Path("...")]`** (`PathAttribute`), not ASP.NET `[Route]`. Reserve `[Route]` for controller-based endpoints (which this convention avoids).
|
|
45
|
+
13. **[convention] Cross-slice access is read-only through Chronicle** — inject another slice's read model or reference its events; never instantiate or DI another slice's command/handler/service.
|
|
46
|
+
14. **[contract] Never inject `IEventLog` into `Handle()`** — express appends through return types (`IEnumerable<object>` with `EventForEventSourceId` wrappers for cross-stream). In application reactors, return side-effect events or commands (or use `ICommandPipeline` and inspect the result); don't reach for `IEventLog` directly.
|
|
47
|
+
15. **[contract] Never edit a generated file** — proxies carry a `// @generated by Cratis` header. Fix the C# source and rebuild.
|
|
48
|
+
16. **[convention] Use the Cratis dialogs** — `CommandDialog` from `@cratis/components/CommandDialog`, `Dialog` from `@cratis/components/Dialogs`; never a vendor or hand-rolled modal. The default frontend stack is Cratis Components **4.x** (Components-owned markup, `--cratis-*` tokens, `pt`/`data-cratis-part` parts; no PrimeReact dependency) — **not** Tailwind (Tailwind is one supported way to write the token-mapping CSS, not the generic default).
|
|
49
|
+
17. **[convention] One slice is one unit** — creating/renaming/moving/deleting a slice means doing the same to every artifact (the `.cs`, every `when_*/`, every `.tsx`, the composition import/JSX, the route).
|
|
50
|
+
|
|
51
|
+
## Implementation Workflow
|
|
52
|
+
|
|
53
|
+
- **Phase 0 — Model.** Confirm Module/Feature, slice name, slice type, domain rules; for new behavior or unclear event vocabulary run the **event-modeling** skill first.
|
|
54
|
+
- **Phase 1 — Backend.** Implement a coherent slice change. **Gate:** incremental Debug build of the affected project (regenerates proxies, compiles `#if DEBUG` spec code).
|
|
55
|
+
- **Phase 2 — Specs.** Mandatory for every slice type, in-process scenario family first (`CommandScenario<T>`, `EventScenario`, `ReadModelScenario<T>`, `ReactorScenario<T>`). **Gate:** tests pass.
|
|
56
|
+
- **Phase 3 — Frontend.** Build from the generated proxies, register in the composition page, wire routing. **Gate:** lint, conditional test, build.
|
|
57
|
+
|
|
58
|
+
**Backend before frontend, always** — the frontend depends on proxies that exist only after a successful build. Generate proxies from the **Debug** build (house convention); verify Release without re-running the generator: `dotnet build -c Release -p:CratisProxiesOutputPath=`. Why that property, and what to do when nothing is generated: the **cratis-arc-command** skill's proxy-generation reference.
|
|
59
|
+
|
|
60
|
+
## Quality Gates
|
|
61
|
+
|
|
62
|
+
| Phase | Command (app-pinned) | Pass criteria |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Backend | build (Debug) | zero errors, zero warnings — validates `#if DEBUG` spec code and regenerates proxies |
|
|
65
|
+
| Backend | build (Release) | zero errors, zero warnings — build-only check; pass `-p:CratisProxiesOutputPath=` to skip re-running proxy generation |
|
|
66
|
+
| Specs | test | zero failures |
|
|
67
|
+
| Frontend | lint | zero errors |
|
|
68
|
+
| Frontend | test | zero failures when frontend specs/behavior changed |
|
|
69
|
+
| Frontend | build | zero errors |
|
|
70
|
+
|
|
71
|
+
Run affected-project incremental checks after a coherent change, then targeted regression tests for the changed behavior. Re-run a failed gate after a relevant fix. Reserve wider matrices and clean/Release builds for cross-cutting changes, demonstrated stale outputs, or required merge/release gates. Documentation/rule-only edits need relevant Markdown, frontmatter, link, and corpus checks, not an application build. Diagnose unrelated or environmental failures within a bounded attempt; report the evidence and blocker instead of broadening scope or retrying indefinitely. Required gates remain blocking until satisfied; never silently waive red CI.
|
|
72
|
+
|
|
73
|
+
Documentation-only changes carry non-release intent and run the documentation checks, never a blanket exemption from red CI — see [pull-requests.md](./pull-requests.md).
|