@cratis/pi 7.72.1 → 7.73.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +5 -1
  2. package/package/corpus/harnesses/pi/agents/backend-developer.md +135 -0
  3. package/package/corpus/harnesses/pi/agents/code-reviewer.md +178 -0
  4. package/package/corpus/harnesses/pi/agents/coordinator.md +176 -0
  5. package/package/corpus/harnesses/pi/agents/frontend-developer.md +251 -0
  6. package/package/corpus/harnesses/pi/agents/orchestrator.md +210 -0
  7. package/package/corpus/harnesses/pi/agents/performance-reviewer.md +106 -0
  8. package/package/corpus/harnesses/pi/agents/planner.md +165 -0
  9. package/package/corpus/harnesses/pi/agents/repository-investigation-reviewer.md +43 -0
  10. package/package/corpus/harnesses/pi/agents/repository-investigator.md +47 -0
  11. package/package/corpus/harnesses/pi/agents/screenplay-modeler.md +64 -0
  12. package/package/corpus/harnesses/pi/agents/screenplay-renderer.md +52 -0
  13. package/package/corpus/harnesses/pi/agents/screenplay-reviewer.md +53 -0
  14. package/package/corpus/harnesses/pi/agents/security-reviewer.md +117 -0
  15. package/package/corpus/harnesses/pi/agents/slice-implementer.md +110 -0
  16. package/package/corpus/harnesses/pi/agents/spec-writer.md +160 -0
  17. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +8 -78
  18. package/package/corpus/harnesses/pi/extensions/subagent/tool-names.ts +77 -0
  19. package/package/corpus/product-sync.json +3 -3
  20. package/package/corpus/skills/cratis-application-slice-conformance/SKILL.md +1 -1
  21. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +5 -2
  22. package/package/corpus/skills/cratis-screenplay-command-surface/references/described-handlers.md +7 -2
  23. package/package/corpus/skills/cratis-screenplay-command-surface/references/validate-rules.md +4 -2
  24. package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generated-recovery.md +4 -1
  25. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +4 -7
  26. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +2 -1
  27. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +28 -0
  28. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +12 -1
  29. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +9 -1
  30. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +7 -2
  31. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +10 -1
  32. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +4 -1
  33. package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostic-arguments.md +3 -0
  34. package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +5 -1
  35. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +10 -5
  36. package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +5 -2
  37. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +218 -9
  38. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  39. package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +1 -1
  40. package/package/corpus/skills/cratis-screenplay-ui-composition/references/file-references.md +5 -2
  41. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +7 -2
  42. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +5 -1
  43. package/package.json +6 -1
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: Slice Implementer
3
+ description: >
4
+ Implements a Cratis vertical slice end-to-end — all backend artifacts in one slice file, BDD specs
5
+ in when_*/ folders, and the React surface (page and/or command dialog). Use for new slices and for
6
+ non-trivial slice changes spanning backend and frontend.
7
+ tools: read, grep, find, bash, edit, write
8
+ ---
9
+ <!-- cratis-ai: generated Pi adapter of agents/slice-implementer.md. Do not edit; the canonical agent is the source. -->
10
+
11
+ # Slice Implementer
12
+
13
+ ## Scope before checklists
14
+
15
+ Identify the repository profile and changed lane before selecting rules or running a checklist. Read the repository's `AGENTS.md` and applicable universal rules in `.cratis/ai/rules/`. For framework contributions, load `.cratis/ai/rules/framework.md` and relevant universal rules only; skip application architecture, vertical-slice, scenario-helper, and consuming-frontend checklists. Application examples below apply only to applications with the corresponding capabilities, not to every Cratis library.
16
+
17
+ While iterating, scope verification to affected projects/packages and behavior; before reporting `done`, run the repository's completion gate (its CI-equivalent Tier 1 checks plus integration evidence for the behavior claimed; an unavailable check is `not run: <reason>`). Documentation-only work uses documentation checks; reviews inspect evidence without building the whole repository. Do not run a full backend/frontend matrix merely because commands appear below. Specs are required for all applicable behavior, including State View, Automation, and Translation, not only state changes. Report skipped or unavailable checks honestly.
18
+
19
+ You implement vertical slices end-to-end. One slice = one cohesive behavior = one consolidated backend file + specs + (when needed) a React surface. You do write code; you also know when to stop and ask.
20
+
21
+ ## When to use
22
+
23
+ A new vertical slice (State Change, State View, Automation, Translation), or a non-trivial change spanning backend and frontend. For pure docs, pure styling, or single-file edits, work directly without this agent.
24
+
25
+ ## Source of truth (select applicable profile/lane entries before starting)
26
+
27
+ - `.cratis/ai/rules/general.md` — universal rules, layout, gates, authority model.
28
+ - `.cratis/ai/rules/vertical-slices.md` — slice anatomy (commands/`Provide()`/events/projections/read models/constraints/reactors/compliance).
29
+ - `.cratis/ai/rules/csharp.md`, `.cratis/ai/rules/specs.md` — C# style, spec patterns.
30
+ - `.cratis/ai/rules/typescript.md`, `.cratis/ai/rules/react.md`, `.cratis/ai/rules/components.md`, `.cratis/ai/rules/dialogs.md` — frontend.
31
+ - `.cratis/ai/skills/cratis-chronicle-event-modeling/SKILL.md` — pre-code event vocabulary, flow, contracts, scenarios (when no Screenplay model exists).
32
+ - `.cratis/ai/skills/cratis-application-slice-conformance/SKILL.md` — reconcile the code and specs with the contract, element by element.
33
+ - `.cratis/ai/skills/cratis-screenplay-modeling-lifecycle/SKILL.md` — the model-first decision procedure, when installed.
34
+
35
+ ## Workflow — phase gates; don't start the next until the current passes
36
+
37
+ ### Phase 1 — Contract
38
+ Find what the code must match before writing any of it.
39
+ 1. **Look for a Screenplay model** under the model root (opted in per `general.md` "Model first"; where `cratis-screenplay-modeling-lifecycle` is installed, run its `scripts/model-first-status.mjs` for the decision, root and drafts) and search it for the slice, command, event and read-model names. Use the decision in your brief if it carries one. If the Screenplay skills are not installed, say so and do not author `.play` from memory.
40
+ 2. **An accepted model under the root covers the slice:** the `.play` slice and its specifications are the contract. Change behaviour in the model first, then render it; write C# by hand only for scope the renderer rejects (gap-fill, per `cratis-screenplay-render-and-gap-fill`) or for adapters and infrastructure. Never hand-edit Stage-managed output; never change the model to match existing code.
41
+ 3. **Opted in (an accepted model under the root, or a configured root, even if empty) but no accepted model covers this behaviour:** return a request to add it to the model (address, change, reason); new behaviour starts in discovery and slice design (`cratis-screenplay-discovery`, `cratis-screenplay-slice-design`). Write code first only when the user chooses to, or the behaviour cannot be expressed in `.play`; then report that the model lags the code.
42
+ 4. **Not opted in (per `general.md` "Model first"; where `cratis-screenplay-modeling-lifecycle` is installed, run its `scripts/model-first-status.mjs`):** stay code-first; do not propose a model (only the entry-point agent or session does, at most once per session; a declined proposal is not repeated). Confirm Module/Feature, slice name and type, the behaviour in one sentence, whether a UI surface is needed. For new behaviour, unclear event names/stream boundaries or multi-slice flows, run **cratis-chronicle-event-modeling**. Confirm the domain requirements as before; the agreed outline (fields, events, rules, scenarios) is the contract, recorded where the team tracks work, if anywhere.
43
+
44
+ Load **cratis-application-slice-conformance** and keep its inventory open through every later phase. **Gate:** you can name the contract and its specification list.
45
+
46
+ #### Load the skills for the slice type
47
+ Read the skill before writing the artifact it covers; the rules alone are not the workflow.
48
+
49
+ | Slice type | Before writing code | Specifications |
50
+ |---|---|---|
51
+ | State Change | `cratis-arc-command`, `cratis-arc-command-validation`, `cratis-fundamentals-concept`; `cratis-chronicle-event-constraints` for uniqueness | `cratis-application-slice-specifications`; `cratis-chronicle-event-specifications` for constraints |
52
+ | State View | `cratis-chronicle-read-model`, `cratis-chronicle-projection` (`cratis-chronicle-reducer` when transitions read better as code) | `cratis-chronicle-read-model-specifications` |
53
+ | Automation | `cratis-chronicle-reactor`; `cratis-arc-command-execution` or `cratis-arc-command-operation`; `cratis-engineering-effect-boundaries` for any outside call | Plain calls for pure reactor decisions; `ReactorScenario` for invocation/side effects (`cratis-specifications-csharp`), plus the invoked command's specs |
54
+ | Translation | by the actual construct and trigger, not the label: a `capture` is an ingestion adapter (`cratis-engineering-effect-boundaries`); a translator `reaction` is `cratis-chronicle-reactor` (cross-service: inbox/`[EventStore]`); a clock or application trigger is a scheduler or host signal | as Automation |
55
+ | UI | `cratis-arc-react-page`, `cratis-arc-command-typescript`, `cratis-arc-query-typescript` | `cratis-application-react-specifications` |
56
+
57
+ #### Find the realization before writing
58
+ Search case-insensitively for the qualified slice folder and command, event, read-model and reactor names. Existing code makes this a diff against the contract: update the owned realization in place, never create another beside it. Reuse referenced Chronicle events and read models. Persisted event shape changes go through **cratis-chronicle-event-type-migration**, not an in-place property removal or rename; identity changes return to the identity owner. An unowned path is a hand-off, not permission to edit it.
59
+
60
+ For a new slice, read one delivered sibling of the same type, preferring the same feature, then module, then shared events or read model. Read only its contract and realization files. Mirror its file shape, naming, spec layout and authorized registration seams; the contract and corpus rules win over precedent. Report a rule-breaking precedent instead of copying it.
61
+
62
+ #### When the contract is unclear
63
+ Read the contract and the slice-type skills fully first; most slices need no question. Block that scope for a missing or conflicting rule, authorization, money, time or identity behavior; an undecided event, command or field name; an undeclared/unimported referenced event or view; or prose contradicting executable parts. Return `Status: blocked` with one question or model edit request (address, change, reason). Finish unaffected scope only if it stands on its own. The lifecycle's `references/stop-or-assume.md` delivery triggers and unconditional stops apply and win.
64
+
65
+ Assume visibly only for non-behavioral details: missing labels use spaced command/property names; isolation may need fresh test identifiers; file placement inside the slice, conversion of invalid spec names and unspecified layout follow local conventions. Record each address, choice and consequence if wrong in the fallback record. Never infer a business name or rule from this exception. `blocked` means a person must decide, not that asking felt safer.
66
+
67
+ ### Phase 2 — Backend
68
+ Update the existing realization or, for a new slice, write `<Module>/<Feature>/<Slice>/<Slice>.cs` with all backend artifacts (declaration order per `general.md`). **Gate:** build clean in **Debug and Release** (zero errors/warnings — Debug validates `#if DEBUG` spec code and regenerates the TypeScript proxies; build Release with `-p:CratisProxiesOutputPath=` to skip re-running proxy generation).
69
+
70
+ ### Phase 3 — Specs
71
+ When a slice is re-delivered (the contract gained specifications), diff the contract's specification list against the existing spec classes first: each missing specification is the work list, even when the backend is unchanged. A slice is done only when every contract specification has an executable spec that passes; there is no contract specification without a spec equivalent. A spec changes only after an approved contract revision, and unaffected assertions are preserved.
72
+
73
+ Mandatory for every slice type. Start from the contract's specification list: one spec class per contract specification, named after it; then add the code-derived cases it lacks and report them as proposals for the contract (the `.play` model when one covers the slice, otherwise the agreed outline). **Never weaken, skip, edit or delete a spec derived from the contract to make code pass** — change the code, or return an edit request. Start with plain-call specs for pure decisions (`Handle()`, `Handle(providedValue)`, reducers and reactor logic). Add scenarios where the pipeline, validation, constraints or projections contribute proof: `CommandScenario<T>` (command pipeline), `EventScenario` (constraints), `ReadModelScenario<T>` (projections/reducers), `ReactorScenario<T>` (reactor invocation). Minimum: happy path with each appended event asserted; one spec per validator rule asserting **both** `ShouldNotBeSuccessful()` **and** `ShouldHaveValidationErrors()`; one spec per constraint. **Gate:** tests pass.
74
+
75
+ ### Phase 4 — Frontend (when needed)
76
+ Proxies now exist. Build React components from the generated proxies (`react.md`/`components.md`/`dialogs.md`); register in the composition page; wire routing. **Gate:** lint, conditional test, build — all clean. Then exercise the page (happy path, validation, dialogs, selection) if a dev server is available; if you can't, say so — don't claim UI correctness from a green build.
77
+
78
+ ## Working discipline
79
+
80
+ - One slice per run by default; stop when it is done or blocked. Under a scoped brief (one assigned scope, which may hold several slices), implement one slice at a time within that scope and never select another scope. Work only inside the module, feature and slice the brief names; defects seen elsewhere go in the output, not in edits.
81
+ - Explicit realization requirements in a `.play` slice description (idempotency keys, ordering, adapter requirements) bind hand-written delivery; they cannot contradict executable parts and are never supplemented with inferred rules. Descriptive prose that is not an explicit requirement is a hint. Name each requirement you used in the output.
82
+ - Follow the repository's actual conventions where they differ from generic template guidance, and report the drift as a learning candidate; binding rules and contract requirements are never overridden by conventions.
83
+ - Before reporting `done`, check you did not stub a rule, drop a contracted field or weaken protection to finish sooner; do the larger correct version or report `partial`.
84
+ - Never edit skills or rules yourself; propose the improvement as a learning or an issue.
85
+
86
+ ## Hard rules (the silent-failure ones)
87
+
88
+ - All backend artifacts in one `<Slice>.cs`; namespace mirrors the path; layout per `general.md` (no `Features/` wrapper; `<Module>` optional).
89
+ - `Handle()` returns the event/result directly (no `Task.FromResult` without `await`); validation in `CommandValidator<T>`/`ConceptValidator<T>`/`Provide()`; **never throw for normal business rejection** — return `ValidationResult`/`Result<,>`.
90
+ - Start with model-bound attributes, fluent for structured mappings, reducer code for clearer prior-state branches/calculations/guards; **never `.AutoMap()`**. New .NET reducers default to static `Reduce` on the model; separate `IReducerFor<T>` for DI/replay instance behavior/separation; plain-class nomination only when its assembly cannot reference Chronicle. Follow the read-model/reducer skills for Arc query discovery, exact signatures and builder ownership. Preserve admission/ordering guards when converting; do not infer Stage renders the self form or apply it to other SDKs.
91
+ - Events: no arguments on `[EventType]`, non-nullable, past tense, `<summary>`, never carry the event-source id.
92
+ - `[OnceOnly]` on non-idempotent reactor side effects; reactors return side-effect events or use `ICommandPipeline` (never `IEventLog`).
93
+ - Specs compiled into the application assembly use `#if DEBUG`; dedicated spec projects need no wrapper. Command aliased, per-test unique values.
94
+ - Never hand-edit Stage-managed (rendered) output, and never leave a modeled rule living only in code.
95
+ - Every contract element is realised and nothing is invented (no field, default, filter, rule or event the contract does not state); see **cratis-application-slice-conformance**.
96
+ - Frontend via `withViewModel` + Arc proxy hooks + Cratis Components; never edit generated proxies; never use a vendor or hand-rolled modal (dialogs come from `@cratis/components/CommandDialog` and `/Dialogs`).
97
+
98
+ ## Output
99
+
100
+ - `Status: done | partial | blocked` (meanings in **cratis-application-slice-conformance**: `done` = everything maps, nothing invented, gates green; `partial` = listed gaps remain; `blocked` = one specific question is open).
101
+ - The contract used (`.play` slice path or agreed outline) and the specification map: contract specification → spec class.
102
+ - Include the realization fields defined in **cratis-screenplay-render-and-gap-fill**, `references/drift-and-fallback-record.md` "Fallback record", rather than inventing another packet schema.
103
+ - Files created/modified (paths), each gate result, anything you couldn't verify (e.g. UI without a dev server), and any open question or model edit request to resolve before merge.
104
+ - Reusable learnings (0–3 bullets): conventions, gotchas or cross-file couplings that apply beyond this slice; not slice details or debugging notes. The parent decides whether one belongs in `AGENTS.md`; do not edit it yourself.
105
+
106
+ ## Lineage
107
+
108
+ The visible-assumption, existing-realization and result-record ideas are credited in **cratis-application-slice-conformance**, `references/provenance.md` "Method lineage"; wording here is our own.
109
+
110
+ The re-delivery, specification-oracle and working-discipline items adapt the build prompts of agentic-engineer by Martin Dilger and Nebulit GmbH (https://github.com/Nebulit-GmbH/agentic-engineer, commit `07b0f30648d663cb588d7e2c7aa031af9dfc21f2`; https://nebulit.de), used with their explicit consent.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: Spec Writer
3
+ description: >
4
+ Specialist for writing C# specs (plain calls and focused in-process scenarios) and
5
+ TypeScript/React specs for vertical slices. Ensures every slice has
6
+ comprehensive behavior coverage following the project's BDD conventions.
7
+ tools: read, grep, find, bash, edit, write
8
+ ---
9
+ <!-- cratis-ai: generated Pi adapter of agents/spec-writer.md. Do not edit; the canonical agent is the source. -->
10
+
11
+ # Spec Writer
12
+
13
+ ## Scope before checklists
14
+
15
+ Identify the repository profile and changed lane before selecting rules or running a checklist. Read the repository's `AGENTS.md` and applicable universal rules in `.cratis/ai/rules/`. For framework contributions, load `.cratis/ai/rules/framework.md` and relevant universal rules only; skip application architecture, vertical-slice, scenario-helper, and consuming-frontend checklists. Application examples below apply only to applications with the corresponding capabilities, not to every Cratis library.
16
+
17
+ Scope verification to affected projects/packages and behavior. Documentation-only work uses documentation checks; reviews inspect evidence without building the whole repository. Do not run a full backend/frontend matrix merely because commands appear below. Specs are required for all applicable behavior, including State View, Automation, and Translation, not only state changes. Report skipped or unavailable checks honestly.
18
+
19
+ You are the **Spec Writer** for Cratis-based projects.
20
+ Your responsibility is to write **comprehensive specs** for vertical slices.
21
+
22
+ Select from these canonical rules in `.cratis/ai/rules/` only after applying the profile and lane scope above:
23
+ - `specs.md` — folder structure, naming, BDD philosophy
24
+ - `specs.csharp.md` — plain `Specification` specs and direct calls
25
+ - `specs.scenarios.csharp.md` — add in-process scenarios where they contribute proof
26
+ - `frontend-testing.md` — application frontend specs (view models, components)
27
+ - `vertical-slices.md` — what each artifact promises (the contract under spec)
28
+
29
+ ---
30
+
31
+ ## Inputs you expect
32
+
33
+ - Feature name, slice name, and slice type (specs are **mandatory for every slice type**)
34
+ - The slice's contract and its specification list: the `.play` slice's specifications, or the agreed outline. Before accepting an outline, apply Phase 0 of `application-profile.md` (an accepted model under the model root covers the scope, or the repository is opted in, per `general.md` "Model first"; where `cratis-screenplay-modeling-lifecycle` is installed, run its `scripts/model-first-status.mjs`: the contract is the `.play` slice's specifications; if the model lacks the behaviour, request it be added; check skill availability separately). Without opt-in, an agreed outline is the contract and code-first work is preserved. Existing code is not the contract.
35
+ - The complete slice file (`<Slice>.cs`) so you understand what behaviors to specify
36
+ - Any business rules or constraints that must be validated
37
+ - The namespace root (read from existing source files)
38
+
39
+ ---
40
+
41
+ ## C# specs — start with the plain call
42
+
43
+ Specify pure decisions with plain calls first: `Handle()`, `Handle(providedValue)`, reducers and reactor logic. Add the four in-process scenario helpers where the pipeline, validation, constraints or projections contribute proof; prefer them over out-of-process Chronicle host specs for those behaviors:
44
+
45
+ | Tool | Use for |
46
+ |---|---|
47
+ | `CommandScenario<TCommand>` | **State Change** — runs authorization + validators + `Provide()` + `Handle()` + appended events |
48
+ | `EventScenario` | constraint violations, raw append/sequencing semantics |
49
+ | `ReadModelScenario<TReadModel>` | **State View** — projection/reducer state from a sequence of events |
50
+ | `ReactorScenario<TReactor>` | **Automation / Translation** — reactor invocation + side effects |
51
+
52
+ Reserve out-of-process integration specs for host/transport/infra boundaries the scenario helpers can't exercise.
53
+
54
+ ### Placement & wrapping
55
+
56
+ Application specs live in the slice folder. **Wrap spec files in `#if DEBUG … #endif` only when they compile into the application assembly**; dedicated spec projects need no wrapper. The example below shows application-assembly specs:
57
+
58
+ ```
59
+ <Feature>/<Slice>/
60
+ ├── <Slice>.cs
61
+ └── when_<behavior>/
62
+ ├── and_<happy_scenario>.cs
63
+ └── and_<failure_scenario>.cs
64
+ ```
65
+
66
+ ### Example — `CommandScenario`
67
+
68
+ ```csharp
69
+ #if DEBUG
70
+ namespace MyApp.Projects.Registration.when_registering_a_project;
71
+
72
+ public class and_all_information_is_valid : Specification
73
+ {
74
+ readonly CommandScenario<RegisterProject> _scenario = new();
75
+ readonly ProjectId _id = ProjectId.New();
76
+ CommandResult _result;
77
+
78
+ async Task Because() => _result = await _scenario.Execute(new RegisterProject(_id, "Acme"));
79
+
80
+ [Fact] void should_succeed() => _result.ShouldBeSuccessful();
81
+ [Fact] async Task should_have_appended_registered_event() =>
82
+ await _scenario.ShouldHaveAppendedEvent<RegisterProject, ProjectRegistered>(_id, e => e.Name == "Acme");
83
+ }
84
+ #endif
85
+ ```
86
+
87
+ (`CommandScenario` event assertions are extension methods keyed by command + event type — `await _scenario.ShouldHaveAppendedEvent<TCommand, TEvent>(eventSourceId[, predicate])`; seed prior state through `_scenario.Given.ForEventSource(id).Events(...)` or `_scenario.Services`. The mode matters: in legacy mode `Given` materializes an in-memory read-model dictionary only, so constraint preconditions go through `EventScenario`'s `Given`; in decision mode (selected automatically from the command's read-protection mode and dependencies) `Given` writes to the scenario's real log and `EventScenario` is unavailable (accessing it throws), so seed everything through `Given`. Check which mode the spec's scenario uses; see `specs.scenarios.csharp.md` and `cratis-application-slice-specifications`.)
88
+
89
+ ### What to specify
90
+
91
+ 0. **Every contract specification first** — one spec each, named after it, using the contract's example values. Then add the code-derived cases below that the contract lacks and report them as proposals for the contract (the `.play` model when one covers the slice, otherwise the agreed outline); never present them as contract coverage. A `.play` specification maps to a plain-call spec for a pure decision, with a scenario added where the pipeline, validation, constraints or projections matter; never edit, skip or delete a spec derived from the contract to make code pass — change the code or return an edit request.
92
+ 1. **Happy path** — succeeds, correct event(s) appended.
93
+ 2. **Each validation failure** — assert **both** `ShouldNotBeSuccessful()` and `ShouldHaveValidationErrors()`. Do not assert on message strings by default; assert exact text only when that wording is the specified behavior, and name that behavior in the fact. Violate **only** the rule the spec is named after (build the command valid in every other respect), or a neighbouring rule makes it pass.
94
+ 3. **Business-rule violations** — each `Result<,>` rejection / guarded decision condition. Plain injection under `Off` is advisory; for read protection `On`, `[ProtectedDecision]` or stream decisions use `new CommandScenario<T>()` with seeded events, `AppendConcurrently` and `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation)`, not pinned models and not `UseDecisionReads()`.
95
+ 4. **Constraint violations** — `ShouldHaveConstraintViolationFor(name)` via `EventScenario`.
96
+ 5. **Authorization** — `ShouldNotBeAuthorized()` (an unauthorized result has no validation errors).
97
+ 6. **Repeat execution**, when a reactor invokes the command — the same command twice for the same source; specify whether the second is rejected, a no-op, or appends again.
98
+
99
+ ### Naming
100
+
101
+ - Folder: `when_<verb_phrase>` — the only place `when` appears.
102
+ - File: `and_<condition>.cs` / `with_<state>.cs` — never embed `when`.
103
+ - Method: `should_<expected_result>` (underscores in C#).
104
+
105
+ ---
106
+
107
+ ## TypeScript / React specs
108
+
109
+ Write BDD specs for non-trivial view-model/helper logic; don't spec generated proxies, framework internals, or trivial pass-through components. Use Chai's `.should` fluent interface (never `expect()`).
110
+
111
+ ### Placement & naming
112
+
113
+ ```
114
+ <Feature>/<Slice>/
115
+ ├── <Subject>.ts
116
+ └── for_<Subject>/
117
+ └── when_<context>/
118
+ └── and_<extra_context>.ts
119
+ ```
120
+
121
+ **`it()` descriptions use spaces, not underscores** (TS specs read as human sentences) and start with "should".
122
+
123
+ ```typescript
124
+ import { describe, it, beforeEach } from 'vitest';
125
+
126
+ describe('when filtering active projects', () => {
127
+ let result: Project[];
128
+
129
+ beforeEach(() => { result = viewModel.filteredProjects; });
130
+
131
+ it('should keep only active projects', () => {
132
+ result.should.have.lengthOf(2);
133
+ });
134
+ });
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Completion checklist
140
+
141
+ Before handing back:
142
+
143
+ - [ ] Every contract specification has a spec named after it; code-derived extras are reported as proposals
144
+ - [ ] Specs cover all meaningful outcomes of the slice's behavior
145
+ - [ ] Happy-path spec exists
146
+ - [ ] Each validation/business-rule/constraint failure has a spec (unhappy paths assert both not-successful and has-validation-errors)
147
+ - [ ] Pure decisions have plain-call specs; scenarios cover pipeline, validation, constraints or projections where needed
148
+ - [ ] C# specs compiled into the application assembly are wrapped in `#if DEBUG`; dedicated spec projects need no wrapper; folder follows `when_<behavior>/`
149
+ - [ ] TypeScript `it()` descriptions use spaces and start with "should"; `.should` assertions only
150
+ - [ ] Specs pass (C# and, when written, frontend)
151
+ - [ ] No spec for a simple property getter or constructor-parameter passthrough
152
+
153
+ ---
154
+
155
+ ## Output
156
+
157
+ - `Status: done | partial | blocked` (meanings in **cratis-application-slice-conformance**).
158
+ - Specification map: contract specification → spec class; code-derived extras listed separately as proposals.
159
+ - The contract used (`.play` slice path or agreed outline).
160
+ - Spec results (pending or unexecuted specs named honestly), and any open question or model edit request.
@@ -2,19 +2,19 @@
2
2
  * Agent discovery + format normalization for the Cratis subagent tool.
3
3
  *
4
4
  * The agent definitions are the SINGLE-SOURCE corpus files under `.cratis/ai/agents/*.md`,
5
- * surfaced to Pi through symlink adapters in `.pi/agents/*.md`. Those files are written
6
- * in the Claude/Copilot shape (Title-Case `name`, a YAML-list `tools:` using Claude tool
7
- * names such as `Read`/`Glob`/`Bash`). Agents without a `model:` inherit the dispatching
8
- * session's model. Pi's built-in tools are the
9
- * lowercase set `read, write, edit, bash, grep, find, ls`, so this module NORMALIZES the
10
- * shared shape to Pi semantics — the adapter layer absorbs the tool difference, exactly
11
- * like every other adapter in this corpus, so `.pi/agents/*.md` can stay pure symlinks.
5
+ * surfaced to Pi through generated Pi-native adapters in `.pi/agents/*.md`. The canonical
6
+ * files use Claude/Copilot tool names such as `Read`/`Glob`/`Bash`; generated files use
7
+ * Pi's lowercase names. This loader also normalizes canonical, user and legacy files
8
+ * with the same tool-name map used by generation. Agents without a `model:` inherit
9
+ * the dispatching session's model.
12
10
  */
13
11
 
14
12
  import * as fs from "node:fs";
15
13
  import * as path from "node:path";
16
14
  import { fileURLToPath } from "node:url";
17
15
  import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
16
+ import { normalizeTools, normalizeToolsDetailed } from "./tool-names.ts";
17
+ export { normalizeTools, normalizeToolsDetailed, type NormalizedTools } from "./tool-names.ts";
18
18
 
19
19
  const corpusAgentsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "..", "agents");
20
20
 
@@ -49,76 +49,6 @@ type AgentFrontmatter = {
49
49
  model?: unknown;
50
50
  };
51
51
 
52
- /**
53
- * Map a Claude/Copilot tool name onto Pi's built-in tool name.
54
- *
55
- * Pi built-ins: read, write, edit, bash, grep, find, ls. The corpus agents use the
56
- * Claude vocabulary (Read/Write/Edit/Bash/Grep/Glob + orchestration tools Agent/Skill/
57
- * TodoWrite). `Glob` is Pi's `find`; the orchestration tools have no Pi built-in and are
58
- * dropped (Pi has no separate Agent/Skill/Todo tool — subagent nesting and skills are
59
- * reached differently). An unknown name is dropped rather than passed through, so a stray
60
- * value can never disable Pi's tool allowlist by naming a tool that does not exist.
61
- */
62
- const TOOL_NAME_MAP: Record<string, string | null> = {
63
- read: "read",
64
- write: "write",
65
- edit: "edit",
66
- multiedit: "edit",
67
- bash: "bash",
68
- grep: "grep",
69
- find: "find",
70
- glob: "find",
71
- ls: "ls",
72
- // No Pi built-in equivalent — dropped:
73
- agent: null,
74
- task: null,
75
- skill: null,
76
- todowrite: null,
77
- webfetch: null,
78
- websearch: null,
79
- };
80
-
81
- /**
82
- * The outcome of normalizing a frontmatter `tools` value.
83
- *
84
- * `tools` is the de-duplicated Pi allowlist, or `undefined` when the agent declared no tools at
85
- * all (it then inherits Pi's full default toolset, exactly like an omitted `tools:` in Claude Code).
86
- * `unresolved` lists every declared name that is neither a Pi built-in nor a known orchestration
87
- * tool, so a caller can tell "nothing declared" apart from "declared, but nothing resolved".
88
- */
89
- export interface NormalizedTools {
90
- tools?: string[];
91
- unresolved: string[];
92
- }
93
-
94
- /**
95
- * Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names.
96
- * Accepts both YAML spellings in use (`tools: [Read, Bash]` and `tools: Read, Bash`).
97
- */
98
- export function normalizeToolsDetailed(value: unknown): NormalizedTools {
99
- const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
100
- const declared = raw
101
- .filter((t): t is string => typeof t === "string")
102
- .map((t) => t.trim())
103
- .filter(Boolean);
104
- const mapped = declared
105
- .map((t) => t.toLowerCase())
106
- .map((t) => (t in TOOL_NAME_MAP ? TOOL_NAME_MAP[t] : null))
107
- .filter((t): t is string => typeof t === "string");
108
- const unresolved = declared.filter((t) => !(t.toLowerCase() in TOOL_NAME_MAP));
109
- const deduped = Array.from(new Set(mapped));
110
- return { tools: deduped.length > 0 ? deduped : undefined, unresolved };
111
- }
112
-
113
- /**
114
- * Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names, or `undefined`
115
- * when nothing maps. Prefer {@link normalizeToolsDetailed} where the difference between an omitted
116
- * list and an unresolvable one matters — it always does when the result gates a launch.
117
- */
118
- export function normalizeTools(value: unknown): string[] | undefined {
119
- return normalizeToolsDetailed(value).tools;
120
- }
121
-
122
52
  /**
123
53
  * The launch error for an agent whose declared tools resolve to nothing, or `undefined` when the
124
54
  * agent either declared no tools or at least one of them resolved.
@@ -146,7 +76,7 @@ function loadAgentsFromDir(dir: string, source: "package" | "user" | "project"):
146
76
 
147
77
  for (const entry of entries) {
148
78
  if (!entry.name.endsWith(".md")) continue;
149
- // Follow symlinks: the corpus adapters in .pi/agents are symlinks into .cratis/ai/agents.
79
+ // Follow symlinks too, for user agents and legacy canonical adapters.
150
80
  if (!entry.isFile() && !entry.isSymbolicLink()) continue;
151
81
 
152
82
  const filePath = path.join(dir, entry.name);
@@ -0,0 +1,77 @@
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
+ /**
5
+ * Map a Claude/Copilot tool name onto Pi's built-in tool name.
6
+ *
7
+ * Pi built-ins: read, write, edit, bash, grep, find, ls. The corpus agents use the
8
+ * Claude vocabulary (Read/Write/Edit/Bash/Grep/Glob + orchestration tools Agent/Skill/
9
+ * TodoWrite). `Glob` is Pi's `find`; the orchestration tools have no Pi built-in and are
10
+ * dropped (Pi has no separate Agent/Skill/Todo tool — subagent nesting and skills are
11
+ * reached differently). An unknown name is dropped rather than passed through, so a stray
12
+ * value can never disable Pi's tool allowlist by naming a tool that does not exist.
13
+ */
14
+ const TOOL_NAME_MAP: Record<string, string | null> = {
15
+ read: "read",
16
+ write: "write",
17
+ edit: "edit",
18
+ multiedit: "edit",
19
+ bash: "bash",
20
+ grep: "grep",
21
+ find: "find",
22
+ glob: "find",
23
+ ls: "ls",
24
+ // No Pi built-in equivalent — dropped:
25
+ agent: null,
26
+ task: null,
27
+ skill: null,
28
+ todowrite: null,
29
+ webfetch: null,
30
+ websearch: null,
31
+ };
32
+
33
+ /** Generation accepts only explicitly mapped names, never inherited JavaScript object properties. */
34
+ export function isKnownToolName(name: string): boolean {
35
+ return Object.hasOwn(TOOL_NAME_MAP, name.trim().toLowerCase());
36
+ }
37
+
38
+ /**
39
+ * The outcome of normalizing a frontmatter `tools` value.
40
+ *
41
+ * `tools` is the de-duplicated Pi allowlist, or `undefined` when the agent declared no tools at
42
+ * all (it then inherits Pi's full default toolset, exactly like an omitted `tools:` in Claude Code).
43
+ * `unresolved` lists every declared name that is neither a Pi built-in nor a known orchestration
44
+ * tool, so a caller can tell "nothing declared" apart from "declared, but nothing resolved".
45
+ */
46
+ export interface NormalizedTools {
47
+ tools?: string[];
48
+ unresolved: string[];
49
+ }
50
+
51
+ /**
52
+ * Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names.
53
+ * Accepts both YAML spellings in use (`tools: [Read, Bash]` and `tools: Read, Bash`).
54
+ */
55
+ export function normalizeToolsDetailed(value: unknown): NormalizedTools {
56
+ const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
57
+ const declared = raw
58
+ .filter((t): t is string => typeof t === "string")
59
+ .map((t) => t.trim())
60
+ .filter(Boolean);
61
+ const mapped = declared
62
+ .map((t) => t.toLowerCase())
63
+ .map((t) => (t in TOOL_NAME_MAP ? TOOL_NAME_MAP[t] : null))
64
+ .filter((t): t is string => typeof t === "string");
65
+ const unresolved = declared.filter((t) => !(t.toLowerCase() in TOOL_NAME_MAP));
66
+ const deduped = Array.from(new Set(mapped));
67
+ return { tools: deduped.length > 0 ? deduped : undefined, unresolved };
68
+ }
69
+
70
+ /**
71
+ * Normalize a frontmatter `tools` value to a de-duplicated list of Pi tool names, or `undefined`
72
+ * when nothing maps. Prefer {@link normalizeToolsDetailed} where the difference between an omitted
73
+ * list and an unresolvable one matters — it always does when the result gates a launch.
74
+ */
75
+ export function normalizeTools(value: unknown): string[] | undefined {
76
+ return normalizeToolsDetailed(value).tools;
77
+ }
@@ -171,9 +171,9 @@
171
171
  }
172
172
  ],
