@cratis/pi 0.0.1 → 2.0.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.
Files changed (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -37
  3. package/package/corpus/agents/backend-developer.md +125 -0
  4. package/package/corpus/agents/code-reviewer.md +165 -0
  5. package/package/corpus/agents/coordinator.md +163 -0
  6. package/package/corpus/agents/frontend-developer.md +246 -0
  7. package/package/corpus/agents/orchestrator.md +196 -0
  8. package/package/corpus/agents/performance-reviewer.md +109 -0
  9. package/package/corpus/agents/planner.md +145 -0
  10. package/package/corpus/agents/repository-investigation-reviewer.md +45 -0
  11. package/package/corpus/agents/repository-investigator.md +50 -0
  12. package/package/corpus/agents/security-reviewer.md +118 -0
  13. package/package/corpus/agents/slice-implementer.md +59 -0
  14. package/package/corpus/agents/spec-writer.md +149 -0
  15. package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +213 -0
  16. package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +27 -0
  17. package/package/corpus/harnesses/pi/extensions/package.json +4 -0
  18. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +167 -0
  19. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +352 -0
  20. package/package/corpus/hooks/README.md +434 -0
  21. package/package/corpus/hooks/agent-stop.md +49 -0
  22. package/package/corpus/hooks/pre-commit.md +47 -0
  23. package/package/corpus/hooks/scripts/cratis-guard-writes.sh +87 -0
  24. package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +11 -0
  25. package/package/corpus/hooks/scripts/cratis-pattern-scan.sh +197 -0
  26. package/package/corpus/hooks/scripts/cratis-patterns.json +84 -0
  27. package/package/corpus/hooks/scripts/cratis-quality-gate.sh +219 -0
  28. package/package/corpus/hooks/scripts/hook-lib.sh +152 -0
  29. package/package/corpus/hooks/scripts/quality-gates.json +261 -0
  30. package/package/corpus/hooks/scripts/type-references-allowlist.txt +71 -0
  31. package/package/corpus/hooks/scripts/validate-package-imports.sh +166 -0
  32. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +120 -0
  33. package/package/corpus/hooks/scripts/validate-type-references.sh +308 -0
  34. package/package/corpus/hooks/settings.template.json +40 -0
  35. package/package/corpus/prompts/add-business-rule.prompt.md +22 -0
  36. package/package/corpus/prompts/add-concept.prompt.md +17 -0
  37. package/package/corpus/prompts/add-ef-migration.prompt.md +24 -0
  38. package/package/corpus/prompts/add-projection.prompt.md +20 -0
  39. package/package/corpus/prompts/add-reactor.prompt.md +22 -0
  40. package/package/corpus/prompts/add-reducer.prompt.md +20 -0
  41. package/package/corpus/prompts/audit-hooks.prompt.md +15 -0
  42. package/package/corpus/prompts/check-doc-drift.prompt.md +21 -0
  43. package/package/corpus/prompts/code-review.prompt.md +9 -0
  44. package/package/corpus/prompts/new-feature.prompt.md +9 -0
  45. package/package/corpus/prompts/new-vertical-slice.prompt.md +18 -0
  46. package/package/corpus/prompts/review-pr.prompt.md +35 -0
  47. package/package/corpus/prompts/review-skill.prompt.md +16 -0
  48. package/package/corpus/prompts/scaffold-feature.prompt.md +16 -0
  49. package/package/corpus/prompts/ship-changes.prompt.md +20 -0
  50. package/package/corpus/prompts/verify-ai-setup.prompt.md +19 -0
  51. package/package/corpus/prompts/write-documentation.prompt.md +21 -0
  52. package/package/corpus/prompts/write-specs.prompt.md +22 -0
  53. package/package/corpus/rules/capability-is-not-authority.md +31 -0
  54. package/package/corpus/rules/code-quality.csharp.md +91 -0
  55. package/package/corpus/rules/code-quality.md +82 -0
  56. package/package/corpus/rules/code-quality.typescript.md +89 -0
  57. package/package/corpus/rules/components.md +207 -0
  58. package/package/corpus/rules/concepts.md +115 -0
  59. package/package/corpus/rules/csharp.md +269 -0
  60. package/package/corpus/rules/dialogs.md +264 -0
  61. package/package/corpus/rules/documentation-structure-and-formatting.md +148 -0
  62. package/package/corpus/rules/documentation.md +90 -0
  63. package/package/corpus/rules/editing-cratis-docs.md +69 -0
  64. package/package/corpus/rules/efcore.md +235 -0
  65. package/package/corpus/rules/efcore.specs.md +44 -0
  66. package/package/corpus/rules/exit-codes-and-wrappers.md +33 -0
  67. package/package/corpus/rules/framework.md +52 -0
  68. package/package/corpus/rules/frontend-quality.md +59 -0
  69. package/package/corpus/rules/frontend-testing.md +126 -0
  70. package/package/corpus/rules/general.md +305 -0
  71. package/package/corpus/rules/git-commits.md +138 -0
  72. package/package/corpus/rules/github-actions.md +92 -0
  73. package/package/corpus/rules/glossary.md +61 -0
  74. package/package/corpus/rules/guards-and-fuses.md +45 -0
  75. package/package/corpus/rules/local-work-artifacts.md +33 -0
  76. package/package/corpus/rules/managing-ai-rules.md +40 -0
  77. package/package/corpus/rules/orleans.md +50 -0
  78. package/package/corpus/rules/pull-requests.md +78 -0
  79. package/package/corpus/rules/react.md +195 -0
  80. package/package/corpus/rules/reactors.md +238 -0
  81. package/package/corpus/rules/rtk.md +37 -0
  82. package/package/corpus/rules/specs.csharp.md +139 -0
  83. package/package/corpus/rules/specs.md +132 -0
  84. package/package/corpus/rules/specs.scenarios.csharp.md +172 -0
  85. package/package/corpus/rules/specs.typescript.md +139 -0
  86. package/package/corpus/rules/storybook.md +84 -0
  87. package/package/corpus/rules/terminal-commands.md +19 -0
  88. package/package/corpus/rules/typescript.md +149 -0
  89. package/package/corpus/rules/verification-discipline.md +21 -0
  90. package/package/corpus/rules/vertical-slices.md +338 -0
  91. package/package/corpus/rules/web-fetching.md +11 -0
  92. package/package/corpus/rules/writing-correct-examples.md +35 -0
  93. package/package/corpus/rules/writing-cratis-docs.md +70 -0
  94. package/package/corpus/skills/cratis-engineering-csharp-conventions/LICENSE +2 -0
  95. package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +131 -0
  96. package/package/corpus/skills/cratis-engineering-csharp-conventions/references/code-style.md +187 -0
  97. package/package/corpus/skills/cratis-engineering-csharp-conventions/references/domain-philosophy.md +91 -0
  98. package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +223 -0
  99. package/package/corpus/skills/cratis-engineering-decision-record/LICENSE +2 -0
  100. package/package/corpus/skills/cratis-engineering-decision-record/SKILL.md +133 -0
  101. package/package/corpus/skills/cratis-engineering-decision-record/references/record-format.md +107 -0
  102. package/package/corpus/skills/cratis-engineering-docs-authoring/LICENSE +2 -0
  103. package/package/corpus/skills/cratis-engineering-docs-authoring/SKILL.md +86 -0
  104. package/package/corpus/skills/cratis-engineering-docs-authoring/references/site-format.md +46 -0
  105. package/package/corpus/skills/cratis-engineering-effect-boundaries/LICENSE +2 -0
  106. package/package/corpus/skills/cratis-engineering-effect-boundaries/SKILL.md +130 -0
  107. package/package/corpus/skills/cratis-engineering-effect-boundaries/references/failure-archetypes.md +133 -0
  108. package/package/corpus/skills/cratis-fundamentals-concept/verification.json +8 -0
  109. package/package/profile-catalog.json +821 -0
  110. package/package.json +35 -13
  111. package/src/index.ts +80 -0
  112. /package/{skills → package/corpus/skills}/cratis-application-react-specifications/LICENSE +0 -0
  113. /package/{skills → package/corpus/skills}/cratis-application-react-specifications/SKILL.md +0 -0
  114. /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/LICENSE +0 -0
  115. /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/SKILL.md +0 -0
  116. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/LICENSE +0 -0
  117. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/SKILL.md +0 -0
  118. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authentication.md +0 -0
  119. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authorization.md +0 -0
  120. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/frontend.md +0 -0
  121. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/local-development.md +0 -0
  122. /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +0 -0
  123. /package/{skills → package/corpus/skills}/cratis-arc-command/LICENSE +0 -0
  124. /package/{skills → package/corpus/skills}/cratis-arc-command/SKILL.md +0 -0
  125. /package/{skills → package/corpus/skills}/cratis-arc-command/references/command-result.md +0 -0
  126. /package/{skills → package/corpus/skills}/cratis-arc-command/references/handler-shapes.md +0 -0
  127. /package/{skills → package/corpus/skills}/cratis-arc-command/references/proxy-generation.md +0 -0
  128. /package/{skills → package/corpus/skills}/cratis-arc-command/references/read-model-injection.md +0 -0
  129. /package/{skills → package/corpus/skills}/cratis-arc-command-execution/LICENSE +0 -0
  130. /package/{skills → package/corpus/skills}/cratis-arc-command-execution/SKILL.md +0 -0
  131. /package/{skills → package/corpus/skills}/cratis-arc-command-validation/LICENSE +0 -0
  132. /package/{skills → package/corpus/skills}/cratis-arc-command-validation/SKILL.md +0 -0
  133. /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/LICENSE +0 -0
  134. /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/SKILL.md +0 -0
  135. /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/LICENSE +0 -0
  136. /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/SKILL.md +0 -0
  137. /package/{skills → package/corpus/skills}/cratis-arc-query-paging/LICENSE +0 -0
  138. /package/{skills → package/corpus/skills}/cratis-arc-query-paging/SKILL.md +0 -0
  139. /package/{skills → package/corpus/skills}/cratis-arc-react-page/LICENSE +0 -0
  140. /package/{skills → package/corpus/skills}/cratis-arc-react-page/SKILL.md +0 -0
  141. /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-page.md +0 -0
  142. /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-tables.md +0 -0
  143. /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/dialogs.md +0 -0
  144. /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/mvvm.md +0 -0
  145. /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/queries-and-commands.md +0 -0
  146. /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/LICENSE +0 -0
  147. /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/SKILL.md +0 -0
  148. /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/LICENSE +0 -0
  149. /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/SKILL.md +0 -0
  150. /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/LICENSE +0 -0
  151. /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/SKILL.md +0 -0
  152. /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/LICENSE +0 -0
  153. /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/SKILL.md +0 -0
  154. /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/LICENSE +0 -0
  155. /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/SKILL.md +0 -0
  156. /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/LICENSE +0 -0
  157. /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/SKILL.md +0 -0
  158. /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/LICENSE +0 -0
  159. /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/SKILL.md +0 -0
  160. /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/LICENSE +0 -0
  161. /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/SKILL.md +0 -0
  162. /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/LICENSE +0 -0
  163. /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/SKILL.md +0 -0
  164. /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/LICENSE +0 -0
  165. /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/SKILL.md +0 -0
  166. /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/LICENSE +0 -0
  167. /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/SKILL.md +0 -0
  168. /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/blocked-tools.md +0 -0
  169. /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/observational-tools.md +0 -0
  170. /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/LICENSE +0 -0
  171. /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/SKILL.md +0 -0
  172. /package/{skills → package/corpus/skills}/cratis-chronicle-projection/LICENSE +0 -0
  173. /package/{skills → package/corpus/skills}/cratis-chronicle-projection/SKILL.md +0 -0
  174. /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/fluent-builder.md +0 -0
  175. /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/model-bound-attributes.md +0 -0
  176. /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/LICENSE +0 -0
  177. /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/SKILL.md +0 -0
  178. /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/LICENSE +0 -0
  179. /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/SKILL.md +0 -0
  180. /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/references/queries.md +0 -0
  181. /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/LICENSE +0 -0
  182. /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/SKILL.md +0 -0
  183. /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/LICENSE +0 -0
  184. /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/SKILL.md +0 -0
  185. /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/LICENSE +0 -0
  186. /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/SKILL.md +0 -0
  187. /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/LICENSE +0 -0
  188. /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/SKILL.md +0 -0
  189. /package/{skills → package/corpus/skills}/cratis-code-review/LICENSE +0 -0
  190. /package/{skills → package/corpus/skills}/cratis-code-review/SKILL.md +0 -0
  191. /package/{skills → package/corpus/skills}/cratis-components-accessibility/LICENSE +0 -0
  192. /package/{skills → package/corpus/skills}/cratis-components-accessibility/SKILL.md +0 -0
  193. /package/{skills → package/corpus/skills}/cratis-components-schema-editor/LICENSE +0 -0
  194. /package/{skills → package/corpus/skills}/cratis-components-schema-editor/SKILL.md +0 -0
  195. /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/LICENSE +0 -0
  196. /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/SKILL.md +0 -0
  197. /package/{skills → package/corpus/skills}/cratis-components-styling/LICENSE +0 -0
  198. /package/{skills → package/corpus/skills}/cratis-components-styling/SKILL.md +0 -0
  199. /package/{skills → package/corpus/skills}/cratis-components-toolbar/LICENSE +0 -0
  200. /package/{skills → package/corpus/skills}/cratis-components-toolbar/SKILL.md +0 -0
  201. /package/{skills → package/corpus/skills}/cratis-documentation-writing/LICENSE +0 -0
  202. /package/{skills → package/corpus/skills}/cratis-documentation-writing/SKILL.md +0 -0
  203. /package/{skills → package/corpus/skills}/cratis-event-model-diagram/LICENSE +0 -0
  204. /package/{skills → package/corpus/skills}/cratis-event-model-diagram/SKILL.md +0 -0
  205. /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/LICENSE +0 -0
  206. /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/SKILL.md +0 -0
  207. /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/LICENSE +0 -0
  208. /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/SKILL.md +0 -0
  209. /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/LICENSE +0 -0
  210. /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/SKILL.md +0 -0
  211. /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/LICENSE +0 -0
  212. /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/SKILL.md +0 -0
  213. /package/{skills → package/corpus/skills}/cratis-performance-review/LICENSE +0 -0
  214. /package/{skills → package/corpus/skills}/cratis-performance-review/SKILL.md +0 -0
  215. /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/LICENSE +0 -0
  216. /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/SKILL.md +0 -0
  217. /package/{skills → package/corpus/skills}/cratis-security-review/LICENSE +0 -0
  218. /package/{skills → package/corpus/skills}/cratis-security-review/SKILL.md +0 -0
  219. /package/{skills → package/corpus/skills}/cratis-specification-by-example/LICENSE +0 -0
  220. /package/{skills → package/corpus/skills}/cratis-specification-by-example/SKILL.md +0 -0
  221. /package/{skills → package/corpus/skills}/cratis-specifications-csharp/LICENSE +0 -0
  222. /package/{skills → package/corpus/skills}/cratis-specifications-csharp/SKILL.md +0 -0
  223. /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/application-scenarios.md +0 -0
  224. /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/csharp-patterns.md +0 -0
  225. /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/integration-specs.md +0 -0
  226. /package/{skills → package/corpus/skills}/cratis-specifications-typescript/LICENSE +0 -0
  227. /package/{skills → package/corpus/skills}/cratis-specifications-typescript/SKILL.md +0 -0
  228. /package/{skills → package/corpus/skills}/cratis-specifications-typescript/references/typescript-patterns.md +0 -0
  229. /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/LICENSE +0 -0
  230. /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/SKILL.md +0 -0
  231. /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/LICENSE +0 -0
  232. /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/SKILL.md +0 -0
  233. /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +0 -0
  234. /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/observational-tools.md +0 -0
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env bash
2
+ # Tier 3 of the package drift guard, and the only tier that reads .NET rather than TypeScript.
3
+ # Tier 1 asks whether a module specifier resolves; Tier 2 asks whether the names imported through it
4
+ # exist. Both are blind to a type the corpus only ever names in prose or in a C# type position — and
5
+ # that is exactly how `ReactorSideEffect` survived: never a module specifier, never an import, told
6
+ # readers to return it from a reactor, and never existed in any Chronicle release.
7
+ #
8
+ # Reporting every unresolved name is the obvious design and it is wrong: 599 of the 1279 distinct
9
+ # names this script reads — 47% — resolve nowhere, because the corpus legitimately invents domain
10
+ # examples (`AuthorRegistered`, `IAuthorService`), placeholders and prose nouns. So only two
11
+ # constructs are ever reported, and between them they take those 599 down to two:
12
+ #
13
+ # 1. ATTRIBUTE POSITION — `[Name]` / `[Name<T>]` / `[Name(...)]` inside an inline code span or a
14
+ # ```csharp block. Attribute brackets are unambiguous C#, and a code span cannot be a markdown
15
+ # link, so the syntax alone identifies an API reference. `Name` and `NameAttribute` both count.
16
+ #
17
+ # 2. FRAMEWORK-ADJACENT TYPE TOKEN — any other PascalCase token in a code span or ```csharp block
18
+ # that resolves nowhere AND is a strict PascalCase-word-boundary *prefix* of a real Cratis type
19
+ # name. That is the fabrication signature: someone half-remembers a real family of names and
20
+ # coins a member of it that was never minted. `ReactorSideEffect` is a prefix of
21
+ # `ReactorSideEffectFailure`; `AuthorRegistered` is a prefix of nothing Cratis ships.
22
+ #
23
+ # Constructs measured and rejected — `new TypeName`, and `IInterfaceName` fenced, spanned or in bare
24
+ # prose — are written up in ../README.md. They are still *read* here; they just have to earn a
25
+ # warning through rule 2 rather than on their syntax alone.
26
+ #
27
+ # WARN, never fail, and — when it genuinely cannot judge — LOUD, never silent. A could-not-run
28
+ # condition exits 2 with a reason on stderr: this script spent its first lifetime exiting 0 at the
29
+ # `Directory.Packages.props` gate in the corpus repository, so it never checked anything anywhere
30
+ # (issue #287), and the fabricated APIs it exists to catch survived precisely because of it.
31
+ # Needs no `jq` and no node_modules; it needs a local NuGet cache, and no cache is a could-not-run.
32
+ #
33
+ # Portable: bash 3.2 + grep + sed + awk + find. No jq, no network, nothing written outside a tempdir.
34
+ #
35
+ # Usage: validate-type-references.sh [--self-test] [root ...] # default roots: .cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts
36
+ # --self-test # seed the known fabrication into a scratch corpus and
37
+ # # fail unless the guard names it (guards-and-fuses.md)
38
+ # CRATIS_HOOKS_TYPE_REPORT=1 ... # also print every distinct name and its status
39
+ set -euo pipefail
40
+
41
+ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
42
+ cd "$root"
43
+
44
+ warn() { printf 'ai-corpus warn: %s\n' "$1" >&2; }
45
+ cannot() { printf 'ai-corpus type-references: could not run: %s\n' "$1" >&2; exit 2; }
46
+ # `if`, not `A && B || C`: the trailing `|| true` was there to keep a disabled report
47
+ # from failing the caller, but it also swallowed a real printf failure, and shellcheck
48
+ # flags the shape (SC2015) for exactly that reason.
49
+ report() {
50
+ if [[ "${CRATIS_HOOKS_TYPE_REPORT:-0}" == "1" ]]; then printf 'ai-corpus type: %s\n' "$1" >&2; fi
51
+ }
52
+
53
+ # Nothing authoritative to compare against is not a finding, but it is also not a pass. In a .NET
54
+ # solution the pins come from the central package management file; in this corpus repository they
55
+ # come from the tracked pin list of the exact product versions the skills verify against. Neither
56
+ # present, or a NuGet cache that holds none of the pinned versions, is a loud exit 2 — the reader
57
+ # must be able to tell "ran and found nothing" from "never looked" (exit-codes-and-wrappers.md).
58
+ self_test=0
59
+ if [[ "${1:-}" == "--self-test" ]]; then self_test=1; shift; fi
60
+ pins_file="$(dirname "${BASH_SOURCE[0]}")/cratis-nuget-pins.txt"
61
+ if [[ -f Directory.Packages.props ]]; then
62
+ queue="$(awk 'match($0, /<PackageVersion[^>]*>/) {
63
+ el = substr($0, RSTART, RLENGTH); id = ""; ver = ""
64
+ if (match(el, /Include="[^"]*"/)) id = substr(el, RSTART + 9, RLENGTH - 10)
65
+ if (match(el, /Version="[^"]*"/)) ver = substr(el, RSTART + 9, RLENGTH - 10)
66
+ if (id ~ /^Cratis/ && ver != "") print id " " ver
67
+ }' Directory.Packages.props)"
68
+ elif [[ -f "$pins_file" ]]; then
69
+ queue="$(sed -E 's/#.*//; s/[[:space:]]+/ /g; s/^ //; s/ $//' "$pins_file" | grep -v '^$' || true)"
70
+ [[ -n "$queue" ]] || cannot "the corpus pin list $pins_file holds no package pins"
71
+ else
72
+ cannot "neither Directory.Packages.props nor the corpus pin list $pins_file exists - nothing to verify against"
73
+ fi
74
+ nuget="${NUGET_PACKAGES:-$HOME/.nuget/packages}"
75
+ [[ -d "$nuget" ]] || cannot "no local NuGet cache at $nuget - restore the pinned Cratis packages before judging type references"
76
+
77
+ # `.cratis/ai/hooks` is deliberately not a default root: this file and ../README.md name a deliberately
78
+ # fabricated type as the worked example, and a guard that reports its own documentation is a guard
79
+ # people switch off.
80
+ if [[ $# -gt 0 ]]; then roots=("$@"); else roots=(.cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts); fi
81
+ scan=()
82
+ for d in "${roots[@]}"; do [[ -d "$d" ]] && scan+=("$d"); done
83
+ [[ "${#scan[@]}" -gt 0 ]] || exit 0
84
+
85
+ tmp="$(mktemp -d 2>/dev/null)" || cannot "cannot create a temporary directory"
86
+ trap 'rm -rf "$tmp"' EXIT
87
+
88
+ # --self-test (guards-and-fuses.md): seed the motivating fabrication into a scratch corpus and
89
+ # require this guard to name it, while the real names beside it stay silent. A guard that can pass
90
+ # vacuously is worse than no guard, and this one did exactly that for its whole first lifetime.
91
+ if [[ "$self_test" == 1 ]]; then
92
+ scratch="$tmp/scratch-corpus"
93
+ mkdir -p "$scratch"
94
+ # shellcheck disable=SC2016
95
+ printf 'A reactor may return a `ReactorSideEffect` to control where the event is appended.\n' \
96
+ > "$scratch/fabrication.md"
97
+ # shellcheck disable=SC2016
98
+ printf 'Return `EventForEventSourceId`, or a `ReactorSideEffectFailure` from an `IReactor`.\n' \
99
+ > "$scratch/discrimination.md"
100
+ # shellcheck disable=SC2016
101
+ printf '```csharp\n[ReactorSideEffect]\npublic SomeHandler ...\n```\n' \
102
+ > "$scratch/attribute.md"
103
+ scan=("$scratch")
104
+ fi
105
+
106
+ # Same clearing rule, and the same generosity, as Tiers 1 and 2: a line carrying a version alongside
107
+ # the name has declared the skew on purpose. The second rule is this tier's own — the corpus's job
108
+ # includes naming things that do NOT exist (`the marker interfaces ICommand, IQuery ... do not
109
+ # exist`), and warning about a line whose entire point is that the type is fictional would be the
110
+ # most annoying false positive of all.
111
+ version_re='[0-9]+\.[0-9x]+|≥|>='
112
+ absence_re='(do|does|did) not exist|no longer|never (use|write|call|return|inject|reach)|removed|deprecated|obsolete|there (is|are) no|non-existent|not a real|fabricat'
113
+
114
+ # ---------------------------------------------------------------------------------------------
115
+ # 1. Which packages to believe. Every `Cratis*` version pinned by the central package management
116
+ # file or by the corpus pin list above, plus the Cratis packages those pull in (`Cratis` is a
117
+ # metapackage: Arc, Arc.Chronicle, Chronicle, …), resolved against the local NuGet cache.
118
+ # Deliberately a *union* across whatever versions the closure names rather than NuGet's
119
+ # single-version resolution — over-accepting costs a missed stale line, under-accepting costs a
120
+ # false warning, and only one of those is unacceptable here.
121
+ # ---------------------------------------------------------------------------------------------
122
+ seen=""; libdirs=""; hops=0
123
+ while [[ -n "$queue" && "$hops" -lt 8 ]]; do
124
+ hops=$((hops + 1)); next=""
125
+ while IFS=' ' read -r id ver; do
126
+ [[ -n "$id" && -n "$ver" ]] || continue
127
+ case " $seen " in *" $id/$ver "*) continue ;; esac
128
+ seen="$seen $id/$ver"
129
+ pkgdir="$nuget/$(printf '%s' "$id" | tr '[:upper:]' '[:lower:]')/$ver"
130
+ [[ -d "$pkgdir/lib" ]] || continue
131
+ libdirs="$libdirs$pkgdir/lib
132
+ "
133
+ next="$next$({ grep -ohE '<dependency id="Cratis[^"]*" version="[^"]*"' "$pkgdir"/*.nuspec 2>/dev/null || true; } \
134
+ | sed -E 's/.*id="([^"]*)" version="([^"]*)".*/\1 \2/' | LC_ALL=C sort -u)
135
+ "
136
+ done <<<"$queue"
137
+ queue="$(printf '%s' "$next" | grep -v '^[[:space:]]*$' || true)"
138
+ done
139
+ printf '%s' "$libdirs" | LC_ALL=C sort -u | while IFS= read -r d; do
140
+ [[ -n "$d" ]] && find "$d" -type f -name '*.xml' 2>/dev/null
141
+ done | LC_ALL=C sort -u > "$tmp/xml.txt"
142
+ [[ -s "$tmp/xml.txt" ]] || cannot "the local NuGet cache holds none of the pinned Cratis packages - no XML documentation to verify against"
143
+
144
+ # ---------------------------------------------------------------------------------------------
145
+ # 2. The index. `<member name="T:Full.Namespace.TypeName">` is a complete, machine-readable list of
146
+ # the documented public types; the word list beside it is every identifier the docs mention at
147
+ # all — `cref`s into the BCL, parameter names, `<see>` targets — and exists purely to accept, in
148
+ # the same deliberately-permissive spirit as Tier 2's "a word anywhere in the .d.ts closure".
149
+ # Names the corpus or this repository's own C# declares are accepted too: a worked example that
150
+ # defines `AuthorRegistered` before using it is not documenting a framework API.
151
+ # ---------------------------------------------------------------------------------------------
152
+ { tr '\n' '\0' < "$tmp/xml.txt" | xargs -0 grep -ho 'name="T:[^"]*"' 2>/dev/null \
153
+ | sed -E 's/name="T:([^"]*)"/\1/; s/`[0-9]+$//; s/.*[.+]//' | LC_ALL=C sort -u > "$tmp/types.txt"; } || true
154
+ [[ -s "$tmp/types.txt" ]] || cannot "the pinned Cratis packages expose no documented public types - nothing to verify against"
155
+ { tr '\n' '\0' < "$tmp/xml.txt" | xargs -0 grep -hoE '[A-Za-z_][A-Za-z0-9_]*' 2>/dev/null \
156
+ | LC_ALL=C sort -u > "$tmp/words.txt"; } || true
157
+
158
+ find "${scan[@]}" -type f -name '*.md' 2>/dev/null | LC_ALL=C sort > "$tmp/files.txt"
159
+ [[ -s "$tmp/files.txt" ]] || cannot "no markdown files under the scan roots - nothing to scan"
160
+
161
+ decl_re='(^|[^A-Za-z0-9_])(record|class|interface|struct|enum|delegate)[[:space:]]+(struct[[:space:]]+)?[A-Z][A-Za-z0-9_]*'
162
+ {
163
+ { tr '\n' '\0' < "$tmp/files.txt" | xargs -0 grep -hoE "$decl_re" 2>/dev/null || true; }
164
+ { find Source -type f -name '*.cs' -print0 2>/dev/null | xargs -0 grep -hoE "$decl_re" 2>/dev/null || true; }
165
+ } | sed -E 's/^[^A-Za-z]*//; s/^[a-z]+[[:space:]]+([a-z]+[[:space:]]+)?//' | LC_ALL=C sort -u > "$tmp/declared.txt"
166
+
167
+ allowlist="$(dirname "${BASH_SOURCE[0]}")/type-references-allowlist.txt"
168
+ : > "$tmp/allow.txt"
169
+ if [[ -f "$allowlist" ]]; then
170
+ sed -E 's/#.*//; s/[[:space:]]//g' "$allowlist" | grep -v '^$' > "$tmp/allow.txt" || true
171
+ fi
172
+
173
+ # ---------------------------------------------------------------------------------------------
174
+ # 3. The two constructs, extracted in one markdown-aware pass, and resolved against the index.
175
+ # ---------------------------------------------------------------------------------------------
176
+ # The program is written out rather than inlined, as in validate-package-imports.sh: it has to
177
+ # contain both quote characters and a bare backtick (markdown's code-span delimiter), and a quoted
178
+ # heredoc straight to a file keeps every one of them out of the shell's reach. Not a command
179
+ # substitution — bash 3.2 mis-lexes a backtick inside a here-document nested in `$( … )`.
180
+ cat > "$tmp/extract.awk" <<'AWK'
181
+ function ucount(s, i, n, k) { k = 0; n = length(s); for (i = 1; i <= n; i++) if (substr(s, i, 1) ~ /[A-Z]/) k++; return k }
182
+ # Every PascalCase-word-boundary prefix of a real type, remembering one whole name as the anchor to
183
+ # quote back at the reader. `ReactorSideEffectFailure` contributes Reactor, ReactorSide and
184
+ # ReactorSideEffect; a token landing on one of those is a coined member of a real family.
185
+ function addprefixes(t, i, n, p) {
186
+ n = length(t)
187
+ for (i = 2; i < n; i++) if (substr(t, i + 1, 1) ~ /[A-Z]/) { p = substr(t, 1, i); if (!(p in P)) P[p] = t }
188
+ }
189
+ function classify(name) {
190
+ if (name in T || (name "Attribute") in T) return "type in the pinned Cratis packages"
191
+ if (name in W || (name "Attribute") in W) return "named by the pinned Cratis XML documentation"
192
+ if (name in D) return "declared by the corpus or by this repository"
193
+ if (name in A) return "allowlisted"
194
+ return ""
195
+ }
196
+ function emit(kind, name, line, status, anchor) {
197
+ status = classify(name)
198
+ if (status == "") {
199
+ anchor = ""
200
+ if (kind == "attr") status = "UNRESOLVED"
201
+ else if (ucount(name) >= 2 && (name in P)) { status = "UNRESOLVED"; anchor = P[name] }
202
+ else status = "unresolved, not framework-adjacent"
203
+ if (status == "UNRESOLVED") printf "FLAG\t%s\t%d\t%s\t%s\t%s\n", FILENAME, line, kind, name, anchor
204
+ }
205
+ if (!(name in reported)) { reported[name] = 1; printf "INFO\t%s\t%s\n", name, status }
206
+ }
207
+ function scanattrs(s, line, pos, rest, ch, name) {
208
+ while (match(s, /\[[A-Z][A-Za-z0-9_]*/)) {
209
+ pos = RSTART; ch = (pos > 1) ? substr(s, pos - 1, 1) : " "
210
+ name = substr(s, pos + 1, RLENGTH - 1); rest = substr(s, pos + RLENGTH)
211
+ s = rest
212
+ # An indexer (map[Key]), a bracket continuation, or a bracket inside a string literal
213
+ # ("[NotSet]") is not an attribute; an attribute must also close as one, after optional
214
+ # generics or arguments.
215
+ if (ch ~ /[A-Za-z0-9_)\]"']/) continue
216
+ if (rest !~ /^(<[^<>]*>)?(\([^()]*\))?\]/) continue
217
+ if (length(name) >= 3) emit("attr", name, line)
218
+ }
219
+ }
220
+ function scantypes(s, line, i, n, start, run, prev, nxt, pp) {
221
+ n = length(s); i = 1
222
+ while (i <= n) {
223
+ if (substr(s, i, 1) !~ /[A-Za-z0-9_]/) { i++; continue }
224
+ start = i
225
+ while (i <= n && substr(s, i, 1) ~ /[A-Za-z0-9_]/) i++
226
+ run = substr(s, start, i - start)
227
+ prev = (start > 1) ? substr(s, start - 1, 1) : ""
228
+ nxt = (i <= n) ? substr(s, i, 1) : ""
229
+ if (run !~ /^[A-Z]/) continue # camelCase tail, or a lowercase identifier
230
+ if (prev == ".") continue # `EventStoreName.NotSet` — a member, not a type
231
+ if (length(run) < 4) continue
232
+ if (run !~ /[a-z]/) continue # PII, IMPORTANT — acronyms and shouty prose
233
+ # `<Slice>` / `<ObservableQuery>` is the corpus placeholder idiom. A generic argument list is
234
+ # not: there, the `<` always follows an identifier character.
235
+ if (prev == "<" && nxt == ">") { pp = (start > 2) ? substr(s, start - 2, 1) : ""; if (pp !~ /[A-Za-z0-9_]/) continue }
236
+ emit("type", run, line)
237
+ }
238
+ }
239
+ BEGIN {
240
+ while ((getline l < types) > 0) { T[l] = 1; addprefixes(l) }
241
+ while ((getline l < words) > 0) W[l] = 1
242
+ while ((getline l < declared) > 0) D[l] = 1
243
+ while ((getline l < allow) > 0) A[l] = 1
244
+ fence = 0
245
+ }
246
+ FNR == 1 { fence = 0; lang = "" }
247
+ {
248
+ if ($0 ~ /^[[:space:]]*(```|~~~)/) {
249
+ if (fence == 0) { fence = 1; lang = $0; sub(/^[[:space:]]*(```|~~~)[[:space:]]*/, "", lang); sub(/[[:space:]].*$/, "", lang) }
250
+ else { fence = 0; lang = "" }
251
+ next
252
+ }
253
+ if (fence == 1) {
254
+ if (lang == "csharp" || lang == "cs") { scanattrs($0, FNR); scantypes($0, FNR) }
255
+ next
256
+ }
257
+ # Outside a fence only inline code spans are read: a markdown link cannot live inside one, which
258
+ # is what makes `[Name]` unambiguous, and prose PascalCase is ordinary English.
259
+ n = split($0, seg, "`")
260
+ for (i = 2; i <= n; i += 2) { scanattrs(seg[i], FNR); scantypes(seg[i], FNR) }
261
+ }
262
+ AWK
263
+
264
+ # LC_ALL=C throughout: the corpus is full of em dashes, arrows and emoji, and an awk built against a
265
+ # UTF-8 locale aborts the whole pass on the first byte it cannot convert. Every pattern above is
266
+ # ASCII, so reading bytes loses nothing — a multibyte character is simply not an identifier character.
267
+ { tr '\n' '\0' < "$tmp/files.txt" | LC_ALL=C xargs -0 awk -f "$tmp/extract.awk" \
268
+ -v types="$tmp/types.txt" -v words="$tmp/words.txt" -v declared="$tmp/declared.txt" \
269
+ -v allow="$tmp/allow.txt" > "$tmp/out.txt"; } || true
270
+
271
+ if [[ "$self_test" == 1 ]]; then
272
+ flags="$(grep '^FLAG' "$tmp/out.txt" 2>/dev/null || true)"
273
+ printf '%s\n' "$flags" | grep -q $'\tReactorSideEffect\t' \
274
+ || { printf 'ai-corpus type-references self-test: FAILED - the seeded ReactorSideEffect fabrication was not flagged\n' >&2; exit 1; }
275
+ if printf '%s\n' "$flags" | grep -qE $'\t(EventForEventSourceId|IReactor|ReactorSideEffectFailure)\t'; then
276
+ printf 'ai-corpus type-references self-test: FAILED - a real Cratis type was flagged as unresolved\n' >&2; exit 1
277
+ fi
278
+ printf 'ai-corpus type-references self-test: passed - the seeded fabrication is named and the real types stay silent\n' >&2
279
+ exit 0
280
+ fi
281
+
282
+ while IFS="$(printf '\t')" read -r _ name status; do
283
+ # `|| true` is load-bearing, not decoration: an empty field would leave a failing `&&` list as
284
+ # the last command in the loop body, and `set -e` would end the whole guard right there.
285
+ { [[ -n "$name" ]] && report "$name — $status"; } || true
286
+ done < <(grep '^INFO' "$tmp/out.txt" 2>/dev/null || true)
287
+
288
+ # One warning per (file, name) — the first occurrence — so a page that repeats an example is not a
289
+ # flood. Qualification is judged per (file, name) exactly as in Tiers 1 and 2.
290
+ { grep '^FLAG' "$tmp/out.txt" 2>/dev/null || true; } \
291
+ | LC_ALL=C sort -t"$(printf '\t')" -k2,2 -k5,5 -k3,3n -u | awk -F'\t' '!seen[$2 "\t" $5]++' \
292
+ | while IFS="$(printf '\t')" read -r _ file line kind name anchor; do
293
+ # A here-string, never `printf … | grep -q`: under `pipefail` an early-exiting `grep -q`
294
+ # closes the pipe, `printf` dies of SIGPIPE with 141, and the pipeline reports failure even
295
+ # though the line matched — manufacturing exactly the false positive this guard must not make.
296
+ hits="$(grep -nwF -- "$name" "$file" 2>/dev/null || true)"
297
+ if [[ -n "$hits" ]] && grep -qE "$version_re" <<<"$hits"; then
298
+ report "$name — unresolved but version-qualified in $file"; continue
299
+ fi
300
+ if [[ -n "$hits" ]] && grep -qiE "$absence_re" <<<"$hits"; then
301
+ report "$name — unresolved but named as absent on purpose in $file"; continue
302
+ fi
303
+ if [[ "$kind" == "attr" ]]; then
304
+ warn "$file:$line: '[$name]' is not an attribute in the pinned Cratis packages — fix the name, add it to .cratis/ai/hooks/scripts/type-references-allowlist.txt if it belongs to another framework, or mark the line with the version it needs (e.g. '(≥ 17.0.0)')"
305
+ else
306
+ warn "$file:$line: '$name' is not a type in the pinned Cratis packages, but '$anchor' is — fix the name, or mark the line with the version it needs (e.g. '(≥ 17.0.0)')"
307
+ fi
308
+ done
@@ -0,0 +1,40 @@
1
+ {
2
+ "$comment": "Canonical Claude Code hook wiring for this repository. Copy to .claude/settings.json (see .cratis/ai/hooks/README.md); that copy is per-machine and gitignored. .cratis/ai/ is the source of truth, so edit this file and re-copy — never hand-edit the copy.",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.cratis/ai/hooks/scripts/cratis-guard-writes.sh"
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "PostToolUse": [
16
+ {
17
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.cratis/ai/hooks/scripts/cratis-pattern-scan.sh"
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "Stop": [
27
+ {
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.cratis/ai/hooks/scripts/cratis-quality-gate.sh"
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ },
37
+ "permissions": {
38
+ "ask": ["Bash(git push *)", "Bash(gh pr *)"]
39
+ }
40
+ }
@@ -0,0 +1,22 @@
1
+ ---
2
+ agent: agent
3
+ description: Add a business rule or event-store constraint to an existing command.
4
+ ---
5
+
6
+ # Add a Business Rule or Constraint
7
+
8
+ Enforce a new rule on an existing command. Invoke the **add-business-rule** skill and follow `.cratis/ai/rules/vertical-slices.md` (the decision matrix).
9
+
10
+ ## Confirm first
11
+
12
+ - **Command** to constrain, and the **rule** in one sentence.
13
+
14
+ ## Pick the mechanism
15
+
16
+ - Reusable value invariant → `ConceptValidator<T>`.
17
+ - Command-input / cross-field / pre-handler rule → `CommandValidator<T>`.
18
+ - Handler needs fetched data first → `Provide()`.
19
+ - State rule that must hold **under concurrency** → inject the read model into `Handle()`, return `Result<TEvent, ValidationResult>`.
20
+ - Uniqueness → `[Unique]` / `IConstraint`.
21
+
22
+ > **Never throw for normal business rejection** — a throw is HTTP 500, not a validation error. Recoverable rejection is a `ValidationResult` / `Result<,>`. Add a spec for the failure case (assert both not-successful and has-validation-errors). The skill carries the detail; don't duplicate it here.
@@ -0,0 +1,17 @@
1
+ ---
2
+ agent: agent
3
+ description: Create a strongly-typed Concept for a primitive domain value or an event-source identity.
4
+ ---
5
+
6
+ # Add a Concept
7
+
8
+ Create a strongly-typed concept to replace a raw primitive. Invoke the **add-concept** skill and follow `.cratis/ai/rules/concepts.md`.
9
+
10
+ ## Confirm first
11
+
12
+ - **Concept name** (e.g. `ProjectId`, `AuthorName`)
13
+ - **Underlying primitive** (`Guid`, `string`, `int`, …)
14
+ - **Value or identity?** — a **value** concept derives from `ConceptAs<T>`; an **event-source identity** derives from `EventSourceId<T>` (never `ConceptAs<Guid>` — the base already supplies the `EventSourceId`/`T`/`string` conversions).
15
+ - **Placement** — the folder that owns it (slice → feature → `Common/`); never a dedicated `Concepts/` folder.
16
+
17
+ The skill carries the templates and checklist; don't duplicate them here.
@@ -0,0 +1,24 @@
1
+ ---
2
+ agent: agent
3
+ description: Add or update an Entity Framework Core DbContext, table column, or hand-written migration.
4
+ ---
5
+
6
+ # Add an EF Core Migration
7
+
8
+ Make a database schema change via EF Core. Invoke the **add-ef-migration** skill and follow `.cratis/ai/rules/efcore.md` (+ `.cratis/ai/rules/efcore.specs.md`).
9
+
10
+ > Applies only to projects that use EF Core.
11
+
12
+ ## Confirm first
13
+
14
+ - **Change type** (new table / column / relationship / rename), the **entity**, and its **feature DbContext**.
15
+
16
+ ## Non-negotiables
17
+
18
+ - Migrations are **hand-written** in the `Database` project — never `dotnet ef migrations add` / `database update`.
19
+ - Version-named files `v{major}_{minor}_{patch}.cs` in a folder matching the entity category; namespace matches the folder.
20
+ - Use the cross-database column helpers (`StringColumn`, `GuidColumn`, `NumberColumn<T>`, `DateTimeOffsetColumn`) and `WellKnownTables` constants — never raw `table.Column<T>()` or magic strings.
21
+ - Never hardcode a provider (`UseSqlite`/`UseNpgsql`) — use `UseDatabaseFromConnectionString`.
22
+ - Never mutate state directly through a DbContext — writes flow through Chronicle events.
23
+
24
+ The skill carries the step-by-step detail; don't duplicate it here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ agent: agent
3
+ description: Add a Chronicle projection to an existing read model slice.
4
+ ---
5
+
6
+ # Add a Projection
7
+
8
+ Add a Chronicle projection that populates a read model from events. Invoke the **add-projection** skill and follow `.cratis/ai/rules/vertical-slices.md` (projections). For reactors, use the **add-reactor** prompt instead.
9
+
10
+ ## Confirm first
11
+
12
+ - **Events to project from** and the **read model** shape.
13
+
14
+ ## Key rules
15
+
16
+ - Default to **model-bound attributes** on the read model (`[FromEvent<T>]` class-level, `[SetFrom<T>]`, `[Key]`, `[ChildrenFrom<T>]`, `[RemovedWith<T>]`); drop to fluent `IProjectionFor<T>` only for joins/transforms; reducer for "current state + event → next state".
17
+ - **AutoMap is on by default — never call `.AutoMap()`** (matching names map automatically).
18
+ - Projections consume Chronicle **events**, never other read models.
19
+
20
+ Run a clean build afterward. The skill carries the detail; don't duplicate it here.
@@ -0,0 +1,22 @@
1
+ ---
2
+ agent: agent
3
+ description: Add a Chronicle reactor (automation or translation) that reacts to events and triggers side effects.
4
+ ---
5
+
6
+ # Add a Reactor
7
+
8
+ Add a reactor that observes events and produces side effects. Invoke the **add-reactor** skill and follow `.cratis/ai/rules/reactors.md`.
9
+
10
+ ## Confirm first
11
+
12
+ - **Events to react to**, the **side effect / automation**, and whether it's `Automation` (side effects) or `Translation` (triggers commands in another slice).
13
+
14
+ ## Key rules
15
+
16
+ - `IReactor` is a marker interface; dispatch is by the first parameter type; the method name is descriptive only.
17
+ - Reactors are **idempotent** and **stateless**; use event data directly (don't query the read model back).
18
+ - To change state elsewhere, return side-effect events or inject `ICommandPipeline` — **never** `IEventLog`.
19
+ - `[OnceOnly]` on any non-idempotent side effect (emails, payments, external writes).
20
+ - Test with `ReactorScenario<TReactor>`.
21
+
22
+ The skill carries the detail; don't duplicate it here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ agent: agent
3
+ description: Add a Chronicle reducer to a read model when model-bound and fluent projections cannot express the state transition.
4
+ ---
5
+
6
+ # Add a Reducer
7
+
8
+ Add an `IReducerFor<T>` reducer — the last-resort escape hatch for a "current state + event → next state" transition that model-bound projection attributes and fluent `IProjectionFor<T>` cannot express. Invoke the **add-reducer** skill and follow `.cratis/ai/rules/vertical-slices.md`. For ordinary projections, use the **add-projection** prompt instead.
9
+
10
+ ## Confirm first
11
+
12
+ - **Why a reducer** (which model-bound / fluent approach was ruled out) and the **events** + **read model** shape.
13
+
14
+ ## Key rules
15
+
16
+ - Reducers are the **last resort** — exhaust model-bound attributes and fluent `IProjectionFor<T>` first.
17
+ - Handle the **nullable current** state (the first event has no prior state).
18
+ - Keep reducers passive and deterministic — no side effects, no reading other read models.
19
+
20
+ Run a clean build afterward. The skill carries the detail; don't duplicate it here.
@@ -0,0 +1,15 @@
1
+ ---
2
+ agent: agent
3
+ description: Audit hook files for correctness, portability, and enforcement coverage.
4
+ ---
5
+
6
+ # Audit Hooks
7
+
8
+ Review `.cratis/ai/hooks/` and report whether hooks are:
9
+
10
+ - enforcing the intended policy
11
+ - portable across environments
12
+ - aligned with canonical source rules
13
+ - using bash-first commands for script execution
14
+
15
+ Focus on gaps, risks, and missing checks. If improvements are obvious and low-risk, propose exact edits.
@@ -0,0 +1,21 @@
1
+ ---
2
+ agent: agent
3
+ description: Check for drift between AI assets and documentation inventory.
4
+ ---
5
+
6
+ # Check Documentation Drift
7
+
8
+ Check whether AI assets and docs are in sync:
9
+
10
+ - `.cratis/ai/rules/` vs documented instruction inventory
11
+ - `.cratis/ai/skills/` vs documented skill inventory
12
+ - `.cratis/ai/agents/` vs documented agent roster
13
+ - `.cratis/ai/hooks/` vs architecture docs
14
+
15
+ Report:
16
+
17
+ 1. Missing documentation entries.
18
+ 2. Stale documentation entries.
19
+ 3. Suggested updates by file.
20
+
21
+ Apply updates only if asked.
@@ -0,0 +1,9 @@
1
+ ---
2
+ agent: agent
3
+ description: Review a proposed change for correctness, maintainability, and security.
4
+ ---
5
+
6
+ # Code Review Prompt
7
+
8
+ Review the proposed change for correctness, maintainability, and security.
9
+ Focus on actionable findings and minimize false positives.
@@ -0,0 +1,9 @@
1
+ ---
2
+ agent: agent
3
+ description: Implement a requested feature as a vertical slice with minimal, focused changes.
4
+ ---
5
+
6
+ # New Feature Prompt
7
+
8
+ Implement the requested feature as a vertical slice with minimal, focused changes.
9
+ Add or update tests for behavior changes and validate build/test before completion.
@@ -0,0 +1,18 @@
1
+ ---
2
+ agent: agent
3
+ description: Scaffold a complete vertical slice (backend + specs + frontend) for a Cratis-based project.
4
+ ---
5
+
6
+ # New Vertical Slice
7
+
8
+ Implement a complete **vertical slice** end-to-end. Invoke the **new-vertical-slice** skill and follow it exactly; for a full backend + specs + frontend slice you may hand the work to the **Slice Implementer** agent.
9
+
10
+ ## Confirm first
11
+
12
+ - **Module / Feature** and **slice name**
13
+ - **Slice type** — `State Change` / `State View` / `Automation` / `Translation`
14
+ - **Behavior** in one sentence, plus the command/query properties and their concept types
15
+
16
+ ## How it runs
17
+
18
+ Backend → build (Debug + Release) → specs (the in-process `*Scenario` family) → frontend → compose/route, with each quality gate green before the next phase. The authoritative rules are `.cratis/ai/rules/general.md` and `.cratis/ai/rules/vertical-slices.md`; the skill carries the step-by-step detail. Do not duplicate that detail here.
@@ -0,0 +1,35 @@
1
+ ---
2
+ agent: agent
3
+ description: Review a pull request against all Cratis project standards and produce a structured review report.
4
+ ---
5
+
6
+ # Review Pull Request
7
+
8
+ Produce a structured review of a pull request against all Cratis standards.
9
+
10
+ ## Confirm first
11
+
12
+ - **PR number or branch**, and the affected repos/projects.
13
+
14
+ ## Process
15
+
16
+ 1. **Gather context** — list and read every changed file; identify the slice type(s).
17
+ 2. **Architecture & quality** — run the **Code Reviewer** agent (it checks `.cratis/ai/rules/` and folds in the performance pass).
18
+ 3. **Security** — run the **Security Reviewer** agent.
19
+ 4. **Spec coverage** — confirm each slice has specs (happy path + each failure); confirm tests pass if runnable.
20
+ 5. **Docs** — confirm public-facing changes updated documentation.
21
+
22
+ ## Output
23
+
24
+ ```
25
+ ## Pull Request Review — #<number>
26
+ ### Summary — <2–3 sentences>
27
+ ### Architecture & Quality — ✅ / ⚠️ / ❌ <findings>
28
+ ### Security — ✅ / ⚠️ / ❌ <findings>
29
+ ### Spec Coverage — ✅ / ⚠️ / ❌
30
+ ### Documentation — ✅ / ⚠️ / ❌
31
+ ### Overall — ✅ Approved / ⚠️ Approved with comments / ❌ Changes requested
32
+ **Blocking** (violates a MUST): 1. … **Suggestions**: 1. …
33
+ ```
34
+
35
+ Be specific — file, line, and corrected code for every blocking issue.
@@ -0,0 +1,16 @@
1
+ ---
2
+ agent: agent
3
+ description: Review one skill for clarity, trigger quality, and maintainability.
4
+ ---
5
+
6
+ # Review Skill
7
+
8
+ Review a specific skill folder under `.cratis/ai/skills/` for:
9
+
10
+ - trigger quality in the description
11
+ - correctness of workflow steps
12
+ - overlap with existing skills
13
+ - missing references or checklists
14
+ - opportunities to split large files
15
+
16
+ Return findings first, then proposed edits.
@@ -0,0 +1,16 @@
1
+ ---
2
+ agent: agent
3
+ description: Scaffold a new feature — folder structure, composition page, routing, and navigation.
4
+ ---
5
+
6
+ # Scaffold a Feature
7
+
8
+ Scaffold a brand-new feature folder (composition page, routing, navigation) — ready for slices. Invoke the **scaffold-feature** skill and follow it exactly.
9
+
10
+ ## Confirm first
11
+
12
+ - **Feature name** — PascalCase (e.g. `Projects`, `Invoices`)
13
+ - **Route path** — kebab-case (e.g. `/projects`)
14
+ - **Navigation label** and **icon** (from `react-icons/md`)
15
+
16
+ The feature folder lives directly under the app source root (or under an optional `<Module>/`) — there is no top-level `Features/` wrapper. After scaffolding, add behavior with the **new-vertical-slice** prompt/skill. The skill carries the step-by-step detail; don't duplicate it here.