@cratis/pi 7.125.2 → 7.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/package/corpus/agents/spec-writer.md +2 -2
  2. package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +1 -1
  3. package/package/corpus/harnesses/copilot/instructions/fast-feedback.instructions.md +37 -0
  4. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +3 -2
  5. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +1 -1
  6. package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +1 -1
  7. package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +7 -1
  8. package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +40 -3
  9. package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +1 -1
  10. package/package/corpus/harnesses/pi/agents/spec-writer.md +2 -2
  11. package/package/corpus/hooks/README.md +1 -1
  12. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +13 -2
  13. package/package/corpus/rules/application-profile.md +1 -1
  14. package/package/corpus/rules/fast-feedback.md +36 -0
  15. package/package/corpus/rules/general.md +1 -0
  16. package/package/corpus/rules/profiles.md +3 -2
  17. package/package/corpus/rules/secrets.md +1 -1
  18. package/package/corpus/rules/specs.csharp.md +1 -1
  19. package/package/corpus/rules/specs.md +7 -1
  20. package/package/corpus/rules/specs.scenarios.csharp.md +40 -3
  21. package/package/corpus/rules/vertical-slices.md +1 -1
  22. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +15 -10
  23. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +1 -1
  24. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +26 -0
  25. package/package/corpus/skills/cratis-arc-command/SKILL.md +7 -4
  26. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +5 -0
  27. package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +23 -2
  28. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +6 -3
  29. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +2 -2
  30. package/package/corpus/skills/cratis-code-review/SKILL.md +4 -2
  31. package/package/corpus/skills/cratis-environments-and-secrets/LICENSE +9 -0
  32. package/package/corpus/skills/cratis-environments-and-secrets/SKILL.md +112 -0
  33. package/package/corpus/skills/cratis-environments-and-secrets/references/environment-file.md +48 -0
  34. package/package/corpus/skills/cratis-onboarding-runtime/LICENSE +9 -0
  35. package/package/corpus/skills/cratis-onboarding-runtime/SKILL.md +94 -0
  36. package/package/corpus/skills/cratis-onboarding-runtime/references/ante-onboarding-keys.md +31 -0
  37. package/package/corpus/skills/cratis-onboarding-runtime/references/performance.md +86 -0
  38. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/LICENSE +9 -0
  39. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md +111 -0
  40. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md +89 -0
  41. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/verification.json +32 -0
  42. package/package/corpus/skills/cratis-screenplay-command-surface/references/policy-persona-seed.md +1 -0
  43. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
  44. package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -0
  45. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +8 -0
  46. package/package/corpus/skills/cratis-screenplay-toolchain/references/neighbouring-skills.md +2 -0
  47. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract-exceptions.json +70 -0
  48. package/package/corpus/skills/cratis-showrunner-herald/LICENSE +9 -0
  49. package/package/corpus/skills/cratis-showrunner-herald/SKILL.md +106 -0
  50. package/package/corpus/skills/cratis-showrunner-herald/references/options.md +35 -0
  51. package/package/corpus/skills/cratis-showrunner-herald/references/templates-and-images.md +70 -0
  52. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +2 -2
  53. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +5 -0
  54. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +39 -0
  55. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +7 -0
  56. package/package/profile-catalog.json +6 -2
  57. package/package.json +1 -1
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: cratis-screenplay-authentication-and-onboarding
3
+ description: Use when a `.play` model declares how users sign in or get into an application, meaning `authentication` providers, the `onboarding` block (signup, invitations, legal terms, emails) or the environment keys a provider or mail server needs. Not for personas and policies, or for running the application.
4
+ license: MIT
5
+ ---
6
+
7
+ # Screenplay authentication and onboarding
8
+
9
+ Two top-level blocks say how people reach an application. `authentication` names the identity
10
+ providers. `onboarding` says how people get in: self-signup, who invites whom, which terms they
11
+ accept and which emails they receive. Both are authoring metadata, not executable behavior: the
12
+ reference evaluator does not run them, and whatever runs the application provisions them.
13
+
14
+ **Requires** Screenplay 4.259.0 or later for `onboarding`, the environment key listing and its
15
+ diagnostics (`screenplay --version`; the compiler bundled in `cratis` may be older). Older
16
+ releases reject the block as an unknown top-level construct.
17
+
18
+ ## The line the model never crosses
19
+
20
+ The model is the same document in every environment. A client id, a client secret, an authority,
21
+ a sender address, a mail server and its password differ per environment, so none of them appears
22
+ in the model. A configuration line under a provider is a compile error (`PLAY0183`), not an
23
+ ignored line. The model refers to a value by a **key derived from its declarations**, and the
24
+ environment supplies the value. Never add the value to the model to "make it render". Creating
25
+ and filling the environment is [cratis-environments-and-secrets](../cratis-environments-and-secrets/SKILL.md).
26
+
27
+ ## `authentication`
28
+
29
+ ```text
30
+ authentication
31
+ provider EntraId
32
+ provider GitHub
33
+ provider OpenId name Partner
34
+ provider OpenId name Supplier
35
+ ```
36
+
37
+ - At most one block for the application (`PLAY0125`); the line carries only the keyword (`PLAY0124`).
38
+ - Providers are `EntraId`, `GitHub`, `Google`, `Apple` and `OpenId`.
39
+ - A provider's **identity** is its `name` alias when it has one, otherwise the provider name.
40
+ Two entries with one identity are `PLAY0169`, so two `OpenId` providers each need a `name`.
41
+ - Typed information about the caller is the separate `identity` block, not part of `authentication`.
42
+
43
+ ## `onboarding`
44
+
45
+ ```text
46
+ onboarding
47
+ signup open creates tenant
48
+ invite tenant as Owner
49
+ invite member
50
+ by Owner, Administrator
51
+ grant Accountant, InvoiceManager
52
+ legal terms
53
+ file Legal/terms-of-service.md
54
+ file Legal/privacy-policy.md
55
+ offering Starter
56
+ emails
57
+ invitation file Emails/invitation.html
58
+ welcome file Emails/welcome.html
59
+ signupDeclined file Emails/signup-declined.html
60
+ images Emails/Images
61
+ ```
62
+
63
+ Read [onboarding directives](references/onboarding-directives.md) before writing or reviewing a
64
+ block. The diagnostics for each rule are in that reference. The rules that decide most mistakes:
65
+
66
+ - It needs `authentication` in the same application, and there is at most one block.
67
+ - Every line is optional and appears once. Anything that is not a directive is a compile error.
68
+ - **No `signup` line means self-signup is off**; every newcomer arrives by invitation.
69
+ `signup open creates tenant` makes a signup create a tenant, `signup open joins tenant` asks to join an existing one.
70
+ - Every persona in `as`, `by` and `grant` must be declared with `persona`; there are no free-text roles.
71
+ - `emails` names only `invitation`, `welcome` and `signupDeclined`. The sender and mail server are not in the model.
72
+ - `file` paths are repository-relative, like handler files. `offering` is an opaque name resolved where the application is provisioned.
73
+
74
+ ## Environment keys
75
+
76
+ Every key is derived from a declaration, so nothing is configured twice. Keys are dotted,
77
+ case-sensitive paths.
78
+
79
+ | Declaration | Keys | Secret |
80
+ |---|---|---|
81
+ | each provider | `authentication.providers.<Identity>.clientId` | no |
82
+ | each provider | `authentication.providers.<Identity>.clientSecret` | **yes** |
83
+ | `EntraId` | `authentication.providers.<Identity>.tenantId` | no |
84
+ | `OpenId` | `authentication.providers.<Identity>.authority` | no |
85
+ | `onboarding` | `onboarding.email.sender`, `onboarding.email.server.host`, `.port`, `.username` | no |
86
+ | `onboarding` | `onboarding.email.server.password` | **yes** |
87
+
88
+ List the keys for the model instead of working them out by hand:
89
+
90
+ ```bash
91
+ screenplay environment ./model # one key per line, secrets marked "(secret)"
92
+ screenplay environment ./model --format json # key, secret, source, owner, description
93
+ ```
94
+
95
+ Pass the folder, not one file. It exits `0` when it listed keys, `1` when the model does not
96
+ compile (it lists nothing) and `2` when it could not run. Compare an environment with the model's
97
+ keys using `cratis config --missing <path>`, which exits `5` when any key is absent.
98
+
99
+ ## Verify
100
+
101
+ 1. `screenplay <folder> --warnaserror` exits `0` with none of the diagnostics in the directives reference.
102
+ 2. The key listing for the folder contains exactly the keys the table above predicts for the providers and the `onboarding` block.
103
+ 3. `grep -rn -i -E "clientSecret|password|authority" <folder>` finds no value in any `.play` file.
104
+
105
+ ## Route near misses
106
+
107
+ - Personas, policies and `require`: `cratis-screenplay-command-surface`.
108
+ - Choosing a compiler and reading verdicts: `cratis-screenplay-toolchain`.
109
+ - Creating an environment, setting a secret: `cratis-environments-and-secrets`.
110
+ - How AuthProxy, Ante and Herald consume the contract at runtime: `cratis-onboarding-runtime`.
111
+ - How an Arc application trusts the proxy: `cratis-arc-authentication-authorization-and-identity`.
@@ -0,0 +1,89 @@
1
+ # Onboarding directives
2
+
3
+ A complete model: personas are declared before the blocks name them.
4
+
5
+ ```text
6
+ policy IsOwner
7
+ require role "Owner"
8
+
9
+ policy IsAdministrator
10
+ require role "Administrator"
11
+
12
+ policy IsAccountant
13
+ require role "Accountant"
14
+
15
+ policy IsInvoiceManager
16
+ require role "InvoiceManager"
17
+
18
+ persona Owner
19
+ description "Owns the tenant"
20
+ goal "Run the tenant"
21
+ policy IsOwner
22
+
23
+ persona Administrator
24
+ description "Administers the tenant"
25
+ goal "Keep people onboarded"
26
+ policy IsAdministrator
27
+
28
+ persona Accountant
29
+ description "Keeps the books"
30
+ goal "Keep the books correct"
31
+ policy IsAccountant
32
+
33
+ persona InvoiceManager
34
+ description "Manages invoices"
35
+ goal "Keep invoices moving"
36
+ policy IsInvoiceManager
37
+
38
+ authentication
39
+ provider EntraId
40
+ provider GitHub
41
+ provider OpenId name Partner
42
+ provider OpenId name Supplier
43
+
44
+ onboarding
45
+ signup open creates tenant
46
+ invite tenant as Owner
47
+ invite member
48
+ by Owner, Administrator
49
+ grant Accountant, InvoiceManager
50
+ legal terms
51
+ file Legal/terms-of-service.md
52
+ file Legal/privacy-policy.md
53
+ offering Starter
54
+ emails
55
+ invitation file Emails/invitation.html
56
+ welcome file Emails/welcome.html
57
+ signupDeclined file Emails/signup-declined.html
58
+ images Emails/Images
59
+ ```
60
+
61
+ Syntax and meaning of each line of the `onboarding` block. All lines are optional and each may appear once.
62
+
63
+ | Directive | Form | Meaning |
64
+ |---|---|---|
65
+ | `signup` | `signup open creates tenant` or `signup open joins tenant` | Anyone may sign up; signing up creates a tenant or asks to join one. Absent: signup is off. |
66
+ | `invite tenant` | `invite tenant as <Persona>` | An invitation that creates a tenant; the invitee becomes the persona in it. One line, no body. |
67
+ | `invite member` | body with `by <Persona>, ...` and `grant <Persona>, ...` | An invitation into an existing tenant. `by` lists who may send it, `grant` what the invitee is given. Both are required. |
68
+ | `legal terms` | body of `file <path>` lines | Documents a newcomer must accept. Present means acceptance is required. |
69
+ | `offering` | `offering <Id>` | The offering a new tenant starts on. Opaque; absent means no licensing. |
70
+ | `emails` | body of `<email> file <path>` lines and `images <folder>` | Templates for `invitation`, `welcome` and `signupDeclined`, and the folder they take images from. |
71
+
72
+ ## What the block deliberately lacks
73
+
74
+ No sender address, mail server, credentials or price. Those are environment values (see the key
75
+ table in `SKILL.md`). Languages for the emails follow the cultures of the model's `.strings` files.
76
+
77
+ ## Diagnostics
78
+
79
+ | Code | Meaning | Fix |
80
+ |---|---|---|
81
+ | `PLAY0826` | The `onboarding` line carries more than the keyword. | Move content into the body. |
82
+ | `PLAY0827` | More than one `onboarding` block. | Merge them. |
83
+ | `PLAY0828` | `onboarding` without `authentication`. | Declare the providers. Draft authoring reports a warning until the folder is assembled. |
84
+ | `PLAY0829` | A line is not a directive, or one is malformed or incomplete. | Use the forms above. |
85
+ | `PLAY0830` | A directive stated twice. | Keep one. |
86
+ | `PLAY0831` | An invitation names a persona nothing declares. | Declare it with `persona`. |
87
+ | `PLAY0832` | `emails` names an email onboarding does not send. | Use `invitation`, `welcome` or `signupDeclined`. |
88
+
89
+ A missing `file` target is reported like a missing handler file: an attachment warning when files are loaded.
@@ -0,0 +1,32 @@
1
+ {
2
+ "skill": "cratis-screenplay-authentication-and-onboarding",
3
+ "input": "Declare how people sign in and get into a Screenplay application, and say which environment keys it needs.",
4
+ "fixtures": [],
5
+ "assertions": [
6
+ {
7
+ "kind": "skill-contains",
8
+ "value": "PLAY0183"
9
+ },
10
+ {
11
+ "kind": "skill-contains",
12
+ "value": "authentication.providers.<Identity>.clientSecret"
13
+ },
14
+ {
15
+ "kind": "skill-contains",
16
+ "value": "onboarding.email.server.password"
17
+ },
18
+ {
19
+ "kind": "skill-contains",
20
+ "value": "screenplay environment ./model --format json"
21
+ },
22
+ {
23
+ "kind": "skill-contains",
24
+ "value": "No `signup` line means self-signup is off"
25
+ },
26
+ {
27
+ "kind": "skill-contains",
28
+ "value": "| `PLAY0832` |",
29
+ "file": "references/onboarding-directives.md"
30
+ }
31
+ ]
32
+ }
@@ -35,6 +35,7 @@ a second (`PLAY0441`) — **or** one implementation (a tagged ` ```csharp ` bloc
35
35
  runner; a code policy binds as opaque ESM v3 and needs a target to evaluate it.
36
36
  A `persona` is authoring metadata: the binder reports it as information `PLAY0270`
37
37
  (report-only) and it does not block binding; an unknown policy it lists is an error.
38
+ Personas an `onboarding` block invites or grants must be declared here; see `cratis-screenplay-authentication-and-onboarding`.
38
39
 
39
40
  ## Anonymous policy
40
41
 
@@ -261,7 +261,7 @@ See read-surface `references/named-keys.md` for the collection-key exclusion alg
261
261
  | Binds as ESM v6 (`PLAY0268` on a compiler that stops before v6) | `Automation` and `Translate` slices, reactions that produce or invoke, captures, top-level `trigger`, clock, trigger and capture specifications. The standalone `screenplay test`/MCP reference route evaluates admitted v6 actions; Stage renders supported event-triggered Automation, with remaining refusals in the Stage skill's `references/admission.md` |
262
262
  | Reaction with trigger `reads` | Direct-producing (or code-bodied) reaction with `reads`: `PLAY0268` ("ESM v6 cannot protect that decision dependency"), non-executable. Invokes-only reaction with `reads`: information only; the invoked command must declare and protect its own decision reads. Keep the `reads`; never remove them to obtain binding |
263
263
  | Binds as opaque code (v3); reference runner reports unsupported | Bodied reducers, bodied named rules, fenced `validate` blocks, code or file policies. A pure reducer body is the only attachment Stage admits |
264
- | Information only, never blocks | `persona` (report-only: `PLAY0270`, the policies it lists must exist), `authentication`, `domain`, `seed`, descriptions and `file` provenance. A persona does not block binding and does not change behavior, so state persona scope in a description or a policy the model uses |
264
+ | Information only, never blocks | `persona` (report-only: `PLAY0270`, the policies it lists must exist), `authentication`, `onboarding` (see `cratis-screenplay-authentication-and-onboarding`), `domain`, `seed`, descriptions and `file` provenance. A persona does not block binding and does not change behavior, so state persona scope in a description or a policy the model uses |
265
265
  | Binds as ESM v7 (`PLAY0268` on a compiler that stops before v7) | `generated` properties and `returns` responses, generated fixtures and `then returns`: bound and executed by the reference runner; a policy, rule or requirement that references a generated value is `PLAY0273`, and generated-concept rules select v20 with PLAY0688/0689 proof diagnostics. Stage renders supported UUID/String strategies, scalar/named responses and fixtures; unsupported types/result combinations remain `-028`/`-038` |
266
266
  | Authorable, not executable (PLAY0268) | Systems/operations, the `numbers exact` source mode (PLAY0268; exact lowering of qualifying Double literals is the admitted v11) remain unadmitted |
267
267
  | Concept generation strategies (ESM v15) | Required scalar generatable Uuid/String concepts; concept-owned uuid/uuidv7/ulid/typeid/nanoid/bytes strategies and admitted modifiers. Strict fixture encoding; Guarded whole-source freshness rejects Concurrency with no append/retry. Generated secrets and sequence remain refused; generated-concept rules select v20, pattern/random/rule and checksum validation select v21. Stage renders supported strategies: command-surface `references/generated-values-and-responses.md` |
@@ -97,6 +97,8 @@ model's behalf.
97
97
  mode in a design model is a recorded gap, not a defect to delete. Removing protection
98
98
  (`pii`, authorization, rules) to pass a tool is always a critical finding.
99
99
  Protection and reaction-privilege checks: [mode compliance](references/mode-compliance.md).
100
+ A client id, secret, authority, sender or mail server in a model is a critical finding: those
101
+ belong in the environment (`cratis-screenplay-authentication-and-onboarding`).
100
102
  12. **Root causes, classify, report** (`references/report-template.md`). Group findings that share
101
103
  a declaration; report the root cause with its symptoms under it; severity critical, major or
102
104
  minor; kind defect, question or note; verdict with confidence. Never apply changes.
@@ -60,6 +60,14 @@ Requires Stage 4.138.0 for the Cratis renderer to render protected reads.
60
60
  - Admission can pass and a Debug test still fail. Admission is never a passing test; see
61
61
  `cratis-stage-rendering-and-sandbox` `references/render-example.md`.
62
62
 
63
+ ## Authentication and onboarding are realized
64
+ A model's `authentication` and `onboarding` blocks are rendered by Stage (AuthProxy and Ante
65
+ configuration, personas, provisioning, emails); do not gap-fill them by hand
66
+ (`cratis-stage-rendering-and-sandbox` `references/rendered-application.md`). A provider kind
67
+ AuthProxy does not support is left out of the render with the warning `STAGE-AUTH-001`; the
68
+ render still publishes, so report it as a model decision, not as a hand-written provider.
69
+ Requires cratis CLI 3.116.0 with Stage 4.211.0 (`cratis --version`); older releases report `authentication` and `onboarding` as unrendered warnings.
70
+
63
71
  ## Stage issues to cite in fallback record entries
64
72
  Historical issue pointers (not a fresh issue-state check): portable query semantics Stage#58;
65
73
  Translate and unsupported automation forms Stage#79; mapping expressions and projection blocks Stage#117; modeled authentication
@@ -7,6 +7,8 @@ Which skill owns each task next to the toolchain.
7
7
  - Construct depth: `cratis-screenplay-command-surface`, `cratis-screenplay-projections`,
8
8
  `cratis-screenplay-read-surface`, `cratis-screenplay-captures-and-reactions`,
9
9
  `cratis-screenplay-specifications`, `cratis-screenplay-ui-composition`.
10
+ - Sign-in providers, onboarding and the environment keys a model needs: `cratis-screenplay-authentication-and-onboarding`;
11
+ creating the environment and setting secrets: `cratis-environments-and-secrets`.
10
12
  - Rendering and gap-fill: `cratis-screenplay-render-and-gap-fill`,
11
13
  `cratis-stage-rendering-and-sandbox`.
12
14
  - Running systems (Chronicle server operations through the `cratis` CLI, with its authorization boundaries): `cratis-chronicle-cli-operations`.
@@ -26,5 +26,75 @@
26
26
  "kind": "diagnostic",
27
27
  "value": "PLAY0686",
28
28
  "reason": "Explicit retirement notice, not an active diagnostic or repair recommendation. The published contract marks PLAY0686 retired. Remove when this notice is removed or replaced."
29
+ },
30
+ {
31
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md",
32
+ "text": "screenplay environment ./model # one key per line, secrets marked \"(secret)\"",
33
+ "kind": "cliCommand",
34
+ "value": "environment",
35
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
36
+ },
37
+ {
38
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md",
39
+ "text": "screenplay environment ./model --format json # key, secret, source, owner, description",
40
+ "kind": "cliCommand",
41
+ "value": "environment",
42
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
43
+ },
44
+ {
45
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md",
46
+ "text": "screenplay environment ./model --format json # key, secret, source, owner, description",
47
+ "kind": "cliOption",
48
+ "value": "--format",
49
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
50
+ },
51
+ {
52
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
53
+ "text": "| `PLAY0826` | The `onboarding` line carries more than the keyword. | Move content into the body. |",
54
+ "kind": "diagnostic",
55
+ "value": "PLAY0826",
56
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
57
+ },
58
+ {
59
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
60
+ "text": "| `PLAY0827` | More than one `onboarding` block. | Merge them. |",
61
+ "kind": "diagnostic",
62
+ "value": "PLAY0827",
63
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
64
+ },
65
+ {
66
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
67
+ "text": "| `PLAY0828` | `onboarding` without `authentication`. | Declare the providers. Draft authoring reports a warning until the folder is assembled. |",
68
+ "kind": "diagnostic",
69
+ "value": "PLAY0828",
70
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
71
+ },
72
+ {
73
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
74
+ "text": "| `PLAY0829` | A line is not a directive, or one is malformed or incomplete. | Use the forms above. |",
75
+ "kind": "diagnostic",
76
+ "value": "PLAY0829",
77
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
78
+ },
79
+ {
80
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
81
+ "text": "| `PLAY0830` | A directive stated twice. | Keep one. |",
82
+ "kind": "diagnostic",
83
+ "value": "PLAY0830",
84
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
85
+ },
86
+ {
87
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
88
+ "text": "| `PLAY0831` | An invitation names a persona nothing declares. | Declare it with `persona`. |",
89
+ "kind": "diagnostic",
90
+ "value": "PLAY0831",
91
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
92
+ },
93
+ {
94
+ "file": ".cratis/ai/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md",
95
+ "text": "| `PLAY0832` | `emails` names an email onboarding does not send. | Use `invitation`, `welcome` or `signupDeclined`. |",
96
+ "kind": "diagnostic",
97
+ "value": "PLAY0832",
98
+ "reason": "Documents the onboarding and environment-key surface of Screenplay 4.259.0, which the pinned 4.245.0 contract predates. Remove when the pinned contract moves to 4.259.0 or later (the check then reports it unnecessary)."
29
99
  }
30
100
  ]
@@ -0,0 +1,9 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the canonical Cratis/AI repository for the full license text.
3
+
4
+ Third-party notices
5
+
6
+ This skill adapts material from agentic-engineer by Martin Dilger and Nebulit GmbH
7
+ (https://github.com/Nebulit-GmbH/agentic-engineer, commit 07b0f30648d663cb588d7e2c7aa031af9dfc21f2;
8
+ https://nebulit.de), used with the explicit consent of Martin Dilger and Nebulit GmbH. The adapted
9
+ material remains credited to Martin Dilger and Nebulit GmbH.
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: cratis-showrunner-herald
3
+ description: Use when a Cratis .NET application sends email such as invitations or welcome messages with Herald (`Cratis.Showrunner.Herald`), including `AddHerald`, `IEmails`, email templates and images, SMTP or the development folder transport. Not for a custom SMTP client or for Ante's token issuance.
4
+ license: MIT
5
+ ---
6
+
7
+ # Herald email
8
+
9
+ Herald sends the email an application needs from template and image files, and records every
10
+ email as a fact. It is a **library** that runs in the application's process and keeps its state
11
+ as events in the application's own Chronicle event store. It is not a service.
12
+
13
+ **Requires** `Cratis.Showrunner.Herald` 1.10.0 or later and an application that is a Chronicle
14
+ client (`AddCratis` or `AddCratisChronicle`); check the package reference in the `.csproj`.
15
+
16
+ ## Add it
17
+
18
+ ```csharp
19
+ builder.AddCratis();
20
+ builder.AddHerald(herald =>
21
+ {
22
+ herald.TemplatesFolder = Path.Combine(builder.Environment.ContentRootPath, "Emails");
23
+ herald.ImagesFolder = Path.Combine(builder.Environment.ContentRootPath, "Emails", "images");
24
+ herald.Sender = new() { Address = builder.Configuration["Herald:Sender"]!, Name = "Ledger" };
25
+ herald.Smtp = new()
26
+ {
27
+ Host = builder.Configuration["Herald:SmtpHost"]!,
28
+ Port = 587,
29
+ Security = SmtpSecurity.StartTls,
30
+ UserName = builder.Configuration["Herald:SmtpUserName"]!,
31
+ Password = builder.Configuration["Herald:SmtpPassword"]!
32
+ };
33
+ });
34
+ ```
35
+
36
+ - **Take the sender, host, user name and password from configuration, never literals.** For a
37
+ Screenplay application these are the `onboarding.email.*` environment values (see
38
+ [cratis-environments-and-secrets](../cratis-environments-and-secrets/SKILL.md)), and the
39
+ password is a secret that never enters source, a log, an event or the transcript.
40
+ - `TemplatesFolder` is required and must exist; `Sender.Address` is required. Options are
41
+ validated at startup and the error names the option.
42
+ - Defaults worth knowing: `DefaultLanguage` `en`, `Transport` `Smtp`, `Smtp.Security` `StartTls`,
43
+ `Retry.MaxAttempts` 6, `PollingInterval` 5 seconds. Read [options](references/options.md) for the full table.
44
+ - Chronicle finds Herald's events through type discovery of referenced `Cratis*` packages.
45
+ - Run the worker in **one** application instance: delivery is at least once.
46
+
47
+ ## Send
48
+
49
+ ```csharp
50
+ await emails.Send(new EmailRequest(
51
+ Kind: "invitation",
52
+ About: context.EventSourceId,
53
+ To: @event.Email,
54
+ Values: new Dictionary<string, string> { ["invitation.link"] = @event.Link })
55
+ {
56
+ ToName = @event.Name,
57
+ Language = @event.Language
58
+ });
59
+ ```
60
+
61
+ Send from where the decision is made: a reactor on the fact that an invitation was issued, or an
62
+ Arc command. `Send` renders first and throws `EmailCannotBeRendered` without queuing anything when
63
+ a template, value or image is wrong; otherwise it appends `EmailQueued` against `About`, returns
64
+ the `EmailId` and does not wait for the server. Pass the compliance `Subject` so erasing the
65
+ person erases the recipient and any link that works as a credential. Report a bounce with
66
+ `emails.Bounced(about, emailId, kind, reason)` and leave the address out of the reason.
67
+
68
+ ## Templates and images
69
+
70
+ A kind is lowercase letters, digits and dashes. The folder holds `{kind}.json` (the manifest of
71
+ accepted values) and `{kind}.{language}.html` per language. Read
72
+ [templates and images](references/templates-and-images.md) before writing either.
73
+
74
+ - Templates are strict Handlebars with only `{{value}}`, `{{#if value}}…{{else}}…{{/if}}` and `{{date value "format"}}`. Triple braces, partials and other helpers are refused.
75
+ - A value the manifest does not declare is an error, never an empty space. Every value is HTML-encoded.
76
+ - Images are PNG, JPEG or GIF referenced as `cid:name.png`. **SVG is refused**; export a PNG.
77
+
78
+ ## Facts and delivery
79
+
80
+ `EmailSent`, `EmailFailed` and `EmailBounced` are in `Cratis.Showrunner.Herald.Contracts`, carry
81
+ the `EmailId`, and never the recipient or a value; project and react to them. They are not
82
+ `[Public]`: they live in the event log so the application's own projections can follow them.
83
+ `EmailQueued` and `EmailAttemptFailed` are Herald's own. A change to a released event is a new generation, not an edit.
84
+
85
+ For development set `Transport = EmailTransportKind.Folder` and `PickupFolder`; each email is
86
+ written as an `.eml` file and nothing leaves the machine. To send through a provider API, register
87
+ a custom `IEmailTransport` before `AddHerald`.
88
+
89
+ ## Pitfalls
90
+
91
+ - Hard-coding the SMTP password in `AddHerald` or committing it: read it from configuration or the environment.
92
+ - Sending a new kind without its manifest, or with a value the manifest lacks: `EmailCannotBeRendered`.
93
+ - Putting a remote `https://` image in a template: use `cid:`.
94
+ - Running two worker instances: duplicate deliveries.
95
+
96
+ ## Verify
97
+
98
+ 1. `dotnet build` is clean and the host starts: option errors surface at startup naming the option.
99
+ 2. With the `Folder` transport, send one email in a spec or locally and open the `.eml`: the subject, plain-text alternative and embedded image are present.
100
+ 3. The event log has `EmailQueued` then `EmailSent` against the `About` id.
101
+
102
+ ## Route near misses
103
+
104
+ - Events, reactors and projections that call Herald: `cratis-chronicle-reactor`, `cratis-chronicle-projection`.
105
+ - Invitation tokens, the lobby and acceptance: `cratis-onboarding-runtime`.
106
+ - Erasing personal data: `cratis-chronicle-compliance`.
@@ -0,0 +1,35 @@
1
+ # Herald options
2
+
3
+ | Option | Default | Meaning |
4
+ |---|---|---|
5
+ | `TemplatesFolder` | required | Folder with the templates; must exist. |
6
+ | `ImagesFolder` | none | Folder with the images; optional when no template has an image. |
7
+ | `DefaultLanguage` | `en` | Language used when no template exists in the language asked for. |
8
+ | `Sender.Address` | required | The address the emails come from. |
9
+ | `Sender.Name`, `Sender.ReplyTo` | empty | Display name; where replies go. |
10
+ | `Look.BrandColor` | `#2563eb` | Button and link color as `#rrggbb`; templates use `{{look.brandColor}}`. |
11
+ | `Look.Footer` | empty | Shared footer; templates use `{{look.footer}}`. |
12
+ | `Transport` | `Smtp` | `Smtp`, or `Folder` to write `.eml` files. |
13
+ | `Smtp.Host`, `Smtp.Port` | none, `587` | The mail server. |
14
+ | `Smtp.Security` | `StartTls` | `StartTls` (587), `Tls` (465) or `None` (a relay on the same machine or network). |
15
+ | `Smtp.UserName`, `Smtp.Password` | empty | Sign-in; leave both empty for an open relay. The password is a secret. |
16
+ | `PickupFolder` | none | Where the `Folder` transport writes. |
17
+ | `Retry.MaxAttempts` | `6` | Deliveries tried before the email fails for good. |
18
+ | `Retry.InitialDelay`, `Retry.MaxDelay` | 30 s, 1 h | Wait after the first failure, doubling up to the maximum. |
19
+ | `PollingInterval` | 5 s | How often queued emails that are due are looked for. |
20
+ | `Namespace` | default namespace | Event store namespace for the facts. |
21
+
22
+ The server certificate is always validated; there is no switch to turn it off.
23
+
24
+ ## Failure handling
25
+
26
+ | What happened | Result |
27
+ |---|---|
28
+ | Server unreachable, sign-in refused, timeout | `EmailAttemptFailed`, retried with growing delay. A bad sign-in is retried because it can be fixed while the email waits. |
29
+ | Server refuses message or recipient for good (550 to 554) | `EmailFailed` at once. |
30
+ | Last allowed attempt fails | `EmailFailed` with the attempt count. |
31
+ | Template or image gone when due | `EmailFailed` with zero attempts. |
32
+
33
+ Failure reasons are sentences Herald wrote, with the password and recipient address removed. An
34
+ unexpected failure is recorded by its kind only. Which emails are waiting comes from the event
35
+ log; the `QueuedEmail` read model lists emails with no outcome yet, for a count.
@@ -0,0 +1,70 @@
1
+ # Templates and images
2
+
3
+ ## Layout
4
+
5
+ ```
6
+ Emails/
7
+ invitation.json the values the invitation email accepts
8
+ invitation.en.html English
9
+ invitation.nb.html Norwegian
10
+ images/
11
+ logo.png
12
+ ```
13
+
14
+ ## Manifest
15
+
16
+ ```json
17
+ {
18
+ "values": [
19
+ { "name": "invitation.link", "required": true },
20
+ "invitation.expiresAt",
21
+ "inviter.name"
22
+ ]
23
+ }
24
+ ```
25
+
26
+ A plain string is an optional value. A required value that is missing or blank stops the email
27
+ before it is queued. A name cannot be both a value and a group (`inviter` and `inviter.name`),
28
+ and `look` is reserved for `look.brandColor` and `look.footer`.
29
+
30
+ ## Template
31
+
32
+ A header between two `---` lines carries `subject` and optionally `preview`; both may use values.
33
+ Then email-ready HTML.
34
+
35
+ ```html
36
+ ---
37
+ subject: {{inviter.name}} invited you to Ledger
38
+ preview: Accept the invitation before {{date invitation.expiresAt "d MMMM"}}
39
+ ---
40
+ <html>
41
+ <head><style>.button { background-color: {{look.brandColor}}; color: #ffffff; }</style></head>
42
+ <body>
43
+ <table role="presentation" width="600" align="center"><tr><td>
44
+ <img src="cid:logo.png" alt="Ledger" width="120">
45
+ {{#if inviter.name}}<p>{{inviter.name}} invited you.</p>{{/if}}
46
+ <a class="button" href="{{invitation.link}}">Accept the invitation</a>
47
+ <p>{{look.footer}}</p>
48
+ </td></tr></table>
49
+ </body>
50
+ </html>
51
+ ```
52
+
53
+ Author for email clients: table layout, about 600 px wide, `role="presentation"` on layout tables,
54
+ an `alt` on every image. Write styles in a `<style>` element; Herald moves the rules into `style`
55
+ attributes at render. Media queries stay in the element.
56
+
57
+ ## Rendering rules
58
+
59
+ - Every value is HTML-encoded in the body, quotes in attributes included. Subject and preview are plain text and not encoded.
60
+ - The subject is collapsed to one line. The plain-text alternative is generated from the HTML.
61
+ - Dates are ISO 8601 and formatted in the culture of the email's language.
62
+ - Language falls back from `nb-NO` to `nb` to `DefaultLanguage`; the language used is recorded.
63
+
64
+ ## Images
65
+
66
+ Referenced as `cid:logo.png` and embedded inline, so nothing is hosted and no remote content has
67
+ to be allowed. PNG, JPEG and GIF are embedded. SVG and anything else fail the rendering with an
68
+ error naming the file, because mail clients do not render SVG; export a PNG at twice the display
69
+ size. A referenced image that is missing also fails rendering. File names use letters, digits,
70
+ dots, dashes and underscores and are looked up only inside the images folder.
@@ -89,8 +89,8 @@ before changing runner or package family.
89
89
 
90
90
  The plain `Specification` base is the universal foundation and the dominant mode
91
91
  in library and framework code. In applications, start pure decisions with a
92
- plain call, including direct validator calls, and add scenarios only for
93
- pipeline/authorization wiring, constraints, or declarative mapping. Keep shared lifecycle on; measure scenario cost using
92
+ plain call, including direct validator calls, and add the scenarios the
93
+ per-artifact decision table in `specs.scenarios.csharp.md` obligates. Keep shared lifecycle on; measure scenario cost using
94
94
  [running-specs.md](references/running-specs.md). Read
95
95
  [application-scenarios.md](references/application-scenarios.md) before using
96
96
  one. Out-of-process integration specifications are an advanced case reserved for
@@ -206,6 +206,11 @@ generated application: API on `9090`, Workbench on `https://localhost:35000`.
206
206
  Requires Cratis CLI 3.73.0 for the semantic engine by default, 3.75.0 for v27 keys;
207
207
  older CLIs use the legacy EventModel engine, which does not enforce authorization.
208
208
  Unsigned caller headers make sandbox authorization no security boundary.
209
+ A model with `authentication` makes `cratis run` start a stack instead: AuthProxy (the
210
+ address to open), the Stage with `Stage__Sandbox__ForwardedIdentity=true` so it trusts the
211
+ forwarded identity instead of playing a persona, and with `onboarding` Ante and MongoDB.
212
+ `cratis render` realizes authentication and onboarding (`references/rendered-application.md`).
213
+ Requires cratis CLI 3.116.0 (`cratis --version`); older CLIs run the Stage alone and play a persona.
209
214
  Read [references/sandbox-and-specrunner.md](references/sandbox-and-specrunner.md)
210
215
  before running either image: engine selection, protected reads, outside-append
211
216
  conflicts/retries, reached bodies, authored occurrence and generated-spec limits live there.