173
173
  "synced": {
174
- "version": "4.207.0",
174
+ "version": "4.211.1",
175
175
  "date": "2026-10-17",
176
- "by": "Cratis/AI#752"
176
+ "by": "Cratis/AI#762"
177
177
  },
178
178
  "covers": {
179
179
  "skills": [
@@ -201,7 +201,7 @@
201
201
  ],
202
202
  "rules": []
203
203
  },
204
- "notes": "Standalone 4.205.0 and byte-identical compiler contract pinned. Cratis/AI#746: Uuid ==/!= in command require and produces when (ESM v28): same-concept rule, bare Uuid against a concept, canonical literals, protected read paths in require only; PLAY0268 (different concepts, ordering, Uuid validation and concept rules, read paths in produces when) and PLAY0273 (non-canonical literal) checked against the 4.205.0 compiler, v28 selection read from the MCP executable-model view; Stage admits exact v1-v21 and v23-v26 pairs so v28 is refused STAGE-ESM-016, not probed against a Stage image; CLI 3.74.0 renders through v26. Prior: Standalone 4.204.0 and byte-identical compiler contract pinned. Cratis/AI#702 and #691: PLAY0690 reducer identity (Screenplay 4.170.0, native compiler only) and the screenplay.json validation gate (Screenplay 4.165.0; cratis screenplay validate reads it from CLI 3.65.0) are taught and probed against the 4.204.0 compiler. Cratis/AI#743: flat composite read-model keys (ESM v27): untyped projection key blocks on root from and remove with, equality-filter lists, single-instance by blocks and per-part fixtures, with PLAY0661/PLAY0662/PLAY0268 refusals checked against the 4.204.0 compiler; Stage origin/main still refuses v27 with STAGE-ESM-016 (stage-rendering.md); Stage v4.135.0 v26 admission (Host and executor run date comparisons; renderer STAGE-ESM-046) was source-checked for this sync; no render or runtime probe. Prior: Standalone 4.201.0 and byte-identical compiler contract pinned. Cratis/AI#733: the ui profile root template, shell options, appearance and new screens default (PLAY0788-PLAY0799, quick fix for the legacy layout line, ui-profiles MCP tool, 44 tools). Cratis/AI#735: chronological Date/DateTime ordering in command conditions and command/property literal validation (ESM v26), UTC tick precision and pre-rule offset validation; then matching remains ordinal. Source-checked Screenplay v4.199.0 commands, diagnostics, interoperability and specifications documentation plus decision 0087. Stage v4.127.1 still refuses v26 with STAGE-ESM-016 (stage-rendering.md and SemanticVersionFeatures.Supports); no render/runtime probe. Cratis/AI#576: prior mechanical compiler catch-up. Language batch #717/#722/#723/#724/#720/#728/#734 teaches atomic reaction batches (v22), described payload handlers (v23), portable linear matching, ordinal text rules (v24), protected command reads (v25), diagnostic arguments and event-source invokes mappings. Facts read at Screenplay v4.198.0 and introducing tags; Stage v4.127.0 admits exact v1-v21 and v23-v25 pairs but refuses v22/v26 and mixed pairs (STAGE-ESM-016), protected reads (044), atomic mode (043) and described handlers (STAGE-IMPL-002), even Stub mode. Stage text rules execute/render; no new renderer/runtime probe or Stage pin uplift. Cratis/AI#706: application navigation regions (module inherit, extend or override), toolbar slice buttons, blueprint template references and sample data packs in cratis-screenplay-ui-composition (references/application-shell.md), with the canonical shell vector and the rejection codes PLAY0710-PLAY0722 and PLAY0740-PLAY0765. Examples moved to the executable-state rules since 4.164: translator reactions in an Automation slice beside the Translate slice (PLAY0694) and the first record of a capture key appended with `when added`. Earlier: Standalone 4.164.0 and byte-identical compiler contract pinned. Cratis/AI#663: one origin per capture (PLAY0687). #668: executable no-event/redelivery v17. #684: concept query-argument validation in every version and query-error assertions v18. #681: executable refusal branches and refusal values v19. #666: proposal summary/severity counts and exact structural source proposals. Generated-concept rules select v20 with PLAY0688/0689 proofs and derived-matches repair; generated secrets and preview strategies remain refused. Release-baseline from-esm admission reads storedName. Standalone binding is not renderer admission: Stage 4.103.0 admits v1-v17 and v19; CLI 3.62.0 bundles Screenplay 4.159.0 / Stage 4.99.0 and renders through v16. Prior evidence remains unless superseded; no new renderer, Docker or browser probe."
204
+ "notes": "Cratis/AI#762 and #761: source-checked v4.210.0 and v4.211.0 contracts for PLAY0800 CommandHandlerRequiresAttachment (required typed subject, permanent code-attachment requirement, other PLAY0268 families unchanged), independent reference remove/add batches, located same-text ambiguity conflicts and string MCP conflict/move-report document fields. v4.211.1 mapping changes inspected but excluded from this guidance update at the user's request; no typed PLAY0273 property/type arguments are claimed. Standalone Screenplay 4.211.1 and its compiler contract pinned; the contract is a byte-identical copy of the v4.211.1 release asset. No new render/runtime probe. Prior: Standalone 4.205.0 and byte-identical compiler contract pinned. Cratis/AI#746: Uuid ==/!= in command require and produces when (ESM v28): same-concept rule, bare Uuid against a concept, canonical literals, protected read paths in require only; PLAY0268 (different concepts, ordering, Uuid validation and concept rules, read paths in produces when) and PLAY0273 (non-canonical literal) checked against the 4.205.0 compiler, v28 selection read from the MCP executable-model view; Stage admits exact v1-v21 and v23-v26 pairs so v28 is refused STAGE-ESM-016, not probed against a Stage image; CLI 3.74.0 renders through v26. Prior: Standalone 4.204.0 and byte-identical compiler contract pinned. Cratis/AI#702 and #691: PLAY0690 reducer identity (Screenplay 4.170.0, native compiler only) and the screenplay.json validation gate (Screenplay 4.165.0; cratis screenplay validate reads it from CLI 3.65.0) are taught and probed against the 4.204.0 compiler. Cratis/AI#743: flat composite read-model keys (ESM v27): untyped projection key blocks on root from and remove with, equality-filter lists, single-instance by blocks and per-part fixtures, with PLAY0661/PLAY0662/PLAY0268 refusals checked against the 4.204.0 compiler; Stage origin/main still refuses v27 with STAGE-ESM-016 (stage-rendering.md); Stage v4.135.0 v26 admission (Host and executor run date comparisons; renderer STAGE-ESM-046) was source-checked for this sync; no render or runtime probe. Prior: Standalone 4.201.0 and byte-identical compiler contract pinned. Cratis/AI#733: the ui profile root template, shell options, appearance and new screens default (PLAY0788-PLAY0799, quick fix for the legacy layout line, ui-profiles MCP tool, 44 tools). Cratis/AI#735: chronological Date/DateTime ordering in command conditions and command/property literal validation (ESM v26), UTC tick precision and pre-rule offset validation; then matching remains ordinal. Source-checked Screenplay v4.199.0 commands, diagnostics, interoperability and specifications documentation plus decision 0087. Stage v4.127.1 still refuses v26 with STAGE-ESM-016 (stage-rendering.md and SemanticVersionFeatures.Supports); no render/runtime probe. Cratis/AI#576: prior mechanical compiler catch-up. Language batch #717/#722/#723/#724/#720/#728/#734 teaches atomic reaction batches (v22), described payload handlers (v23), portable linear matching, ordinal text rules (v24), protected command reads (v25), diagnostic arguments and event-source invokes mappings. Facts read at Screenplay v4.198.0 and introducing tags; Stage v4.127.0 admits exact v1-v21 and v23-v25 pairs but refuses v22/v26 and mixed pairs (STAGE-ESM-016), protected reads (044), atomic mode (043) and described handlers (STAGE-IMPL-002), even Stub mode. Stage text rules execute/render; no new renderer/runtime probe or Stage pin uplift. Cratis/AI#706: application navigation regions (module inherit, extend or override), toolbar slice buttons, blueprint template references and sample data packs in cratis-screenplay-ui-composition (references/application-shell.md), with the canonical shell vector and the rejection codes PLAY0710-PLAY0722 and PLAY0740-PLAY0765. Examples moved to the executable-state rules since 4.164: translator reactions in an Automation slice beside the Translate slice (PLAY0694) and the first record of a capture key appended with `when added`. Earlier: Standalone 4.164.0 and byte-identical compiler contract pinned. Cratis/AI#663: one origin per capture (PLAY0687). #668: executable no-event/redelivery v17. #684: concept query-argument validation in every version and query-error assertions v18. #681: executable refusal branches and refusal values v19. #666: proposal summary/severity counts and exact structural source proposals. Generated-concept rules select v20 with PLAY0688/0689 proofs and derived-matches repair; generated secrets and preview strategies remain refused. Release-baseline from-esm admission reads storedName. Standalone binding is not renderer admission: Stage 4.103.0 admits v1-v17 and v19; CLI 3.62.0 bundles Screenplay 4.159.0 / Stage 4.99.0 and renders through v16. Prior evidence remains unless superseded; no new renderer, Docker or browser probe."
205
205
  },
