@cratis/pi 7.92.0 → 7.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/harnesses/copilot/instructions/discoverability.instructions.md +34 -0
- package/package/corpus/hooks/scripts/cratis-patterns.json +24 -0
- package/package/corpus/prompts/pre-publish-check.prompt.md +26 -0
- package/package/corpus/rules/discoverability.md +33 -0
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/skills/cratis-discoverability/LICENSE +2 -0
- package/package/corpus/skills/cratis-discoverability/SKILL.md +73 -0
- package/package/corpus/skills/cratis-discoverability/references/entities.md +68 -0
- package/package/corpus/skills/cratis-discoverability/references/package-metadata.md +38 -0
- package/package/corpus/skills/cratis-discoverability/references/page-checklist.md +88 -0
- package/package/corpus/skills/cratis-discoverability/references/structured-data.md +62 -0
- package/package/corpus/skills/cratis-discoverability/scripts/verify-discoverability.mjs +107 -0
- package/package/corpus/skills/cratis-discoverability/verification.json +13 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/references/text-and-match-rules.md +17 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +14 -2
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-model-review/references/checklist.md +3 -3
- package/package/corpus/skills/cratis-screenplay-model-review/references/model-inventory-report.md +2 -1
- package/package/corpus/skills/cratis-screenplay-model-review/references/verified-sources.md +1 -1
- package/package/corpus/skills/cratis-screenplay-read-surface/SKILL.md +3 -3
- package/package/corpus/skills/cratis-screenplay-read-surface/references/equality-filters.md +1 -1
- package/package/corpus/skills/cratis-screenplay-read-surface/references/named-keys.md +1 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +8 -2
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/scenario-examples.md +2 -2
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +4 -3
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/view-and-story-specs.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/references/read-model-design.md +3 -2
- package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +6 -2
- package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +19 -0
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/stream-id-rules.md +4 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +9 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +10 -8
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +1 -1
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +1 -1
- package/package/profile-catalog.json +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/Documentation/**/*.md,**/Documentation/**/*.mdx,**/src/pages/**/*.astro,**/src/pages/**/*.md,**/src/pages/**/*.mdx,**/src/pages/**/*.html,**/src/content/**/*.md,**/src/content/**/*.mdx"
|
|
3
|
+
paths:
|
|
4
|
+
- "**/Documentation/**/*.md"
|
|
5
|
+
- "**/Documentation/**/*.mdx"
|
|
6
|
+
- "**/src/pages/**/*.astro"
|
|
7
|
+
- "**/src/pages/**/*.md"
|
|
8
|
+
- "**/src/pages/**/*.mdx"
|
|
9
|
+
- "**/src/pages/**/*.html"
|
|
10
|
+
- "**/src/content/**/*.md"
|
|
11
|
+
- "**/src/content/**/*.mdx"
|
|
12
|
+
---
|
|
13
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
14
|
+
|
|
15
|
+
# Writing content that people and assistants can find
|
|
16
|
+
|
|
17
|
+
Readability comes first. If a change for search engines or AI assistants makes a page harder to read, drop the change. Voice, structure and examples stay with [writing-cratis-docs.md](./writing-cratis-docs.instructions.md), [editing-cratis-docs.md](./editing-cratis-docs.instructions.md) and [documentation-structure-and-formatting.md](./documentation-structure-and-formatting.instructions.md); this rule adds only what they do not cover. Before publishing a page, post or package, use **cratis-discoverability**.
|
|
18
|
+
|
|
19
|
+
## Every page, post and README
|
|
20
|
+
|
|
21
|
+
- **Define first, where a definition belongs.** Landing pages, product index pages, glossary entries, READMEs and package descriptions open with one self-contained sentence: "X is a <category> for <audience> that <does>."
|
|
22
|
+
- **Docs tutorials, getting-started pages and explanations follow [writing-cratis-docs.md](./writing-cratis-docs.instructions.md)**: scenario first, pain before relief. Their answer-first, self-contained definition goes in the frontmatter `description`, which serves search and AI. Where it reads naturally, one definition sentence may follow the opening scenario. writing-cratis-docs wins on voice and structure.
|
|
23
|
+
- **Title and description are written by hand.** Title with the distinguishing term first: 50-60 characters for blog and landing pages, 20-60 for docs and reference pages; a README H1 that is the product name is fine. Description is one sentence of 60-160 characters, never over 160, and not a copy of the title or of another page's description.
|
|
24
|
+
- **Name things one way.** The first mention is the full name ("Cratis Chronicle"), later mentions may shorten it. Use the canonical names in the **cratis-discoverability** entity reference, and do not call the platform "Cratis.io".
|
|
25
|
+
- **Links name their destination.** Link text says what is behind it. Outside the docs site, link docs with the absolute canonical host `https://www.cratis.io/...`, never the bare `cratis.io`.
|
|
26
|
+
- **Images carry alt text.** Describe the content. Use an empty `alt=""` only for decoration.
|
|
27
|
+
- **No keyword stuffing, no filler for length, no text hidden from readers.** A page is done when it is complete.
|
|
28
|
+
|
|
29
|
+
## Sites and packages
|
|
30
|
+
|
|
31
|
+
- Public pages need a canonical link, one `<h1>`, Open Graph tags, valid JSON-LD that describes visible content, a sitemap entry and a `robots.txt` that allows crawling. Add no new `HowTo` or `FAQPage` markup; keep the visible Q&A.
|
|
32
|
+
- `llms.txt` is an optional index. Never claim it improves ranking. See **cratis-llm-friendly-documentation**.
|
|
33
|
+
- Package metadata (`Description`, `PackageProjectUrl`, `PackageTags`, npm `description`, `homepage`, `keywords`) and the README opener use the same definition sentence, and the project URL points at the docs page `https://www.cratis.io/<product>/`.
|
|
34
|
+
- State only facts the repository supports: versions, licenses, limits. The claim gate in `BRAND.md`, `MESSAGING.md` and `SITE.md` still applies.
|
|
@@ -87,6 +87,30 @@
|
|
|
87
87
|
"paths": ["**/*.ts", "**/*.tsx"],
|
|
88
88
|
"exclude_paths": ["**/node_modules/**", "**/dist/**", "**/wwwroot/**", "**/obj/**", "**/bin/**"],
|
|
89
89
|
"message": "Cratis Components 4 has no PrimeReact underneath — a `primereact/dialog` import is a leftover from Components 3. Use `CommandDialog` from `@cratis/components/CommandDialog` or `Dialog` from `@cratis/components/Dialogs`."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"id": "cratis-apex-docs-link",
|
|
93
|
+
"rule": "discoverability.md — canonical links",
|
|
94
|
+
"pattern": "https?://cratis\\.io([/)\"' <]|$)",
|
|
95
|
+
"paths": ["**/*.md", "**/*.mdx", "**/*.html", "**/*.astro"],
|
|
96
|
+
"exclude_paths": ["**/node_modules/**", "**/dist/**", "**/obj/**", "**/bin/**", "**/.ai-work/**"],
|
|
97
|
+
"message": "Discoverability (advisory): the docs host is `https://www.cratis.io`. Link the canonical host, not the bare `cratis.io`."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "cratis-empty-image-alt",
|
|
101
|
+
"rule": "discoverability.md — images carry alt text",
|
|
102
|
+
"pattern": "!\\[\\]\\(",
|
|
103
|
+
"paths": ["**/*.md", "**/*.mdx"],
|
|
104
|
+
"exclude_paths": ["**/node_modules/**", "**/dist/**", "**/obj/**", "**/bin/**", "**/.ai-work/**"],
|
|
105
|
+
"message": "Discoverability (advisory): an image with empty alt text. Describe the content, or keep it empty only when the image is purely decorative."
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "cratis-package-url-github-only",
|
|
109
|
+
"rule": "discoverability.md — package metadata",
|
|
110
|
+
"pattern": "<PackageProjectUrl>[[:space:]]*https?://github\\.com",
|
|
111
|
+
"paths": ["**/*.csproj", "**/*.props"],
|
|
112
|
+
"exclude_paths": ["**/node_modules/**", "**/obj/**", "**/bin/**"],
|
|
113
|
+
"message": "Discoverability (advisory): `PackageProjectUrl` should point at the product's docs page `https://www.cratis.io/<product>/`, not only at GitHub."
|
|
90
114
|
}
|
|
91
115
|
]
|
|
92
116
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
agent: agent
|
|
3
|
+
description: "Run the discoverability check on content about to be published."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pre-publish check
|
|
7
|
+
|
|
8
|
+
Run the discoverability check on what is about to be published: a docs page, blog
|
|
9
|
+
post, landing page, README or package metadata. Follow
|
|
10
|
+
`.cratis/ai/rules/discoverability.md` and invoke **cratis-discoverability**.
|
|
11
|
+
|
|
12
|
+
## Identify the content
|
|
13
|
+
|
|
14
|
+
Find the files in the change, the content type of each, and the owning source (never
|
|
15
|
+
a synced copy). Ask only when the content type is unclear.
|
|
16
|
+
|
|
17
|
+
## Check and fix
|
|
18
|
+
|
|
19
|
+
Apply the skill's checks for each content type to the draft: opening definition,
|
|
20
|
+
title and description, canonical names, canonical `https://www.cratis.io/...` links,
|
|
21
|
+
alt text and, for sites and packages, the structured data and metadata. Make the
|
|
22
|
+
fixes in the owning files. Where a fix would hurt readability, keep the text and
|
|
23
|
+
report it. For a built site, run the skill's checker on the build output.
|
|
24
|
+
|
|
25
|
+
Report what changed, what was left alone and why, and any check that could not run.
|
|
26
|
+
Publishing, merging and releasing stay with a person.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/Documentation/**/*.md,**/Documentation/**/*.mdx,**/src/pages/**/*.astro,**/src/pages/**/*.md,**/src/pages/**/*.mdx,**/src/pages/**/*.html,**/src/content/**/*.md,**/src/content/**/*.mdx"
|
|
3
|
+
paths:
|
|
4
|
+
- "**/Documentation/**/*.md"
|
|
5
|
+
- "**/Documentation/**/*.mdx"
|
|
6
|
+
- "**/src/pages/**/*.astro"
|
|
7
|
+
- "**/src/pages/**/*.md"
|
|
8
|
+
- "**/src/pages/**/*.mdx"
|
|
9
|
+
- "**/src/pages/**/*.html"
|
|
10
|
+
- "**/src/content/**/*.md"
|
|
11
|
+
- "**/src/content/**/*.mdx"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Writing content that people and assistants can find
|
|
15
|
+
|
|
16
|
+
Readability comes first. If a change for search engines or AI assistants makes a page harder to read, drop the change. Voice, structure and examples stay with [writing-cratis-docs.md](./writing-cratis-docs.md), [editing-cratis-docs.md](./editing-cratis-docs.md) and [documentation-structure-and-formatting.md](./documentation-structure-and-formatting.md); this rule adds only what they do not cover. Before publishing a page, post or package, use **cratis-discoverability**.
|
|
17
|
+
|
|
18
|
+
## Every page, post and README
|
|
19
|
+
|
|
20
|
+
- **Define first, where a definition belongs.** Landing pages, product index pages, glossary entries, READMEs and package descriptions open with one self-contained sentence: "X is a <category> for <audience> that <does>."
|
|
21
|
+
- **Docs tutorials, getting-started pages and explanations follow [writing-cratis-docs.md](./writing-cratis-docs.md)**: scenario first, pain before relief. Their answer-first, self-contained definition goes in the frontmatter `description`, which serves search and AI. Where it reads naturally, one definition sentence may follow the opening scenario. writing-cratis-docs wins on voice and structure.
|
|
22
|
+
- **Title and description are written by hand.** Title with the distinguishing term first: 50-60 characters for blog and landing pages, 20-60 for docs and reference pages; a README H1 that is the product name is fine. Description is one sentence of 60-160 characters, never over 160, and not a copy of the title or of another page's description.
|
|
23
|
+
- **Name things one way.** The first mention is the full name ("Cratis Chronicle"), later mentions may shorten it. Use the canonical names in the **cratis-discoverability** entity reference, and do not call the platform "Cratis.io".
|
|
24
|
+
- **Links name their destination.** Link text says what is behind it. Outside the docs site, link docs with the absolute canonical host `https://www.cratis.io/...`, never the bare `cratis.io`.
|
|
25
|
+
- **Images carry alt text.** Describe the content. Use an empty `alt=""` only for decoration.
|
|
26
|
+
- **No keyword stuffing, no filler for length, no text hidden from readers.** A page is done when it is complete.
|
|
27
|
+
|
|
28
|
+
## Sites and packages
|
|
29
|
+
|
|
30
|
+
- Public pages need a canonical link, one `<h1>`, Open Graph tags, valid JSON-LD that describes visible content, a sitemap entry and a `robots.txt` that allows crawling. Add no new `HowTo` or `FAQPage` markup; keep the visible Q&A.
|
|
31
|
+
- `llms.txt` is an optional index. Never claim it improves ranking. See **cratis-llm-friendly-documentation**.
|
|
32
|
+
- Package metadata (`Description`, `PackageProjectUrl`, `PackageTags`, npm `description`, `homepage`, `keywords`) and the README opener use the same definition sentence, and the project URL points at the docs page `https://www.cratis.io/<product>/`.
|
|
33
|
+
- State only facts the repository supports: versions, licenses, limits. The claim gate in `BRAND.md`, `MESSAGING.md` and `SITE.md` still applies.
|
|
@@ -131,6 +131,7 @@ A claim is only as good as the signal behind it — a build result, a test run,
|
|
|
131
131
|
| C# / TypeScript style and code-quality principles | `csharp.md`, `typescript.md`, `code-quality.md`, `code-quality.csharp.md`, `code-quality.typescript.md` |
|
|
132
132
|
| Kotlin / Java / Elixir style; Orleans grains; reactors; GitHub Actions workflows | `kotlin.md`, `java.md`, `elixir.md`, `orleans.md`, `reactors.md`, `github-actions.md` |
|
|
133
133
|
| Documentation pages (`Documentation/`): structure, voice, examples, editing | `documentation.md`, `documentation-structure-and-formatting.md`, `writing-cratis-docs.md`, `editing-cratis-docs.md`, `writing-correct-examples.md` |
|
|
134
|
+
| Findability of published pages, READMEs and package metadata | `discoverability.md` |
|
|
134
135
|
| Go style, source and module files | `go.md`; framework contributors also read `go-engineering.md`; product APIs use `cratis-chronicle-client-go`, `cratis-arc-go` and `cratis-fundamentals-concept-go` |
|
|
135
136
|
| Service lifetimes — why anything taking a scoped dependency is scoped or transient, never a singleton | `csharp.md` |
|
|
136
137
|
| React + Arc + Cratis Components + MVVM + dialogs | `react.md`, `components.md`, `dialogs.md` |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-discoverability
|
|
3
|
+
description: Run the pre-publish check that makes Cratis content easy to find in search and AI answers without making it harder to read - a docs page, blog post, landing page, README or package metadata (title, description, opening definition, canonical links, entity names, alt text, structured data). Use when about to publish or review content for discoverability, SEO, AEO or GEO, or when writing package descriptions and README openers. Not for page voice and structure (cratis-documentation-writing), llms.txt design (cratis-llm-friendly-documentation), distribution plans (cratis-developer-marketing) or length decisions (cratis-content-length).
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Discoverability before you publish
|
|
8
|
+
|
|
9
|
+
Discoverability is a property of good content: a clear definition, an honest title, one name per
|
|
10
|
+
thing and working links. The always-on rule is [discoverability.md](../../rules/discoverability.md);
|
|
11
|
+
this skill is the procedure to run on the finished draft. If a fix makes the page worse to read,
|
|
12
|
+
drop the fix and say so.
|
|
13
|
+
|
|
14
|
+
## Procedure
|
|
15
|
+
|
|
16
|
+
1. Name the content type: docs page, blog post, landing page, README or package metadata.
|
|
17
|
+
2. Name the reader's one job and, for docs, the page type (tutorial, how-to, reference, explanation).
|
|
18
|
+
3. Apply the checks for that type below. Fix the draft; do not annotate it.
|
|
19
|
+
4. For a built site, run the checker and fix errors; warnings are judgment calls to report.
|
|
20
|
+
5. Report what was changed, what was left alone and why.
|
|
21
|
+
|
|
22
|
+
Read `references/page-checklist.md` for the full per-type checklist and the field lengths.
|
|
23
|
+
|
|
24
|
+
## Checks by content type
|
|
25
|
+
|
|
26
|
+
- **Docs page.** The `description` is written by hand as one self-contained
|
|
27
|
+
definition or outcome sentence. Tutorials, getting-started pages and explanations follow
|
|
28
|
+
`writing-cratis-docs` (scenario first); the definition lives in the `description`, and one
|
|
29
|
+
definition sentence may follow the opening scenario where it reads naturally. `writing-cratis-docs`
|
|
30
|
+
wins on voice and structure. Product index pages open with the definition sentence.
|
|
31
|
+
Reference pages stay terse: title is the exact name plus its product, no question headings, no
|
|
32
|
+
intro. Titles are unique across the site and name the product ("Chronicle projections").
|
|
33
|
+
- **Blog post.** The title states the topic in the words a reader would search. The intro gives the
|
|
34
|
+
conclusion in two sentences. Show the named author and date, add `lastUpdated` only for real
|
|
35
|
+
changes, link the owning docs pages with `https://www.cratis.io/...`, keep `description` apart
|
|
36
|
+
from the card `excerpt`.
|
|
37
|
+
- **Landing page.** One primary term in the title, the `<h1>` or the first paragraph, and a
|
|
38
|
+
one-sentence definition above the fold. Brand lines stay; pair them with a category term in the
|
|
39
|
+
title and description. Respect the claim gate in `BRAND.md`, `MESSAGING.md` and `SITE.md`.
|
|
40
|
+
- **README.** The H1 may be the product name ("Cratis <Product>"). The first sentence defines the product in 15-35 plain
|
|
41
|
+
nouns, with no logo-only or metaphor opener. Links are absolute; link the docs page.
|
|
42
|
+
- **Package metadata.** One sentence naming what the package is and which Cratis product it belongs
|
|
43
|
+
to, identical to the README opener. `PackageProjectUrl` and `homepage` point at the docs page, not
|
|
44
|
+
only at GitHub. Read `references/package-metadata.md` for the field map.
|
|
45
|
+
|
|
46
|
+
## Names
|
|
47
|
+
|
|
48
|
+
Use the canonical names, one per concept, and the first-mention rule. Read
|
|
49
|
+
`references/entities.md` when naming a product, writing a definition sentence or filling `sameAs`.
|
|
50
|
+
Do not invent a name, URL, maturity word or claim that the references do not state.
|
|
51
|
+
|
|
52
|
+
## Structured data
|
|
53
|
+
|
|
54
|
+
Describe only what is visible on the page, one `@graph` per page, stable `@id`s. Add no `HowTo` and
|
|
55
|
+
no new `FAQPage`. Read `references/structured-data.md` for the patterns and what each page type
|
|
56
|
+
gets. Do not name people in structured data.
|
|
57
|
+
|
|
58
|
+
## Checking a built site
|
|
59
|
+
|
|
60
|
+
For a built static site (Astro `dist/` or a plain-HTML repository root), run with Node.js:
|
|
61
|
+
`node <skill-directory>/scripts/verify-discoverability.mjs --root <dir> --site <https://host>`.
|
|
62
|
+
It reports `<html lang>`, title, description, canonical host, `<h1>` count, JSON-LD parse errors,
|
|
63
|
+
`robots.txt`, the sitemap and `llms.txt` structure. Errors exit 1; warnings do not. Pass
|
|
64
|
+
`--warn-only` to downgrade errors and `--require-llms` where the site ships `llms.txt`.
|
|
65
|
+
Read `scripts/verify-discoverability.mjs` only when changing the checker.
|
|
66
|
+
|
|
67
|
+
## Pitfalls
|
|
68
|
+
|
|
69
|
+
- Question headings everywhere: use them only where they read naturally (concept pages, FAQs,
|
|
70
|
+
comparisons, blog), never on reference pages or tutorial steps.
|
|
71
|
+
- Padding to a word count: there is no minimum length; a complete page is done.
|
|
72
|
+
- Apex `https://cratis.io/` links: the docs host is `https://www.cratis.io`.
|
|
73
|
+
- A description that repeats the title or a sibling page's description.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Entity names and definitions
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Naming rules
|
|
6
|
+
- Shared identifiers
|
|
7
|
+
- Definition sentences
|
|
8
|
+
- Disambiguation
|
|
9
|
+
|
|
10
|
+
Only facts the Cratis repositories state are listed. If a product, URL or identifier is not here,
|
|
11
|
+
find it in the product's own README or docs before writing it; do not guess.
|
|
12
|
+
|
|
13
|
+
## Naming rules
|
|
14
|
+
|
|
15
|
+
- First mention on a page: "Cratis Chronicle". Later mentions may be "Chronicle". In package
|
|
16
|
+
descriptions, README H1s and GitHub About text, always the prefixed form.
|
|
17
|
+
- The product is Cratis Direct. "Direct" and "Stagehand" are internal or older names.
|
|
18
|
+
- Cratis Build, Cratis Studio and Cratis Assurance are the three offers on cratis.no. In technical
|
|
19
|
+
copy and metadata, name the product (Chronicle, Arc), not the offer.
|
|
20
|
+
- The product is not "Cratis.io". `cratis.io` is the docs host, canonical `https://www.cratis.io`.
|
|
21
|
+
- "Cratis" alone is both the company and the platform. Say "the Cratis platform" where the company
|
|
22
|
+
could be meant.
|
|
23
|
+
- Cratis Studio maturity: follow `BRAND.md` (Preview) until the owner decides otherwise.
|
|
24
|
+
|
|
25
|
+
## Shared identifiers
|
|
26
|
+
|
|
27
|
+
| Item | Value |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| Organization `@id` | `https://cratis.no/#organization` |
|
|
30
|
+
| Company site | `https://cratis.no` |
|
|
31
|
+
| Docs (canonical host) | `https://www.cratis.io` |
|
|
32
|
+
| Blog | `https://blog.cratis.io` |
|
|
33
|
+
| GitHub organization | `https://github.com/Cratis` |
|
|
34
|
+
| NuGet profile | `https://www.nuget.org/profiles/Cratis` |
|
|
35
|
+
| npm organization | `https://www.npmjs.com/org/cratis` |
|
|
36
|
+
| YouTube | `https://www.youtube.com/@CratisStack` |
|
|
37
|
+
|
|
38
|
+
Product docs follow `https://www.cratis.io/<product>/`.
|
|
39
|
+
|
|
40
|
+
## Definition sentences
|
|
41
|
+
|
|
42
|
+
Use these as the opening sentence of a definitional page, README or package description. Adjust only
|
|
43
|
+
to fit the page; keep the facts.
|
|
44
|
+
|
|
45
|
+
| Name | Sentence | Docs page (under `https://www.cratis.io`) |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| Cratis (platform) | Cratis is an open-source (MIT) event sourcing and CQRS platform for .NET, built around the Chronicle event-sourcing database. | `https://www.cratis.io/` |
|
|
48
|
+
| Cratis Chronicle | Cratis Chronicle is an open-source (MIT) event-sourcing database and runtime with a .NET kernel and clients for several languages. | `/chronicle/` |
|
|
49
|
+
| Cratis Arc | Cratis Arc is an open-source (MIT) CQRS application framework for ASP.NET Core with generated TypeScript proxies. | `/arc/` |
|
|
50
|
+
| Cratis Fundamentals | Cratis Fundamentals is the set of shared building blocks beneath the Cratis stack for .NET and TypeScript. | `/fundamentals/` |
|
|
51
|
+
| Cratis Studio | Cratis Studio is a collaborative environment for designing, visualizing and editing Screenplay event models. | `/studio/` |
|
|
52
|
+
| Cratis Direct | Cratis Direct is the managed control plane for the Cratis software factory: it turns GitHub signals into prioritized, governed AI work and reviewed pull requests. | none confirmed |
|
|
53
|
+
| Cratis Cast | Cratis Cast is a profile-site engine and directory for the people who build with Cratis, with verified, event-sourced profile pages. | `https://cast.cratis.io` |
|
|
54
|
+
| Cratis Screenplay | Cratis Screenplay is a business-oriented declarative language for specifying the desired functionality of an information system. | `/screenplay/` |
|
|
55
|
+
| Cratis Stage | Cratis Stage compiles Screenplay source, verifies modeled specifications, and renders or partially performs Cratis applications. | none confirmed |
|
|
56
|
+
| Cratis Prologue | Cratis Prologue captures selected signals from an existing system and interprets them into a provisional event model for review. | `/prologue/` |
|
|
57
|
+
| Cratis CLI | The Cratis CLI is a terminal tool for inspecting and diagnosing Cratis Chronicle. | `/cli/` |
|
|
58
|
+
| Chronicle Workbench | Chronicle Workbench is the web dashboard bundled with Cratis Chronicle for browsing events and operating a deployment. | `/chronicle/workbench/` |
|
|
59
|
+
| Chronicle MCP | The Chronicle MCP server connects an AI agent to a running Cratis Chronicle database over the Model Context Protocol. | `/chronicle-mcp/` |
|
|
60
|
+
|
|
61
|
+
Chronicle storage is pluggable (MongoDB by default, PostgreSQL, SQL Server, SQLite, in-memory).
|
|
62
|
+
Arc works without event sourcing; the Chronicle integration is optional.
|
|
63
|
+
|
|
64
|
+
## Disambiguation
|
|
65
|
+
|
|
66
|
+
- `@cratis/arc` is the browser client of Arc .NET; `@cratis/arc.core` is the Node server package.
|
|
67
|
+
- "Cast" alone is not the streaming protocol: write "Cratis Cast".
|
|
68
|
+
- A docs slug not in the table is unconfirmed. Check the docs site before linking it.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Package metadata
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Field map
|
|
6
|
+
- Writing the description
|
|
7
|
+
- Pitfalls
|
|
8
|
+
|
|
9
|
+
## Field map
|
|
10
|
+
|
|
11
|
+
| Field | NuGet (`.csproj`, `Directory.Build.props`) | npm (`package.json`) |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Description | `<Description>`, 80-250 characters; the first 100 are what shows | `description`, 80-160 characters |
|
|
14
|
+
| Tags | `<PackageTags>`, 5-10, `cratis` plus category terms | `keywords`, the same list |
|
|
15
|
+
| Project URL | `<PackageProjectUrl>` = `https://www.cratis.io/<product>/` | `homepage`, the same |
|
|
16
|
+
| Repository | `RepositoryUrl` | `repository` |
|
|
17
|
+
| Authors | `Cratis` | `author` `Cratis` |
|
|
18
|
+
| License | `PackageLicenseExpression` `MIT` | `license` `MIT` |
|
|
19
|
+
| Readme | A packed README with the same opener | `README.md` |
|
|
20
|
+
|
|
21
|
+
Use a `https://www.cratis.io/<product>/` page that exists; check the docs site rather than guessing
|
|
22
|
+
a slug. If none exists, leave the existing URL and report it.
|
|
23
|
+
|
|
24
|
+
## Writing the description
|
|
25
|
+
|
|
26
|
+
One sentence: what it is, for whom, and which Cratis product it belongs to - "<what it is> for
|
|
27
|
+
<audience>, part of Cratis <Parent>". Name the parent so the package disambiguates ("the client for
|
|
28
|
+
Cratis Chronicle, the open-source event-sourcing database"). The text equals the README opener and
|
|
29
|
+
the GitHub About text. Tags are specific ("event-sourcing", "cqrs", "dotnet"); never "utilities".
|
|
30
|
+
|
|
31
|
+
## Pitfalls
|
|
32
|
+
|
|
33
|
+
- An empty `Description`.
|
|
34
|
+
- Mixed `Authors` values across packages of one product.
|
|
35
|
+
- `PackageProjectUrl` pointing only at GitHub.
|
|
36
|
+
- Two packages with near-identical descriptions. Where `@cratis/arc` (the browser client of Arc
|
|
37
|
+
.NET) and `@cratis/arc.core` (the Node server package) could be confused, each description says
|
|
38
|
+
which one it is.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Page checklist
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Field targets
|
|
6
|
+
- Every page
|
|
7
|
+
- Docs pages
|
|
8
|
+
- Blog posts
|
|
9
|
+
- Landing pages
|
|
10
|
+
- READMEs
|
|
11
|
+
- Before publishing a site change
|
|
12
|
+
|
|
13
|
+
## Field targets
|
|
14
|
+
|
|
15
|
+
Lengths are targets, counted in characters including any site suffix.
|
|
16
|
+
|
|
17
|
+
| Field | Target |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| Title | Distinguishing term first. Blog and landing pages 50-60; docs and reference pages 20-60; a README H1 that is the product name is fine |
|
|
20
|
+
| Description | One sentence, 60-160, never over 160; the point comes first |
|
|
21
|
+
| Lead answer passage | 40-60 words, names its subject, one key property, one limit |
|
|
22
|
+
| Heading | Under about 70 characters |
|
|
23
|
+
| `og:image` | 1200x630, under 200 KB, with `og:image:alt` |
|
|
24
|
+
|
|
25
|
+
## Every page
|
|
26
|
+
|
|
27
|
+
- [ ] One reader job. For docs, one page type: tutorial, how-to, reference or explanation.
|
|
28
|
+
- [ ] Title unique, distinguishing term near the front.
|
|
29
|
+
- [ ] Description written by hand; not the title, not another page's description.
|
|
30
|
+
- [ ] The `description` answers what the page is or achieves in one self-contained sentence; the first paragraph does too on landing, index, glossary and README pages.
|
|
31
|
+
- [ ] Canonical names; first mention uses the full "Cratis X" form.
|
|
32
|
+
- [ ] Facts, numbers and comparisons carry a source or a date; nothing beyond the claim gate.
|
|
33
|
+
- [ ] Links have descriptive text. Root-relative inside the docs site, absolute
|
|
34
|
+
`https://www.cratis.io/...` elsewhere.
|
|
35
|
+
- [ ] Images have alt text (empty only when decorative) and sensible size.
|
|
36
|
+
- [ ] Linked from at least two other pages and from the right hub; no orphan.
|
|
37
|
+
- [ ] Read aloud once: if it sounds written for a crawler, rewrite it.
|
|
38
|
+
|
|
39
|
+
## Docs pages
|
|
40
|
+
|
|
41
|
+
- Voice and structure follow `writing-cratis-docs`, which wins on any conflict: tutorials,
|
|
42
|
+
getting-started pages and explanations open with a concrete scenario (pain, then relief). Put the
|
|
43
|
+
answer-first definition in the frontmatter `description`; one definition sentence may follow the
|
|
44
|
+
scenario where it reads naturally.
|
|
45
|
+
- Product index page: opens with the one-sentence definition, then the without-versus-with framing.
|
|
46
|
+
- Concept page: scenario first; then why it exists, when to use it, when not to, how it works.
|
|
47
|
+
H2s such as "What is X" and "When to use X" only where they fit. No H1 in the body.
|
|
48
|
+
- Tutorial: title names the outcome and starts with a verb; the scenario, then what the reader will
|
|
49
|
+
have running; prerequisites and versions up front; one visible result per step; "Last updated" and the
|
|
50
|
+
version it applies to.
|
|
51
|
+
- How-to: the task as the title; goal, prerequisites, numbered steps, a done check, the two likely
|
|
52
|
+
failures; versions stated.
|
|
53
|
+
- Reference: the title is the exact name with its product; first sentence says what it is for;
|
|
54
|
+
tables and signatures; stable anchors; no intro, no pitch. Merge or enrich near-empty stub pages.
|
|
55
|
+
|
|
56
|
+
## Blog posts
|
|
57
|
+
|
|
58
|
+
- [ ] Title states the topic; series name goes in a `series` field or at the end.
|
|
59
|
+
- [ ] Two-sentence conclusion up front; "when this is the wrong fit" where it applies.
|
|
60
|
+
- [ ] Named author where agreed (`cratis-team` only for company announcements), date, `lastUpdated`
|
|
61
|
+
only on real changes.
|
|
62
|
+
- [ ] At least one internal link to the owning docs page; tags reused from the existing list, no
|
|
63
|
+
singleton tags; a cover image with alt text for posts likely to be shared.
|
|
64
|
+
|
|
65
|
+
## Landing pages
|
|
66
|
+
|
|
67
|
+
- [ ] One primary term in title, `<h1>` or first paragraph; one-sentence definition above the fold.
|
|
68
|
+
- [ ] Description differs per page and is not a 300-character paragraph.
|
|
69
|
+
- [ ] No testimonials, numbers, customers, prices or maturity claims the claim gate does not allow.
|
|
70
|
+
- [ ] Norwegian pages are written natively, keep established English technical terms, link to the
|
|
71
|
+
English page with `hreflang`, and are not machine-translated and published unreviewed.
|
|
72
|
+
|
|
73
|
+
## READMEs
|
|
74
|
+
|
|
75
|
+
- [ ] H1 may be the product name ("Cratis <Product>"); first sentence is the definition (15-35 words, no metaphor).
|
|
76
|
+
- [ ] Then install, a minimal working example, a link to `https://www.cratis.io/<product>/`,
|
|
77
|
+
license and how it relates to the rest of Cratis.
|
|
78
|
+
- [ ] GitHub About text equals the README opener (80-160 characters); website is the docs page;
|
|
79
|
+
topics include `cratis` plus category terms.
|
|
80
|
+
|
|
81
|
+
## Before publishing a site change
|
|
82
|
+
|
|
83
|
+
- [ ] Title, description, canonical, Open Graph and JSON-LD present and unique on changed pages.
|
|
84
|
+
- [ ] Sitemap lists new pages; `robots.txt` unchanged unless intended.
|
|
85
|
+
- [ ] JSON-LD validates; no `HowTo`; no new `FAQPage`.
|
|
86
|
+
- [ ] The content is in the raw HTML the server returns, not only rendered by JavaScript.
|
|
87
|
+
- [ ] Internal links resolve; no apex `cratis.io` links.
|
|
88
|
+
- [ ] A real HTTP 404 for unknown URLs; redirect stubs keep a canonical and stay out of the sitemap.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Structured data patterns
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Rules
|
|
6
|
+
- Types per page type
|
|
7
|
+
- Pattern
|
|
8
|
+
|
|
9
|
+
## Rules
|
|
10
|
+
|
|
11
|
+
- One `@graph` per page with stable `@id` values. The organization `@id` is
|
|
12
|
+
`https://cratis.no/#organization`.
|
|
13
|
+
- Markup describes what is visible on the page, and every date is a real one.
|
|
14
|
+
- Add no `HowTo`. Add no new `FAQPage`; keep the visible question and answer text. Use `QAPage` only
|
|
15
|
+
for real user questions and answers.
|
|
16
|
+
- Do not name people (`Person`, `author` name) in structured data unless the owner has approved it.
|
|
17
|
+
Reference the organization by `@id`.
|
|
18
|
+
- Do not add `offers`, an address or contact data without approval (see `SITE.md`).
|
|
19
|
+
- Validate with the schema.org validator or Google's Rich Results Test. Invalid markup is worse than
|
|
20
|
+
none. Static sites hand-write `<script type="application/ld+json">`; Astro sites generate it in
|
|
21
|
+
the head component, so change it there rather than per page.
|
|
22
|
+
|
|
23
|
+
## Types per page type
|
|
24
|
+
|
|
25
|
+
| Page type | Types |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| Company home | `Organization` (name, url, logo, `sameAs`), `WebSite` |
|
|
28
|
+
| Product marketing page | `SoftwareApplication` (name, applicationCategory, license, url, `sameAs`) |
|
|
29
|
+
| Docs concept, tutorial, how-to, reference | `TechArticle` with `headline`, `dateModified`, plus `BreadcrumbList` |
|
|
30
|
+
| Docs hub and landing page | `CollectionPage` or `WebPage`, not `TechArticle` |
|
|
31
|
+
| Blog post | `BlogPosting` (`datePublished`, `dateModified`, `publisher`, `image`) |
|
|
32
|
+
| Repository or sample code | `SoftwareSourceCode` |
|
|
33
|
+
| Legal pages | `WebPage` |
|
|
34
|
+
| 404 and app hosts | none |
|
|
35
|
+
|
|
36
|
+
## Pattern
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"@context": "https://schema.org",
|
|
41
|
+
"@graph": [
|
|
42
|
+
{
|
|
43
|
+
"@type": "TechArticle",
|
|
44
|
+
"@id": "https://www.cratis.io/chronicle/projections/#article",
|
|
45
|
+
"headline": "Chronicle projections",
|
|
46
|
+
"description": "A projection declares how Chronicle builds a read model from events.",
|
|
47
|
+
"dateModified": "YYYY-MM-DD",
|
|
48
|
+
"publisher": { "@id": "https://cratis.no/#organization" },
|
|
49
|
+
"mainEntityOfPage": "https://www.cratis.io/chronicle/projections/"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"@type": "BreadcrumbList",
|
|
53
|
+
"itemListElement": [
|
|
54
|
+
{ "@type": "ListItem", "position": 1, "name": "Chronicle", "item": "https://www.cratis.io/chronicle/" },
|
|
55
|
+
{ "@type": "ListItem", "position": 2, "name": "Projections" }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The values are illustrative: take the title, description and dates from the page itself.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
//
|
|
4
|
+
// Checks a built static site (Astro dist/ or a plain-HTML repo root) before it is published:
|
|
5
|
+
// every indexable page has <html lang>, one <title>, a meta description, a canonical on the
|
|
6
|
+
// expected host and parseable JSON-LD; the site has robots.txt, a sitemap and (optionally) a
|
|
7
|
+
// well-formed llms.txt. Node standard library only.
|
|
8
|
+
//
|
|
9
|
+
// Usage: node verify-discoverability.mjs --root dist --site https://blog.cratis.io [--require-llms] [--warn-only]
|
|
10
|
+
// Exit codes: 0 clean or warnings only, 1 errors, 2 could not run.
|
|
11
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
12
|
+
import { join, relative } from 'node:path';
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const option = (name, fallback) => { const i = args.indexOf(name); return i < 0 ? fallback : args[i + 1]; };
|
|
16
|
+
const root = option('--root', 'dist');
|
|
17
|
+
const site = option('--site');
|
|
18
|
+
const requireLlms = args.includes('--require-llms');
|
|
19
|
+
const warnOnly = args.includes('--warn-only');
|
|
20
|
+
const ignore = (option('--ignore', 'node_modules,.git,.cratis,.github,404.html,_astro,storybook') ?? '').split(',');
|
|
21
|
+
|
|
22
|
+
if (!site || !existsSync(root)) {
|
|
23
|
+
console.error(`could not run: need --site and an existing --root (got ${root})`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
const host = new URL(site).host;
|
|
27
|
+
let errors = 0;
|
|
28
|
+
let warnings = 0;
|
|
29
|
+
const report = (level, file, message) => {
|
|
30
|
+
if (level === 'error' && !warnOnly) errors++; else warnings++;
|
|
31
|
+
console.log(`::${level === 'error' && !warnOnly ? 'error' : 'warning'} file=${file}::${message}`);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function* htmlFiles(dir) {
|
|
35
|
+
for (const name of readdirSync(dir)) {
|
|
36
|
+
if (ignore.includes(name)) continue;
|
|
37
|
+
const path = join(dir, name);
|
|
38
|
+
if (statSync(path).isDirectory()) yield* htmlFiles(path);
|
|
39
|
+
else if (name.endsWith('.html')) yield path;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const attr = (tag, name) => tag.match(new RegExp(`${name}\\s*=\\s*["']([^"']*)["']`, 'i'))?.[1];
|
|
44
|
+
const tags = (html, pattern) => [...html.matchAll(pattern)].map(match => match[0]);
|
|
45
|
+
const titles = new Map();
|
|
46
|
+
|
|
47
|
+
for (const path of htmlFiles(root)) {
|
|
48
|
+
const file = relative(process.cwd(), path);
|
|
49
|
+
const html = readFileSync(path, 'utf8');
|
|
50
|
+
const metas = tags(html, /<meta\b[^>]*>/gi);
|
|
51
|
+
const robots = metas.find(tag => /name\s*=\s*["']robots["']/i.test(tag));
|
|
52
|
+
if (robots && /noindex/i.test(attr(robots, 'content') ?? '')) continue; // deliberately not indexed
|
|
53
|
+
if (/http-equiv\s*=\s*["']refresh["']/i.test(html)) continue; // redirect stub
|
|
54
|
+
|
|
55
|
+
if (!/<html\b[^>]*\blang\s*=/i.test(html)) report('error', file, 'missing <html lang>');
|
|
56
|
+
const title = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1]?.trim();
|
|
57
|
+
if (!title) report('error', file, 'missing or empty <title>');
|
|
58
|
+
else {
|
|
59
|
+
if (title.length > 65) report('warning', file, `title is ${title.length} characters; keep the distinguishing words in the first 60`);
|
|
60
|
+
titles.set(title, [...(titles.get(title) ?? []), file]);
|
|
61
|
+
}
|
|
62
|
+
const description = metas.find(tag => /name\s*=\s*["']description["']/i.test(tag));
|
|
63
|
+
const descriptionText = description ? attr(description, 'content') ?? '' : '';
|
|
64
|
+
if (!descriptionText) report('error', file, 'missing meta description');
|
|
65
|
+
else if (descriptionText.length < 60 || descriptionText.length > 160) report('warning', file, `meta description is ${descriptionText.length} characters (aim for 60-160)`);
|
|
66
|
+
const canonical = tags(html, /<link\b[^>]*>/gi).find(tag => /rel\s*=\s*["']canonical["']/i.test(tag));
|
|
67
|
+
const href = canonical ? attr(canonical, 'href') : undefined;
|
|
68
|
+
if (!href) report('error', file, 'missing <link rel="canonical">');
|
|
69
|
+
else {
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(href);
|
|
72
|
+
if (url.host !== host) report('error', file, `canonical points at ${url.host}, expected ${host}`);
|
|
73
|
+
} catch {
|
|
74
|
+
report('error', file, `canonical is not an absolute URL: ${href}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if ((html.match(/<h1\b/gi) ?? []).length !== 1) report('warning', file, 'expected exactly one <h1>');
|
|
78
|
+
for (const block of html.matchAll(/<script\b[^>]*type\s*=\s*["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi)) {
|
|
79
|
+
try {
|
|
80
|
+
const data = JSON.parse(block[1]);
|
|
81
|
+
const nodes = Array.isArray(data) ? data : data['@graph'] ?? [data];
|
|
82
|
+
if (!Array.isArray(data) && !String(data['@context'] ?? '').includes('schema.org')) report('error', file, 'JSON-LD without a schema.org @context');
|
|
83
|
+
for (const node of nodes) if (!node['@type']) report('warning', file, 'JSON-LD node without @type');
|
|
84
|
+
} catch (error) {
|
|
85
|
+
report('error', file, `JSON-LD does not parse: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const [title, files] of titles) if (files.length > 1) report('warning', files[0], `title "${title}" is shared by ${files.length} pages`);
|
|
91
|
+
|
|
92
|
+
const robotsPath = join(root, 'robots.txt');
|
|
93
|
+
if (!existsSync(robotsPath)) report('error', root, 'no robots.txt');
|
|
94
|
+
else if (!/^\s*sitemap\s*:/im.test(readFileSync(robotsPath, 'utf8'))) report('warning', robotsPath, 'robots.txt does not declare a Sitemap');
|
|
95
|
+
if (!readdirSync(root).some(name => /^sitemap.*\.xml$/.test(name))) report('error', root, 'no sitemap*.xml at the site root');
|
|
96
|
+
|
|
97
|
+
const llmsPath = join(root, 'llms.txt');
|
|
98
|
+
if (existsSync(llmsPath)) {
|
|
99
|
+
// Same structure rules as vendor/claude-seo/scripts/agentic_check.py evaluate_llms_txt (Lighthouse llms-txt audit).
|
|
100
|
+
const llms = readFileSync(llmsPath, 'utf8').replace(/^\uFEFF/, '');
|
|
101
|
+
if (!/^\s*#\s+.+/m.test(llms)) report('error', llmsPath, 'llms.txt has no H1');
|
|
102
|
+
if (!/\[.+\]\(.+\)/.test(llms)) report('error', llmsPath, 'llms.txt has no Markdown links');
|
|
103
|
+
if (!/^\s*>\s+\S/m.test(llms)) report('warning', llmsPath, "llms.txt has no '>' summary");
|
|
104
|
+
} else if (requireLlms) report('error', root, 'no llms.txt');
|
|
105
|
+
|
|
106
|
+
console.log(`verify-discoverability: ${errors} error(s), ${warnings} warning(s)`);
|
|
107
|
+
process.exit(errors ? 1 : 0);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "cratis-discoverability",
|
|
3
|
+
"input": "Run the pre-publish discoverability check on a new blog post, a package README and the package metadata for a Cratis library.",
|
|
4
|
+
"fixtures": [],
|
|
5
|
+
"assertions": [
|
|
6
|
+
{ "kind": "skill-contains", "value": "If a fix makes the page worse to read," },
|
|
7
|
+
{ "kind": "skill-contains", "value": "Add no `HowTo` and" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "Do not name people in structured data." },
|
|
9
|
+
{ "kind": "skill-contains", "value": "Apex `https://cratis.io/` links: the docs host is `https://www.cratis.io`." },
|
|
10
|
+
{ "kind": "skill-contains", "value": "no logo-only or metaphor opener" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "Padding to a word count: there is no minimum length" }
|
|
12
|
+
]
|
|
13
|
+
}
|
package/package/corpus/skills/cratis-screenplay-command-surface/references/text-and-match-rules.md
CHANGED
|
@@ -63,6 +63,23 @@ settings. Syntax remains `matches "^INV-[0-9]{6}$"` or `matches email`.
|
|
|
63
63
|
|
|
64
64
|
`\s` includes U+00A0, U+FEFF and Unicode line/paragraph separators; it excludes
|
|
65
65
|
U+0085. Dot excludes LF, CR, U+2028 and U+2029. `\w` excludes U+0130.
|
|
66
|
+
### Write unambiguous patterns (PLAY0803)
|
|
67
|
+
|
|
68
|
+
Requires Screenplay 4.218.2 or later (`screenplay --version`); older compilers do not warn.
|
|
69
|
+
|
|
70
|
+
Generated clients run the pattern in a browser's backtracking engine, so an exponentially
|
|
71
|
+
ambiguous pattern can hang a tab on a crafted value even though Screenplay's own automaton
|
|
72
|
+
stays linear. The warning `PLAY0803` (`AmbiguousMatchPattern`) names the ambiguous
|
|
73
|
+
subexpression and a simpler rewrite where one exists; C#, TypeScript, Monaco and VS Code
|
|
74
|
+
report it identically. Very large patterns are not analyzed, so keep them small anyway.
|
|
75
|
+
|
|
76
|
+
| Ambiguous | Write instead |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `(a+)+$`, `(a*)*` | `a+`, `a*` (never quantify a group whose body already repeats) |
|
|
79
|
+
| `(a\|a)*` | `a*` (alternatives must not match the same text) |
|
|
80
|
+
|
|
81
|
+
Prefer a character class such as `[ab]*` over alternatives that overlap. Fix the pattern rather than suppressing the warning; validate with `--warnings-as-errors`.
|
|
82
|
+
|
|
66
83
|
`matches email` is conservative, not RFC 5322: one `@`, nonempty local part without
|
|
67
84
|
whitespace/`@`, and at least two nonempty domain parts without whitespace/`@`.
|
|
68
85
|
A no-break space therefore fails email validation. Other bare names report PLAY0366.
|
package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md
CHANGED
|
@@ -278,7 +278,7 @@ See read-surface `references/named-keys.md` for the collection-key exclusion alg
|
|
|
278
278
|
| Protected command reads (ESM v25) | Root `command reads protected`; eligible read paths in requirements and production mappings, absence Validation, concurrent matching facts Conflict; Stage admits v25 and, in a new enough bundle, renders protected reads as Arc read protection, refusing unpreservable shapes and every older bundle with STAGE-ESM-044; semantic Host/executor execute admitted reads with non-Error protected requirements refused STAGE-ESM-045 |
|
|
279
279
|
| Chronological command dates (ESM v26) | Same-primitive Date/DateTime `==`, `!=`, `>`, `>=`, `<`, `<=` in require/produces-when (property or literal RHS) and command/property rules (literal RHS only). DateTime uses UTC ticks, participating offset-less inputs reject Validation before rules; then matching stays ordinal. Requires Screenplay 4.199.0 (`screenplay --version`); older binders refuse PLAY0268. The Stage Host and executor run v26 (older Stage refuses STAGE-ESM-016); the renderer and legacy engines refuse STAGE-ESM-046 (toolchain `language-admission.md`). Read command-surface `references/date-comparisons.md`. |
|
|
280
280
|
| Current routing and actor admission | Sources/streams and command/specification routes bind and reference-execute as v8; reaction `runs as system role` supplies the invocation actor as v10. Whole-workspace binding determines executable availability; CLI/Stage realization remains separate |
|
|
281
|
-
| Blocks binding (`PLAY0268`) | enum/identity compliance and events combining personal or explicitly subject-scoped secret values with explicit subject marks; a compiler that stops before v13 refuses all `pii`/`secret` concepts ("compliance attributes require portable data-subject semantics"), while v13 binds them and Stage renders protection; bare `rule <Name>`, read paths without protected opt-in or in unsupported positions such as `produces when`, date comparisons with mixed primitives, optional condition operands, `$context`, protected read paths or `today`, date rules on concepts or reaction `where`, `$context.tenant`/claims/roles/causation in `produces`, `$env` conditions, `file` constraints, unquoted `import` of an external contract, a read model with neither explicit `key` nor unambiguous query-based identity as described above (several key marks bind as v27; older binders refuse PLAY0268; toolchain `language-admission.md`), queries with context `from` sources (typed query context is unadmitted), single-result filters (collections only), `scoped to` (opaque scope)
|
|
281
|
+
| Blocks binding (`PLAY0268`) | enum/identity compliance and events combining personal or explicitly subject-scoped secret values with explicit subject marks; a compiler that stops before v13 refuses all `pii`/`secret` concepts ("compliance attributes require portable data-subject semantics"), while v13 binds them and Stage renders protection; bare `rule <Name>`, read paths without protected opt-in or in unsupported positions such as `produces when`, date comparisons with mixed primitives, optional condition operands, `$context`, protected read paths or `today`, date rules on concepts or reaction `where`, `$context.tenant`/claims/roles/causation in `produces`, `$env` conditions, `file` constraints, unquoted `import` of an external contract, a read model with neither explicit `key` nor unambiguous query-based identity as described above (several key marks bind as v27; older binders refuse PLAY0268; toolchain `language-admission.md`), queries with context `from` sources (typed query context is unadmitted), single-result filters (collections only), `scoped to` (opaque scope) or a `by` block on a list query or non-optional single result (a `performer` query is not refused: it is an opaque read, PLAY0270, left out of the executable model; [version gate](../../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)); optional keyed and collection queries, including caller-supplied equality filters and observable delivery, bind, `$causedBy` and templates in projections |
|
|
282
282
|
| Blocks binding (`PLAY0800`, permanent attachment requirement) | Attached file/inline command `handler`: gap-fill the slice with a constrained implementation attachment, not by waiting for ESM admission. PLAY0035 beside productions; pending described intent instead binds v23. Read required typed `arguments.subject` for the command, never parse the message. Compiler prerequisite and older PLAY0268 attribution: [diagnostic arguments](../../cratis-screenplay-toolchain/references/diagnostic-arguments.md) |
|
|
283
283
|
| Blocks binding (`PLAY0271`, legacy meaning) | Command reads without protected opt-in and `concurrency`; combined protected reads/concurrency is PLAY0783 |
|
|
284
284
|
| Deferred (`PLAY0269`, information) | Screens, layouts, templates, forms, contributions, UI profiles, themes, behaviors and `on`/`uses` |
|
|
@@ -451,7 +451,14 @@ correctness repair; PLAY0667–0670 require an author decision, with no automati
|
|
|
451
451
|
|
|
452
452
|
Sources/streams and command/specification routes bind as ESM v8 and have
|
|
453
453
|
catalog identities; `executionAvailable` still requires whole-workspace binding.
|
|
454
|
-
Source/stream `propose-rename` repairs bound route references.
|
|
454
|
+
Source/stream `propose-rename` repairs bound route references. `propose-rename` also
|
|
455
|
+
renames a composite stream-id part: it rewrites the part declaration and every
|
|
456
|
+
`part = ...` mapping in command routes, specification routes, examples and redelivery
|
|
457
|
+
locators, keeps part order and stored stream ids, and is refused
|
|
458
|
+
when the new name collides with another part of the same stream. Use it instead of a
|
|
459
|
+
hand-written `propose-ast` batch.
|
|
460
|
+
Requires Screenplay 4.220.0 (`screenplay --version`) for part renames; older servers
|
|
461
|
+
cannot rename a part through `propose-rename`. Operations and
|
|
455
462
|
exact numeric mode remain unadmitted. Check installed schemas, especially when
|
|
456
463
|
the CLI bundle predates the standalone tool.
|
|
457
464
|
|
|
@@ -626,7 +633,12 @@ Upgrade before treating those rows as a coverage gate.
|
|
|
626
633
|
`find-modeling-smells` asks information-level questions, never changes compilation
|
|
627
634
|
or `--warnaserror`: SMELL001 generic event endings, SMELL002 generic command verbs,
|
|
628
635
|
SMELL003 duplicate nonempty event shapes from different commands, SMELL004 event
|
|
629
|
-
fan-out, SMELL005 read-model property fan-in
|
|
636
|
+
fan-out, SMELL005 read-model property fan-in (counts only the events that actually
|
|
637
|
+
feed the property, not every known event through a projection `all` block), SMELL006
|
|
638
|
+
an event property that no mapping, AutoMap, reaction, condition or specification
|
|
639
|
+
reads (worded as a question: dead data or a missing reader?).
|
|
640
|
+
Requires Screenplay 4.221.0 (`screenplay --version`) for SMELL006 and the corrected
|
|
641
|
+
SMELL005 count; older servers over-report SMELL005 and never report SMELL006. Optional scope/document and
|
|
630
642
|
`eventFanOutThreshold`/`propertyFanInThreshold` (default 5, integers 1–200) bound
|
|
631
643
|
selection; equal to threshold is not flagged. Similar shape does not establish
|
|
632
644
|
similar meaning: report a defect only with a domain consequence. No per-declaration
|
|
@@ -53,8 +53,8 @@ model's behalf.
|
|
|
53
53
|
|
|
54
54
|
3. **Element sweep** (phase 1, mechanical, before any judgement). One line per gated element, event,
|
|
55
55
|
name, command, event property and slice; `references/checklist.md` S1-S7 gives each line's
|
|
56
|
-
format. Scoped `find-modeling-smells` supplies advisory generic-name, duplicate-shape
|
|
57
|
-
|
|
56
|
+
format. Scoped `find-modeling-smells` supplies advisory generic-name, duplicate-shape,
|
|
57
|
+
wide-flow and unread-event-property questions, never compiler defects or a substitute for the sweep. Each `NONE` and each form name is its own finding naming that element. A repeated
|
|
58
58
|
payload or generic verb is a signal to investigate: report it only when the name or payload
|
|
59
59
|
hides the business reason or a consequence is shown (distinct facts that share a shape and
|
|
60
60
|
earlier generations are not findings). A description, comment or spec name never excuses a flagged line.
|
|
@@ -52,8 +52,8 @@ not why: a signal, so confirm the hidden reason before reporting. The fix names
|
|
|
52
52
|
change (`BerthReassigned`, `BerthLengthLimitRaised`), one event per reason, each with its own
|
|
53
53
|
rules and consumers. A shared payload shape alone is not a defect: full generations repeat the
|
|
54
54
|
prior shape on purpose and different facts may look alike. Screenplay 4.127.0 MCP
|
|
55
|
-
`find-modeling-smells` reports advisory questions (SMELL001–SMELL005) for generic
|
|
56
|
-
names, duplicate shapes and
|
|
55
|
+
`find-modeling-smells` reports advisory questions (SMELL001–SMELL005; SMELL006 needs a newer server, see [MCP tools](../../cratis-screenplay-model-authoring/references/mcp-tools.md)) for generic
|
|
56
|
+
names, duplicate shapes, wide flows and event properties nothing reads. It supplements the sweep, never changes
|
|
57
57
|
a compiler verdict or proves a domain defect.
|
|
58
58
|
|
|
59
59
|
## Phase 2. Entity walk (5 checks, T1-T5; feeds F1-F3 and scenario coverage)
|
|
@@ -180,7 +180,7 @@ Nebulit (`provenance.md`).
|
|
|
180
180
|
| Id | Check | Evidence | Tier |
|
|
181
181
|
|---|---|---|---|
|
|
182
182
|
| H1 | Query shape follows the business view (list where people scan; lookup where they open one) | queries, screens | D |
|
|
183
|
-
| H2 | Executable scope: a keyed `=> RM optional` query with `by` named like a read-model property, or a collection (`=> RM[]`, observable too) with caller-supplied `by` and/or equality filters. `from`, single-result filters, opaque `scoped to
|
|
183
|
+
| H2 | Executable scope: a keyed `=> RM optional` query with `by` named like a read-model property, or a collection (`=> RM[]`, observable too) with caller-supplied `by` and/or equality filters. `from`, single-result filters, opaque `scoped to` and a list-query `by` block remain PLAY0268; a `performer` query is an opaque read (PLAY0270) left out of the executable model ([version gate](../../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)). A read model with several key parts binds as v27 (Requires Screenplay 4.204.0). The projection routes by the same identity (a `from` key or an event property, or `$eventSourceId`; `$eventSourceId` is not mandated). Filtered models: supported v14 equality filters render within the CLI bundle's ESM ceiling. Renderable scope only: Stage-specific identity/filter limits; keyed optional snapshot lookups and snapshot/observable collections render (Requires Stage 4.90.0 for snapshot lists; older renderers report `STAGE-ESM-010`). Check query concept-validation parity separately; primitive-argument targets answer absence instead of Validation | queries, projections | C (executable); C (render admission, renderable only) |
|
|
184
184
|
| H3 | One builder per read model; projection `key` only on `from` | projections | C |
|
|
185
185
|
| H4 | Screens show one actor's point of view; a form submits one command | screens, forms | D |
|
|
186
186
|
|
package/package/corpus/skills/cratis-screenplay-model-review/references/model-inventory-report.md
CHANGED
|
@@ -32,7 +32,8 @@ the Screenplay MCP views.
|
|
|
32
32
|
for obligations with no matching specification. These read authored examples, not executed
|
|
33
33
|
coverage (`../../cratis-screenplay-model-authoring/references/mcp-tools.md`).
|
|
34
34
|
5. Gaps and orphans: `find-references` and `dependencies` for declarations referenced by nothing;
|
|
35
|
-
`find-modeling-smells` for the candidate shapes
|
|
35
|
+
`find-modeling-smells` for the candidate shapes; SMELL006 names event properties no
|
|
36
|
+
mapping, reaction, condition or specification reads, a question to answer, not a defect.
|
|
36
37
|
|
|
37
38
|
## Report
|
|
38
39
|
Asked conversationally, give the summary and the most important gap and offer the full lists on request.
|
|
@@ -9,7 +9,7 @@ reference execution.
|
|
|
9
9
|
|---|---|
|
|
10
10
|
| The MCP tool catalog (`describe-application`, `search-declarations`, `dependency-graph`, `find-assertion-gaps`, `read-workspace`, `declaration-details`) | `Screenplay`: `Source/DotNET/Screenplay.Mcp/McpToolCatalog.cs`, `Documentation/screenplay/mcp/reference.md` |
|
|
11
11
|
| Diagnostics PLAY0029, PLAY0191, PLAY0268, PLAY0271, PLAY0350, PLAY0381, PLAY0469; one data subject per event | `Screenplay`: `Source/DotNET/Screenplay/Diagnostics/DiagnosticCodes.cs`, the decision record on data subjects |
|
|
12
|
-
| `find-specification-obligations` (SPEC001 to SPEC008, authored presence) and `find-modeling-smells` (SMELL001 to
|
|
12
|
+
| `find-specification-obligations` (SPEC001 to SPEC008, authored presence) and `find-modeling-smells` (SMELL001 to SMELL006, advisory questions); neither proves executed behavior or complete lineage | `Screenplay`: `Documentation/screenplay/mcp/reference.md` |
|
|
13
13
|
| `semantic-diff` source objects and revision-pinned pages | `Screenplay`: `Source/DotNET/Screenplay.Mcp/` (`McpRevisionDiff`, `McpWorkspaces.Comparison`), `Documentation/screenplay/mcp/reference.md` |
|
|
14
14
|
| Model notes: anchors, kinds, the committed sidecar, the open-question gate | `Screenplay`: `Documentation/screenplay/model-notes.md`; notes tools in model-authoring `references/model-notes.md` |
|
|
15
15
|
| Property links and their roles for lineage and journey tracing | `Screenplay`: `Documentation/screenplay/mcp/reference.md`; model-authoring `references/property-links.md` |
|
|
@@ -85,7 +85,7 @@ is not the same thing. Rules and diagnostics:
|
|
|
85
85
|
**Name the compiler and renderer.** Optional keyed queries and collection queries
|
|
86
86
|
bind, including `observable`. Caller-supplied equality
|
|
87
87
|
filters on collections bind as ESM v14; `from`, single-result filters, `scoped to`,
|
|
88
|
-
|
|
88
|
+
and a `by` block on a list query (or on a non-optional single result) remain PLAY0268, each with its reason. A `performer` query is not refused: it is an opaque read (PLAY0270 information) left out of the executable model ([version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)). Read
|
|
89
89
|
[equality filters](references/equality-filters.md) for the admitted shape, a complete
|
|
90
90
|
compiled specification model and the Stage/CLI ceilings. Keep a list the domain needs.
|
|
91
91
|
|
|
@@ -166,7 +166,7 @@ precondition. It takes a `file` reference or an inline block in a tagged fence
|
|
|
166
166
|
(` ```csharp `, ` ```sql `), and is the query's counterpart to a command's
|
|
167
167
|
`handler`. The parser accepts any registered inline language and does not reject
|
|
168
168
|
a nonsensical one, so the choice is yours to get right. Performer code is opaque:
|
|
169
|
-
Screenplay never runs it.
|
|
169
|
+
Screenplay never runs it. It is reported as information `PLAY0270` and left out of the executable model; a specification that reads it is left out while every other specification still runs. Older compilers refuse it with PLAY0268: see the [version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does).
|
|
170
170
|
|
|
171
171
|
A read model a performer composes needs no projection, and no event builds it.
|
|
172
172
|
Specify it from state with `given readmodel`, then `when query <Query>` with the
|
|
@@ -183,7 +183,7 @@ Caller-supplied equality filters on collections select ESM v14 (Requires Screenp
|
|
|
183
183
|
primitive/concept/enum properties, same nominal type ignoring optionality, no
|
|
184
184
|
`from`, distinct names not repeating `by`. `by` and supplied filters AND together;
|
|
185
185
|
optional filters may be omitted, but supplied null is refused. PLAY0268 still
|
|
186
|
-
rejects context sources, single-result filters, opaque scope
|
|
186
|
+
rejects context sources, single-result filters, opaque scope and
|
|
187
187
|
a `by` block on a list query (or on a non-optional single result).
|
|
188
188
|
Authorization runs before lookup, ANDed with module/feature gates; policies see
|
|
189
189
|
only `by`, never filters. Pin denial with `given caller` and `then denied`.
|
|
@@ -39,7 +39,7 @@ Each binding refusal reports PLAY0268 with its query/filter and reason:
|
|
|
39
39
|
| `filter ... from ...` or context-derived `by` | Typed query context sources are not admitted; a caller argument must not stand in for trusted context |
|
|
40
40
|
| Filter on a single-result query | Equality filters are admitted only for collections |
|
|
41
41
|
| `scoped to` | The named scope has no portable executable scope contract |
|
|
42
|
-
| `performer` | Query implementation code is opaque
|
|
42
|
+
| `performer` | Query implementation code is opaque. Requires Screenplay 4.220.1 (`screenplay --version`) for it to be an opaque read (PLAY0270 information) left out of the executable model; older compilers refuse it with PLAY0268 and leave the model unbound |
|
|
43
43
|
| `by` block on a collection query | A composite lookup is admitted only for an optional single-instance query; narrow a list by composite-keyed read-model parts with equality filters instead |
|
|
44
44
|
| Filter on a collection/composite property, missing property or mismatched nominal type | The v14 filter contract requires a same-named scalar primitive, concept or enum property with the same type |
|
|
45
45
|
|
|
@@ -25,7 +25,7 @@ selection is:
|
|
|
25
25
|
(case-sensitive). It first excludes collection-query `by` properties: if exactly
|
|
26
26
|
one candidate remains, that is the identifier; otherwise it uses all `*Id`
|
|
27
27
|
candidates and requires exactly one. An admitted list query here is a collection
|
|
28
|
-
query without context-derived `by`, filters
|
|
28
|
+
query without context-derived `by`, filters or scope.
|
|
29
29
|
|
|
30
30
|
A projection-only/queryless read model needs an explicit `key`: a lone `*Id`
|
|
31
31
|
property alone does not infer identity. Never use the command's `identifier`
|
package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md
CHANGED
|
@@ -35,7 +35,7 @@ ESM). Only the probe answers renderability. `PLAY0270` entries are information,
|
|
|
35
35
|
| Class | Meaning | Typical examples (each verified in `admission.md`) | Action |
|
|
36
36
|
| --- | --- | --- | --- |
|
|
37
37
|
| **Model fix (in mode)** | the model is in, or the user chose, renderable mode and breaks its own subset in a way that has a renderable equivalent with the same meaning | a `produces` whose `for` is not the command identifier; two projections for one read model (`-007`); a policy Stage cannot express portably (`-015`; role-only and opaque pure policies render) | edit request to the modeler session with address, change and rationale |
|
|
38
|
-
| **Capability gap** | the intended meaning needs a construct Stage does not render yet | DateTime equality filters and required parameters after optional filters (`-010`); context/single-result filters, opaque `scoped to
|
|
38
|
+
| **Capability gap** | the intended meaning needs a construct Stage does not render yet | DateTime equality filters and required parameters after optional filters (`-010`); context/single-result filters, opaque `scoped to` and a list-query `by` block remain PLAY0268; a query `performer` (opaque read, PLAY0270) is left out of the model so render emits nothing for it: write the code the model needs by hand as a gap-fill item; a read model with several key parts or a projection keyed by parts (v27) refuses `STAGE-ESM-048` and a single-instance query by parts `STAGE-ESM-047` (Chronicle#4265); Translate, clocks, captures, non-event triggers and opaque reaction bodies (`-024`); claim-reading/opaque `runs as` gates (`-034`), legacy `from` (`-035`), mixed effects in one handler (`-037`, retries can duplicate events; keep it whole), unsupported reaction mappings (`-039`); unsupported generated types/result combinations (`-028`/`-038`); `then no readmodel` (`-027`); evolved events (`STAGE-ESM-026`); unsupported expression/enum production conditions (`-006`; supported conditional productions render); legacy reads (`PLAY0271`), protected reads the renderer cannot preserve, or any protected read in an older CLI bundle (STAGE-ESM-044 despite v25 admission; semantic Host/executor support is separate); code validation (`-005`); protected identities/enums (`-040`) or unadmitted explicit subject lineage (`PLAY0268`); described handlers (v23, STAGE-IMPL-002 even Stub mode), atomic mode (STAGE-ESM-043; v22 admission Requires Stage 4.139.0, older Stage reports -016) | keep the model; fallback record entry; options a to c in `SKILL.md`; link the Stage issue |
|
|
39
39
|
| **Implementation attachment** | a permanent limitation, not future ESM admission | file/inline command handlers (PLAY0800; typed `subject` names the command) | keep the model contract and gap-fill the slice in code with a constrained implementation attachment; do not wait for a new ESM version |
|
|
40
40
|
| **Tool skew** | the bundled compiler disagrees with documented semantics | the false `PLAY0285` on reaction cascades from an older bundle; codes the standalone compiler does not raise | record in the session state and the fallback record; never "fix" a correct model |
|
|
41
41
|
| **Environment** | not the model | restore failures, missing SDK, a destination that is not writable | fix the environment or report blocked |
|
|
@@ -131,7 +131,11 @@ proved and keep other guarantees as target requirements; recovery cases are in
|
|
|
131
131
|
- One behaviour per spec; names read as sentences: `Reserving...`, `Rejecting...`, `Refusing...`
|
|
132
132
|
(denial), `<View>LifecycleN<State>` for families.
|
|
133
133
|
- `for` on every given, appended and expected event (plain `produces <Name>` too; inline `produces event` omits it), and on the `when` command where the
|
|
134
|
-
destination matters. A competing-claim given needs a *different* `for`.
|
|
134
|
+
destination matters. A competing-claim given needs a *different* `for`. A given without `for`
|
|
135
|
+
belongs to the event source the specification acts on; state `for` when the command appends to
|
|
136
|
+
several event sources or an assertion depends on the given's source, and never assert
|
|
137
|
+
`$eventSourceId` of a sourceless given. Read
|
|
138
|
+
[examples and routes](../cratis-screenplay-specifications/references/examples-and-routes.md#givens-without-an-event-source).
|
|
135
139
|
Foreign event fixtures feeding a capture may omit `for`; supplied `for` is
|
|
136
140
|
opaque source text, not the capture's content key.
|
|
137
141
|
- Format rules belong on a `concept`: give each concept rule representative acceptance,
|
|
@@ -147,7 +151,9 @@ proved and keep other guarantees as target requirements; recovery cases are in
|
|
|
147
151
|
- Descriptions are not rendered: a rule only in prose is unenforced; make it a rule layer, a
|
|
148
152
|
spec or a recorded target requirement.
|
|
149
153
|
- Prove projections with events (`given`/`when append`), not `given readmodel`; seed read models
|
|
150
|
-
only for performer-backed views or commands that decide on view state.
|
|
154
|
+
only for performer-backed views or commands that decide on view state. A query `performer` is an opaque read
|
|
155
|
+
(information `PLAY0270`, not `PLAY0268`): it is left out of the executable model, a spec that reads it is
|
|
156
|
+
left out too and the other specs still run, so mark it `gap` for the executable modes ([version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)).
|
|
151
157
|
- No contradiction between a family and single-transition specs of the same view.
|
|
152
158
|
|
|
153
159
|
### Quality checklist (adapted from the TrogonStack scenario checklist; `references/provenance.md`)
|
package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/scenario-examples.md
CHANGED
|
@@ -219,6 +219,6 @@ empty-list intent is a separate claim from a keyed row being absent (`view-and-s
|
|
|
219
219
|
queries are executable too (`ListOpenInvoices` in the backing example); caller-supplied equality filters on collections bind as ESM v14.
|
|
220
220
|
Pass `by` and filters in query arguments; PLAY0468 reports unknown, repeated or
|
|
221
221
|
missing required arguments. Optional filters may be omitted, supplied null is refused.
|
|
222
|
-
Context-sourced/single-result filters, opaque `scoped to
|
|
223
|
-
`by` block remain PLAY0268. Compiled coverage and renderer ceilings:
|
|
222
|
+
Context-sourced/single-result filters, opaque `scoped to`, and a list-query
|
|
223
|
+
`by` block remain PLAY0268; a `performer` query is an opaque read left out of the model (PLAY0270; [version gate](../../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)). Compiled coverage and renderer ceilings:
|
|
224
224
|
read-surface [equality filters](../../cratis-screenplay-read-surface/references/equality-filters.md).
|
package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md
CHANGED
|
@@ -24,7 +24,7 @@ Typed example/override forms: `cratis-screenplay-specifications`
|
|
|
24
24
|
| `then readmodel` / `then query` (keyed `ById`, `arguments`, `result`) | yes | yes | yes |
|
|
25
25
|
| `then query` on an unfiltered list or a list narrowed by one `by` key (observable too) | yes | yes: no `arguments` for an unkeyed list, one `result` per row stating the read-model identifier, no `result` = empty list | snapshot and observable lists render |
|
|
26
26
|
| Collection query with caller-supplied equality `filter`, including observable | yes | yes, ESM v14: same-named scalar property/type, AND with `by`; optional omission allowed, supplied null refused; PLAY0468 checks arguments; row order does not matter | yes on supported snapshot/observable collections; DateTime/filter parameter order refuse -010; independent Stage executor still refuses filtered assertions (-010) |
|
|
27
|
-
| Context `from`, single-result filters, `scoped to
|
|
27
|
+
| Context `from`, single-result filters, `scoped to` or a list-query `by` block | yes | PLAY0268: query context, collection-only filtering, opaque scope or a multipart list lookup are unadmitted | no |
|
|
28
28
|
| `then no readmodel <RM> for "<key>"` | yes | yes | no |
|
|
29
29
|
| `when query` -> `then result` / `then no result` | yes | yes | same as `then query`: only the query-only cases below |
|
|
30
30
|
| `given clock` / `when clock`, `when trigger`, record-source `given`/`when capture` | yes | yes (standalone and any cratis whose compiler admits v6; an older bundle does not, see toolchain `references/executable-subset.md`) | no |
|
|
@@ -45,7 +45,8 @@ Typed example/override forms: `cratis-screenplay-specifications`
|
|
|
45
45
|
| Bodied rules, code policies | spec them | spec reported **unsupported**, never passed | no |
|
|
46
46
|
| Pending described command handlers (v23) | specify intent | binds; denial/validation execute, body-reaching specs Unsupported ImplementationBody; all-Unsupported is not green | STAGE-IMPL-002 even Stub mode |
|
|
47
47
|
| Attached command handlers | spec them in design | blocked: PLAY0800, no spec runs (V3 blocked); permanent constrained-attachment requirement, gap-fill in code | no |
|
|
48
|
-
| `file` constraints
|
|
48
|
+
| `file` constraints | spec them in design | blocked: PLAY0268 at binding, no spec runs (V3 blocked) | no |
|
|
49
|
+
| Query `performer`s | spec them in design | opaque read: PLAY0270 information, left out of the executable model; a spec that reads it is left out, every other spec still runs ([version gate](../../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does); older compilers report PLAY0268) | no |
|
|
49
50
|
| Atomic reaction mode (v22) | declare for new models | failed batch adds no facts or view changes; earlier batches/commands remain; `then error` alone is insufficient | v22 STAGE-ESM-016; mode in admitted v23–v27 STAGE-ESM-043 |
|
|
50
51
|
| Protected command reads (v25) | declare opt-in for eligible reads | absence Validation; matching intervening facts Conflict; race vectors are not a new spec construct | Cratis C# rendering in a new enough bundle (STAGE-ESM-044 before, or for unpreservable shapes); semantic Host/executor execute admitted reads, non-Error protected requirements STAGE-ESM-045; v25 pair admitted |
|
|
51
52
|
|
|
@@ -58,7 +59,7 @@ Requires Stage 4.90.0 for snapshot/live lists, 4.101.0 for no-event specificatio
|
|
|
58
59
|
|
|
59
60
|
## Design mode
|
|
60
61
|
Write the full intended behaviour, including scenarios the executable subset cannot run
|
|
61
|
-
(filtered, scoped and performer-backed views; clock, trigger and capture scenarios when only a
|
|
62
|
+
(filtered, scoped and performer-backed views, the last left out of the executable model rather than refused; clock, trigger and capture scenarios when only a
|
|
62
63
|
older cratis bundle is available). Mark each such spec in the coverage matrix as
|
|
63
64
|
`gap: <reason>` for the narrower modes so the verification and render phases know what will not execute.
|
|
64
65
|
|
package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/view-and-story-specs.md
CHANGED
|
@@ -61,7 +61,7 @@ own full givens. The excerpt is copied from `invoicing-dues-example.md`; the las
|
|
|
61
61
|
both that the stored row is gone and that the keyed query returns nothing. The whole-list query
|
|
62
62
|
(no key) is executable too (Requires Screenplay 4.105.0 or later): `invoicing-dues-example.md` adds
|
|
63
63
|
`ListOpenInvoices => OpenInvoice[]` with one spec that expects one `result` row and one that
|
|
64
|
-
expects the empty list after `PaymentReceived`. Only `filter`, `scoped to` and `performer` queries stay design-mode.
|
|
64
|
+
expects the empty list after `PaymentReceived`. Only `filter`, `scoped to` and `performer` queries stay design-mode (a performer query is left out of the executable model, not refused).
|
|
65
65
|
The source document also asserts the other terminal outcomes (`InvoiceVoided`, `InvoiceSettled`) the same way; they are not copied here.
|
|
66
66
|
```screenplay excerpt parent=references/invoicing-dues-example.md#1
|
|
67
67
|
specification OpenInvoicesLifecycle1Empty
|
|
@@ -120,7 +120,7 @@ Ask the batch below only for what the request leaves open: (1) origin of each ac
|
|
|
120
120
|
|
|
121
121
|
- At most one `identifier` per command; never on an event property or read-model property (use `key`). At most one builder per read model (`PLAY0191`). Read `references/read-model-design.md` ("Identifier and key contract") for the exact rules.
|
|
122
122
|
|
|
123
|
-
- A query's `by` goes on its own body line; `from`, single-result filters, scope
|
|
123
|
+
- A query's `by` goes on its own body line; `from`, single-result filters, scope and a list-query `by` block remain `PLAY0268` (a `performer` query is an opaque read left out of the executable model, [version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)); equality filters on collections bind as ESM v14. Read `references/read-model-design.md` ("Query binding") for the binding rules.
|
|
124
124
|
|
|
125
125
|
- A `unique` constraint names a property the event declares directly; the constraint name is
|
|
126
126
|
its identity (renaming starts an empty index).
|
package/package/corpus/skills/cratis-screenplay-slice-design/references/read-model-design.md
CHANGED
|
@@ -330,8 +330,9 @@ module Berths
|
|
|
330
330
|
- A query's `by` goes on its own body line. Optional keyed and collection queries
|
|
331
331
|
bind, including observable delivery. Caller-supplied equality filters on collections
|
|
332
332
|
bind as ESM v14; `by` ANDs with supplied filters. `from`, single-result
|
|
333
|
-
filters, scope
|
|
334
|
-
respective context/cardinality/opaque/multipart gaps. A
|
|
333
|
+
filters, scope and a `by` block on a list query remain PLAY0268 for their
|
|
334
|
+
respective context/cardinality/opaque/multipart gaps. A `performer` query is an opaque read left
|
|
335
|
+
out of the executable model, not PLAY0268 ([version gate](../../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)). A composite-keyed optional
|
|
335
336
|
single-instance query takes a `by` block naming every part (ESM v27). Read read-surface
|
|
336
337
|
[equality filters](../../cratis-screenplay-read-surface/references/equality-filters.md).
|
|
337
338
|
|
|
@@ -238,7 +238,10 @@ For command specifications:
|
|
|
238
238
|
- [ ] `given caller` is present whenever the command is authorized, with a
|
|
239
239
|
`then denied` case for a caller who must be refused.
|
|
240
240
|
- [ ] `then` contains **either** events **or** an error, never both.
|
|
241
|
-
- [ ] A collision between two event sources uses `for` on the `given` event.
|
|
241
|
+
- [ ] A collision between two event sources uses `for` on the `given` event. A `given` without
|
|
242
|
+
`for` belongs to the event source the specification acts on; state `for` when the command
|
|
243
|
+
appends to several event sources or an assertion depends on it (see
|
|
244
|
+
[examples and routes](references/examples-and-routes.md#givens-without-an-event-source)).
|
|
242
245
|
- [ ] Error cases test business rules; each concept rule has one rejection
|
|
243
246
|
specification through one command, not one per use.
|
|
244
247
|
- [ ] A localized rejection is pinned by its quoted `$strings` key.
|
|
@@ -248,7 +251,8 @@ For command specifications:
|
|
|
248
251
|
For view specifications:
|
|
249
252
|
|
|
250
253
|
- [ ] A view a `performer` composes is specified with `given readmodel`,
|
|
251
|
-
`when query` and `then result` or `then no result
|
|
254
|
+
`when query` and `then result` or `then no result`; the reference runner leaves such a
|
|
255
|
+
spec out (`PLAY0270` information), so it is design intent, not executed proof.
|
|
252
256
|
- [ ] `given readmodel` is a complete instance with its identifier.
|
|
253
257
|
- [ ] `then readmodel` states the identifier and the properties that matter; add
|
|
254
258
|
`exactly` only when extra properties must fail the assertion.
|
package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- Event routes
|
|
12
12
|
- Redelivery locators
|
|
13
13
|
- Operation and recovery intent
|
|
14
|
+
- Givens without an event source
|
|
14
15
|
|
|
15
16
|
## Typed examples
|
|
16
17
|
|
|
@@ -223,3 +224,21 @@ future external-work behavior. Use `then no events` to assert explicitly that th
|
|
|
223
224
|
action and cascade produced no new facts; after append it excludes the input fact.
|
|
224
225
|
It may accompany view/query/response assertions, not events, event-order assertions,
|
|
225
226
|
errors or denial (PLAY0545). An omitted event list is not this explicit assertion.
|
|
227
|
+
|
|
228
|
+
## Givens without an event source
|
|
229
|
+
|
|
230
|
+
A `given` event with no `for` belongs to the event source the specification acts on: the
|
|
231
|
+
event source each fact the command appends goes to, or the `for` of `when append`.
|
|
232
|
+
|
|
233
|
+
- A unique-event constraint counts a sourceless given as already on that event source. A
|
|
234
|
+
unique-value constraint treats its value as the action's own claim.
|
|
235
|
+
- When a projection needs the given's event-source identity, the reference evaluator reports
|
|
236
|
+
`Unsupported` (Projection) and does not invent one. This holds whatever the action,
|
|
237
|
+
because givens are projected before it runs.
|
|
238
|
+
- Runners that append to real event streams, such as Stage, append sourceless givens to the
|
|
239
|
+
action's event source.
|
|
240
|
+
- State `for` when the command appends to more than one event source, or when an assertion
|
|
241
|
+
depends on the given's event source. Never assert `$eventSourceId` of a sourceless given.
|
|
242
|
+
|
|
243
|
+
Requires Screenplay 4.229.0 (`screenplay --version`) for the documented, vector-pinned
|
|
244
|
+
placement; older versions leave a sourceless given's placement unspecified.
|
|
@@ -13,4 +13,8 @@ Rules for stream-id literals, composite ids and the reserved `All` stream name.
|
|
|
13
13
|
PLAY0273 as a backstop. Streams `all`, `ALL` and `Default`
|
|
14
14
|
remain valid. Rename via `propose-rename`; a pin retaining `All` cannot fix it.
|
|
15
15
|
Existing `All` history reads as source-only appends, not the renamed stream.
|
|
16
|
+
Rename a composite part with `propose-rename` too: it rewrites the part and every
|
|
17
|
+
`part = ...` mapping together and refuses a name that collides with another part;
|
|
18
|
+
never hand-write a `propose-ast` batch for it (needs a server that supports part
|
|
19
|
+
renames, see model-authoring `references/mcp-tools.md`).
|
|
16
20
|
Source evidence and migration limits are in that reference.
|
|
@@ -103,7 +103,7 @@ not the source identity.
|
|
|
103
103
|
- **Executable** (binds, V3): `references/executable-subset.md`. Optional keyed lookups, unfiltered
|
|
104
104
|
lists and lists narrowed by one `by` key (observable too) bind; caller-supplied equality
|
|
105
105
|
filters on collections bind as ESM v14. Context `from`, single-result
|
|
106
|
-
filters, opaque `scoped to
|
|
106
|
+
filters, opaque `scoped to` and a `by` block on a list query remain PLAY0268; a `performer` query is an opaque read left out of the executable model (PLAY0270; [version gate](references/executable-subset.md#code-attachments-what-binds-what-never-does)). Read
|
|
107
107
|
read-surface [equality filters](../cratis-screenplay-read-surface/references/equality-filters.md).
|
|
108
108
|
Pending handlers beside unconditional productions bind as described commands (v23),
|
|
109
109
|
but reached bodies are Unsupported; attached file/inline handlers refuse PLAY0800
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- Explicit enum member numbers
|
|
9
9
|
- Reserved stored names and event pins
|
|
10
10
|
- Named keys, production routes and observer filters
|
|
11
|
+
- Ambiguous match patterns (PLAY0803)
|
|
11
12
|
- Projection event scope (PLAY0801)
|
|
12
13
|
- Reducer instance identity (PLAY0690)
|
|
13
14
|
- Caller identity, compliance and routes
|
|
@@ -149,6 +150,14 @@ Overrides/filters select released ESM v10; malformed routes also use PLAY0504.
|
|
|
149
150
|
Read read-surface `references/named-keys.md` and
|
|
150
151
|
[production routes and filters](production-routes-and-filters.md).
|
|
151
152
|
|
|
153
|
+
## Ambiguous match patterns (PLAY0803)
|
|
154
|
+
|
|
155
|
+
Requires Screenplay 4.218.2 (`screenplay --version`); older compilers do not report it.
|
|
156
|
+
|
|
157
|
+
| Code | Severity | Meaning / fix |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| PLAY0803 | W | `AmbiguousMatchPattern`: a `matches` pattern in the portable dialect, such as `(a+)+$`, `(a\|a)*` or `(a*)*`, can backtrack exponentially in a browser engine. Reported by both compilers and editors with the ambiguous subexpression; very large patterns are not analyzed. Rewrite it unambiguously (`a+`, `[ab]*`); see command-surface `text-and-match-rules.md`. |
|
|
160
|
+
|
|
152
161
|
## Projection event scope (PLAY0801)
|
|
153
162
|
|
|
154
163
|
Requires Screenplay 4.212.3 (`screenplay --version`) for this ordinary compilation
|
|
@@ -91,7 +91,7 @@ Requires Screenplay 4.141.0 or later (`screenplay --version`) to bind concept co
|
|
|
91
91
|
equivalent inferred ESM bytes. Flat composite read-model keys bind as v27
|
|
92
92
|
(see below). Collections and observable delivery bind.
|
|
93
93
|
Caller-supplied equality filters on collections also bind as ESM v14; context
|
|
94
|
-
sources, single-result filters, scope
|
|
94
|
+
sources, single-result filters, scope and a list-query `by` block remain blocked; a `performer` query is an opaque read left out of the executable model.
|
|
95
95
|
- One projection per read model: `from`, `every`, `all` (one block per level), `join`,
|
|
96
96
|
`children`, `nested`, `remove with`, `remove via join` (binds; blocks the execution plan at the projection level, see below), `clear with`, counters, `add`,
|
|
97
97
|
`subtract`, `set`, `clear`, variants, and the identifier mapped from the projection's key (`xId = $eventSourceId` only for an event-source key).
|
|
@@ -316,11 +316,13 @@ v22–v28 and [described handlers](../../cratis-screenplay-command-surface/refer
|
|
|
316
316
|
for the result contract. Attached handlers remain unbound.
|
|
317
317
|
|
|
318
318
|
Inline fences (`csharp`, `typescript`, `react`, `html`, `sql`) and `file <path>` attach
|
|
319
|
-
code to an owner.
|
|
320
|
-
the attached command `handler` (PLAY0800)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
319
|
+
code to an owner. Two kinds block binding (V3 blocked, no specification runs):
|
|
320
|
+
the attached command `handler` (PLAY0800) and the `file` constraint (PLAY0268, see below).
|
|
321
|
+
A query `performer` does not block: it is an opaque read, reported as information
|
|
322
|
+
`PLAY0270` and left out of the executable model; a specification that reads it is left
|
|
323
|
+
out, and every other specification still runs with `screenplay test`.
|
|
324
|
+
Requires Screenplay 4.220.1 (`screenplay --version`); older versions refuse a performer
|
|
325
|
+
query with PLAY0268 and leave the whole model unbound. Everything else binds as an **opaque requirement** that needs a target to
|
|
324
326
|
execute: reducer transitions, rule predicates, command and concept code validation, policy
|
|
325
327
|
predicates and reaction effects. Reaction effects bind only when the trigger has no
|
|
326
328
|
`reads`; a trigger with `reads` plus a `file` or inline effect fails binding. Reaction
|
|
@@ -350,7 +352,7 @@ bodies still leave their owner non-executable.
|
|
|
350
352
|
code validation is refused (STAGE-ESM-005). Opaque pure policies render.
|
|
351
353
|
- The reference runner returns Unsupported for the opaque bodies that do bind (rule and
|
|
352
354
|
policy predicates, code validation, reducer transitions); a specification that needs one
|
|
353
|
-
never passes. Attached handlers
|
|
355
|
+
never passes. Attached handlers and file constraints (and reaction effects on a trigger with
|
|
354
356
|
`reads`) are different: they leave the whole model unbound, so no specification runs at
|
|
355
357
|
all.
|
|
356
358
|
|
|
@@ -362,7 +364,7 @@ bodies still leave their owner non-executable.
|
|
|
362
364
|
| Unquoted `import Other.Contract` | PLAY0268 | external contract import is not bound |
|
|
363
365
|
| `pii` or `secret` on a concept | PLAY0268 | Older compilers refuse it outright. Current compilers admit v13 except enum/identity compliance and events mixing personal or explicitly subject-scoped secret values with explicit subject marks; see `compliance.md` in command-surface references |
|
|
364
366
|
| `query` with a `by` block over a list, or `by ... from` context-derived arguments | PLAY0268 | a `by` block binds only on an optional single-instance query (v27). An optional scalar snapshot lookup, an unfiltered list (`RM[]`) and a list narrowed by one caller-supplied `by` key bind, in their `observable` form too |
|
|
365
|
-
| Context `from`, single-result filters, `scoped to
|
|
367
|
+
| Context `from`, single-result filters, `scoped to` | PLAY0268 | Typed query context, filtering of single results and opaque scope remain unadmitted; a `performer` query is not refused (PLAY0270 information, left out of the executable model); each refusal names its reason. Caller-supplied collection equality filters bind as v14. `observable` goes in the return position (`=> observable RM[]`); as a body line it is PLAY0048 |
|
|
366
368
|
| `contains` or `starts with` in conditions only | PLAY0268 | conditions admit scalar equality (Uuid from v28) and numeric ordering, plus same-primitive command date comparisons from v26; property/concept text rules bind as v24 |
|
|
367
369
|
| Date comparisons with mixed primitives, `$context` (including occurred), `today`, protected read paths, optional condition operands, concept rules or reaction `where` | PLAY0268 | v26 admits only command conditions and command/property literal validation rules |
|
|
368
370
|
| Rule on a nested path (`lines.qty all > 0`) | PLAY0268 | put the rule on the nested value's concept |
|
|
@@ -53,7 +53,7 @@ This table is a reading of the renderer's admission, not a render result: run th
|
|
|
53
53
|
| Area | Renders | Refused (code) |
|
|
54
54
|
| --- | --- | --- |
|
|
55
55
|
| Slice kinds | `StateChange`, `StateView`, supported event-triggered `Automation` with Chronicle reactors | Translate, captures, non-event triggers, opaque bodies, clock/trigger/capture specs (`-024`); claim-reading/opaque `runs as` gates (`-034`), mixed effects from one handler (`-037`, redelivery can duplicate events), unsupported conditions/destinations/mappings (`-039`). Keep refused reactions whole |
|
|
56
|
-
| Queries | optional keyed snapshot lookups; snapshot or observable collections, keyed or unkeyed, with v14 equality filters | context/single-result filters, opaque `scoped to
|
|
56
|
+
| Queries | optional keyed snapshot lookups; snapshot or observable collections, keyed or unkeyed, with v14 equality filters | context/single-result filters, opaque `scoped to` and a list-query `by` block: PLAY0268; a `performer` query is an opaque read (PLAY0270) left out of the model ([version gate](executable-subset.md#code-attachments-what-binds-what-never-does)); v27 composite-key lookups by parts: STAGE-ESM-047, read models and projections keyed by parts: STAGE-ESM-048. DateTime filters and required parameters after optional filters refuse `-010`; filter-name collisions refuse `-012`; concept arguments retain Arc validation, including nested members/elements; nullable keys or optional concept filters with `not empty` refuse `-010` rather than changing null behavior |
|
|
57
57
|
| Authorization | role-only, authenticated, opaque pure policies, claim conditions with Uuid, Date, DateTime or text targets (numeric and Boolean claim matches render as always false), `not`/and/or; roles read through the identity's `RoleClaimType` | role-claim URIs, nested opaque policies, unresolved or non-portable policies, no single identifier, opaque-policy subjects that are not text, Uuid, Date or DateTime (`STAGE-ESM-015`, a restriction separate from declarative claim targets) |
|
|
58
58
|
| Event sources, streams, routes (ESM v8) | rendered, including routed read-model/query companion replay | reserved stored names, unformattable routed fixtures (`STAGE-ESM-030`) |
|
|
59
59
|
| Numbers, case tables | whole numbers as `long` from ESM v8; case tables | out-of-`int` literals below v8 (`STAGE-ESM-031`) |
|
|
@@ -99,7 +99,7 @@ ids into routing to fit a renderer (PLAY0515).
|
|
|
99
99
|
observable collections, including supported equality filters;
|
|
100
100
|
Requires Stage 4.90.0 for snapshot lists and live `IReadModels.Observe` streams;
|
|
101
101
|
older renderers refuse snapshot lists with STAGE-ESM-010.
|
|
102
|
-
context/single-result filters, opaque `scoped to`
|
|
102
|
+
context/single-result filters, and opaque `scoped to` block binding; a `performer` query is left out of the executable model ([version gate](executable-subset.md#code-attachments-what-binds-what-never-does)). Keep a list the domain needs and report
|
|
103
103
|
the capability gap; marking it `observable` or keying it is a modeling decision for the user.
|
|
104
104
|
- Attached file/inline handlers refuse PLAY0800 and require constrained implementation
|
|
105
105
|
attachments: gap-fill in code, not by waiting for ESM admission.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Compiler syntax: `cheat-sheet.md`. Negative capability claims depend on the tool you ran; name it in every verdict.
|
|
4
4
|
|
|
5
5
|
1. Clean V1 is not executable: binding errors appear through MCP (V2), `render` or `cratis screenplay validate --executable` (bundled compiler, writes nothing). Neither `screenplay <folder>` nor plain `cratis screenplay validate` binds.
|
|
6
|
-
2. The Screenplay samples validate clean but are not all executable; copying their idioms (context-filtered queries, enum/identity compliance, `today`, `reads`, `$causedBy`, templates
|
|
6
|
+
2. The Screenplay samples validate clean but are not all executable; copying their idioms (context-filtered queries, enum/identity compliance, `today`, `reads`, `$causedBy`, templates) can block V3. Caller-supplied collection equality filters bind; read read-surface `references/equality-filters.md`.
|
|
7
7
|
3. Know the target mode (design/executable/renderable) before writing; state it.
|
|
8
8
|
4. Plain `produces` without `for` means allocation in the ESM but "the identifier" in rendered code. Always write `for <identifier>` on a plain `produces <Name>`; inline `produces event <Name>` omits `for` and defaults to the identifier (PLAY0478).
|
|
9
9
|
5. PLAY0029 is a warning: a misspelled keyword (or `type` inside a slice) drops the construct silently. Use `--warnings-as-errors`.
|
|
@@ -157,7 +157,7 @@ Requires Stage 4.90.0 for snapshot/live lists, 4.96.0 for non-text secrets and 4
|
|
|
157
157
|
| `STAGE-ESM-005` | Code validation or rule predicate bodies; or a command beyond the first capability (optional event property, event revision other than the first, validation or requirement that cannot be rendered, neither `produces` nor a response) | Use declarative validation; non-initial event generations are not rendered |
|
|
158
158
|
| `STAGE-ESM-006` | `produces` that cannot render without changing its destination or mappings (expression/enum conditions, literal or computed mappings, `for` not a command identifier, mappings not one-to-one with event properties, duplicate tags) | Map properties one to one; supported conditional productions render as guarded events, not separate commands |
|
|
159
159
|
| `STAGE-ESM-007`, `-008`, `-009` | Projection count per read model (exactly one projection or reducer each); unresolvable properties or no single read-model transition (**flat** projections only); affected instance not one, not keyed by an event property, or not the event-source identity (flat projections only) | One projection per read model; see "Projections" below |
|
|
160
|
-
| `STAGE-ESM-010` | A query is neither an optional snapshot lookup by the read-model identifier nor a supported snapshot/observable collection query; each query is checked on its own. Both collection modes render. Concept-typed keys and filters retain Arc validation, including composite members and collection elements; nullable concept keys or optional concept filters with `not empty` cannot preserve null semantics | Keyed `XById => X optional` queries or snapshot/observable lists. Equality filters render on both collections; DateTime filters, required filters after optional ones and unsupported null-validation shapes refuse 010. Filter-name collisions refuse 012. Context/single-result filters, opaque scope
|
|
160
|
+
| `STAGE-ESM-010` | A query is neither an optional snapshot lookup by the read-model identifier nor a supported snapshot/observable collection query; each query is checked on its own. Both collection modes render. Concept-typed keys and filters retain Arc validation, including composite members and collection elements; nullable concept keys or optional concept filters with `not empty` cannot preserve null semantics | Keyed `XById => X optional` queries or snapshot/observable lists. Equality filters render on both collections; DateTime filters, required filters after optional ones and unsupported null-validation shapes refuse 010. Filter-name collisions refuse 012. Context/single-result filters, opaque scope and a list-query `by` block remain PLAY0268; a performer query is left out of the model (PLAY0270, Requires Screenplay 4.220.1), so render emits nothing for it and it is a gap-fill item; a read model with several key parts (v27) refuses `STAGE-ESM-048` |
|
|
161
161
|
| `STAGE-ESM-011` | A specification cannot render (see the rules below) | Reshape it only if the assertion keeps its meaning; otherwise keep the specification, record the gap, and gap-fill with the specification as the oracle. Never weaken or delete it to get a render |
|
|
162
162
|
| `STAGE-ESM-012` | Generated C# names collide (properties, types, namespaces, constraints) | Rename |
|
|
163
163
|
| `STAGE-ESM-013` | An event-context value other than `$context.occurred` mapped in `produces` | Map a command property when that preserves the meaning; otherwise gap-fill |
|
|
@@ -657,6 +657,7 @@
|
|
|
657
657
|
"availableTargets": [
|
|
658
658
|
"cratis-content-length",
|
|
659
659
|
"cratis-developer-marketing",
|
|
660
|
+
"cratis-discoverability",
|
|
660
661
|
"cratis-release-notes",
|
|
661
662
|
"cratis-social-feed-post",
|
|
662
663
|
"cratis-technical-examples",
|
|
@@ -670,6 +671,7 @@
|
|
|
670
671
|
{
|
|
671
672
|
"id": "cratis/documentation",
|
|
672
673
|
"availableTargets": [
|
|
674
|
+
"cratis-discoverability",
|
|
673
675
|
"cratis-documentation-writing",
|
|
674
676
|
"cratis-engineering-docs-authoring",
|
|
675
677
|
"cratis-llm-friendly-documentation",
|