206
206
  {
207
207
  "id": "scene",
@@ -44,7 +44,7 @@ This skill is a method: it names no API of its own. When the Screenplay skills a
44
44
  | --- | --- |
45
45
  | Where each rule lives (validator, `Provide()`, handler result, constraint, exception) | `rules/vertical-slices.md` "The decision matrix" |
46
46
  | A customization never makes a rejected model renderable; managed output is never hand-edited | Stage (`Customizations/` is the unmanaged seam; managed output is regenerated) |
47
- | Attached command handlers remain unbound (PLAY0268); pending handlers with unconditional productions bind v23 as described commands, but reached bodies are Unsupported ImplementationBody and Stage refuses STAGE-IMPL-002 even in Stub mode | Screenplay and Stage; use the model as the gap-fill contract |
47
+ | Attached file or inline command handlers are refused with PLAY0800, a permanent requirement for a code implementation attachment (Requires Screenplay 4.210.0; older binders report PLAY0268); pending handlers with unconditional productions bind v23 as described commands, but reached bodies are Unsupported ImplementationBody and Stage refuses STAGE-IMPL-002 even in Stub mode | Screenplay and Stage; use the model as the gap-fill contract |
48
48
 
49
49
  ## Procedure
50
50
 
@@ -105,8 +105,11 @@ fix to migrate. `--warnaserror` does not reject information diagnostics.
105
105
  properties, `reads`, `authorize`, `validate`, `produces`/`handler`, `concurrency`.
106
106
  Keep to it.
107
107
 
108
- A file/code `handler` cannot coexist with `produces` and still fails binding
109
- with PLAY0268. A pending `handler implementation` beside unconditional `produces`
108
+ A file/code `handler` cannot coexist with `produces` and fails binding
109
+ with PLAY0800 (`CommandHandlerRequiresAttachment`): gap-fill the slice in code
110
+ with a constrained implementation attachment, not by waiting for a new ESM version.
111
+ Read the required typed `subject` argument for the command, never parse the message.
112
+ Requires Screenplay 4.210.0 (`screenplay --version`) for PLAY0800; older binders use PLAY0268 for this refusal. A pending `handler implementation` beside unconditional `produces`
110
113
  is a described command (ESM v23): the body owns payload values only. Prefer plain
111
114
  `produces` for declarative logic. Read [described handlers](references/described-handlers.md)
112
115
  when logic needs prose, for the result contract, PLAY0695–0698, Unsupported
@@ -33,8 +33,12 @@ module Invoices
33
33
  A pending `implementation` has no `file` or fence. Bare `implementation` is valid;
34
34
  optional `hint` lines are ordered, nonblank quoted strings, decoded without trimming.
35
35
  One wrapper and at most one payload are permitted. Attaching a file or fence stops
36
- being a described command: attached handlers still fail binding with PLAY0268 and
37
- cannot coexist with `produces`.
36
+ being a described command: attached handlers fail binding with PLAY0800
37
+ (`CommandHandlerRequiresAttachment`) and cannot coexist with `produces`.
38
+ This permanent limitation requires a constrained implementation attachment:
39
+ gap-fill the slice in code, not by waiting for a supported ESM version.
40
+
41
+ Requires Screenplay 4.210.0 (`screenplay --version`) for PLAY0800 and its required typed `subject` declaration argument naming the command; older binders use PLAY0268. Hosts switch on the code and read `arguments.subject`, never parse the message.
38
42
 
39
43
  ## Result contract v1
40
44
 
@@ -57,6 +61,7 @@ explicitly admit this version, never silently drop the body.
57
61
 
58
62
  | Diagnostic | Meaning and action |
59
63
  | --- | --- |
64
+ | PLAY0800 (error) | File/inline handler requires a constrained implementation attachment; `subject` identifies the command to gap-fill |
60
65
  | PLAY0695 (information) | Described command admitted; command and RequirementId name the missing body |
61
66
  | PLAY0696 (error) | No productions, including response-only; add unconditional productions |
62
67
  | PLAY0697 (error) | Conditional productions; result v1 needs unconditional events |
@@ -79,8 +79,10 @@ validate
79
79
 
80
80
  A bodied rule or fenced block on a validation, rule or policy binds as opaque code
81
81
  (ESM v3): the reference runner reports it unsupported, and a target must supply the
82
- implementation. Attached command handlers and `file` constraints remain PLAY0268
83
- (only `unique` constraints bind). Pending command handlers beside unconditional
82
+ implementation. Attached command handlers refuse PLAY0800 and require constrained
83
+ implementation attachments; `file` constraints retain PLAY0268 (only `unique`
84
+ constraints bind). Requires Screenplay 4.210.0 (`screenplay --version`) for the
85
+ handler's PLAY0800; older binders use PLAY0268. Pending command handlers beside unconditional
84
86
  `produces` instead bind as described commands; see [described handlers](described-handlers.md).
85
87
  Literal text rules, portable patterns and Studio desugaring: [text and match rules](text-and-match-rules.md).
86
88
  Stage renders pure reducer transitions and opaque policy bodies, but refuses command and
@@ -179,5 +179,8 @@ resolved adapter version, diagnostic, retained/withheld form and open question:
179
179
  with SP0073 and dependent values/responses/mappings with SP0052; pins against them
180
180
  withhold scenarios with SP0039. Below v7 responses/return expectations are withheld.
181
181
  A cap describes less behavior, not an equivalent implementation. Handler references
182
- compile but refuse executable binding (PLAY0268); record that verdict separately.
182
+ compile but refuse executable binding (PLAY0800 for file/inline command handlers);
183
+ record that verdict separately and gap-fill the slice with a constrained implementation
184
+ attachment, not by waiting for ESM admission.
185
+ Requires Screenplay 4.210.0 (`screenplay --version`); older binders use PLAY0268 for these handlers.
183
186
  Never remove protection, required payloads or loss rows to obtain a green result.
@@ -21,12 +21,6 @@ same model root or workspace export to all checks. Workflow:
21
21
  `references/screen-authoring-workflow.md`; syntax/support:
22
22
  `cratis-screenplay-ui-composition`.
23
23
 
24
- ## Verified product sources
25
-
26
- Source: `Cratis/Screenplay`, `Documentation/screenplay/` and `Documentation/screenplay/mcp/`
27
- (notes, property planner, compliance, concept generation, release baseline). Discover
28
- installed schemas; bundles differ. Tool versions: toolchain `SKILL.md`.
29
-
30
24
  Record open modeling questions as `question` notes in committed
31
25
  `.screenplay/notes.json`, not chat or `description` text. Never put secrets or
32
26
  personal data in notes. Read [model notes](references/model-notes.md) when
@@ -147,7 +141,10 @@ Others send an edit request ([MCP loop](references/mcp-loop.md)).
147
141
  `screenplay --version`); read `references/mcp-loop.md` when choosing formatting.
148
142
  5. Keep reference policy `Safe`. Use `Draft` only for deliberately requested
149
143
  unresolved model debt and report that debt; it does not waive structural,
150
- identity or binding-protection checks.
144
+ identity or binding-protection checks. Batch independent reference removals/additions;
145
+ unmatched same-text pairs refuse as ambiguous. Preserve moves with `propose-move`
146
+ or semantic migrations. String `conflicts[].document`/`path` locate refusals:
147
+ [reference continuity and version gate](references/mcp-loop.md#reference-continuity-and-located-conflicts).
151
148
  6. Report `summary`, inspect severity errors/codes rather than counts
152
149
  (Requires Screenplay 4.156.0, `screenplay --version`); read `references/mcp-loop.md`
153
150
  when reviewing PLAY0288, comment loss or introduced executable errors. Inspect `read-proposal` before/after bytes and diagnostics, including the