@cratis/pi 7.36.1 → 7.38.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/ai-distribution.instructions.md +109 -0
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +81 -0
- package/package/corpus/harnesses/copilot/instructions/capability-is-not-authority.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.csharp.instructions.md +92 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.instructions.md +83 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.typescript.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/components.instructions.md +300 -0
- package/package/corpus/harnesses/copilot/instructions/concepts.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/cratis-folder-is-configuration.instructions.md +29 -0
- package/package/corpus/harnesses/copilot/instructions/csharp.instructions.md +357 -0
- package/package/corpus/harnesses/copilot/instructions/dialogs.instructions.md +278 -0
- package/package/corpus/harnesses/copilot/instructions/documentation-structure-and-formatting.instructions.md +162 -0
- package/package/corpus/harnesses/copilot/instructions/documentation.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/editing-cratis-docs.instructions.md +74 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.instructions.md +84 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.specs.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/elixir.instructions.md +50 -0
- package/package/corpus/harnesses/copilot/instructions/exit-codes-and-wrappers.instructions.md +34 -0
- package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +55 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-quality.instructions.md +60 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-testing.instructions.md +127 -0
- package/package/corpus/harnesses/copilot/instructions/git-commits.instructions.md +139 -0
- package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +87 -0
- package/package/corpus/harnesses/copilot/instructions/glossary.instructions.md +63 -0
- package/package/corpus/harnesses/copilot/instructions/go-engineering.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/go.instructions.md +116 -0
- package/package/corpus/harnesses/copilot/instructions/guards-and-fuses.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/java.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/kotlin.instructions.md +175 -0
- package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +56 -0
- package/package/corpus/harnesses/copilot/instructions/new-repository-intake.instructions.md +35 -0
- package/package/corpus/harnesses/copilot/instructions/orleans.instructions.md +97 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +403 -0
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +176 -0
- package/package/corpus/harnesses/copilot/instructions/react.instructions.md +203 -0
- package/package/corpus/harnesses/copilot/instructions/reactors.instructions.md +336 -0
- package/package/corpus/harnesses/copilot/instructions/rtk.instructions.md +48 -0
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +147 -0
- package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +133 -0
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +281 -0
- package/package/corpus/harnesses/copilot/instructions/specs.typescript.instructions.md +141 -0
- package/package/corpus/harnesses/copilot/instructions/storybook.instructions.md +85 -0
- package/package/corpus/harnesses/copilot/instructions/typescript.instructions.md +151 -0
- package/package/corpus/harnesses/copilot/instructions/verification-discipline.instructions.md +27 -0
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +414 -0
- package/package/corpus/harnesses/copilot/instructions/web-fetching.instructions.md +12 -0
- package/package/corpus/harnesses/copilot/instructions/writing-correct-examples.instructions.md +57 -0
- package/package/corpus/harnesses/copilot/instructions/writing-cratis-docs.instructions.md +72 -0
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +6 -0
- package/package/corpus/product-sync.json +7 -3
- package/package/corpus/rules/orleans.md +4 -0
- package/package/corpus/rules/profiles.md +3 -3
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +17 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/verification.json +12 -0
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +5 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +3 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +2 -2
- package/package/corpus/skills/cratis-components-common-controls/SKILL.md +149 -0
- package/package/corpus/skills/cratis-components-common-controls/verification.json +15 -0
- package/package/corpus/skills/cratis-components-data-surfaces/SKILL.md +185 -0
- package/package/corpus/skills/cratis-components-data-surfaces/verification.json +16 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/SKILL.md +125 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/verification.json +15 -0
- package/package/corpus/skills/cratis-orleans-jobs/SKILL.md +115 -0
- package/package/corpus/skills/cratis-orleans-jobs/references/state-machines.md +64 -0
- package/package/corpus/skills/cratis-orleans-jobs/verification.json +20 -0
- package/package/corpus/skills/cratis-screenplay-discovery/SKILL.md +11 -6
- package/package/corpus/skills/cratis-screenplay-discovery/references/facilitation.md +9 -5
- package/package/corpus/skills/cratis-screenplay-discovery/references/human-workshop.md +1 -1
- package/package/corpus/skills/cratis-screenplay-discovery/references/plotting-and-handoff.md +28 -4
- package/package/corpus/skills/cratis-screenplay-discovery/references/storming-loop.md +2 -1
- package/package/corpus/skills/cratis-screenplay-discovery/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +23 -29
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +15 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/model-notes.md +208 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +20 -1
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +19 -36
- package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +26 -0
- package/package/corpus/skills/cratis-screenplay-model-review/verification.json +7 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +14 -16
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/core-rules-digest.md +8 -3
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/handoff-template.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/intake.md +21 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/reasoning-notes.md +10 -6
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/stop-or-assume.md +32 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/worked-example.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +4 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1716 -183
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +40 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +6 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +26 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +3 -0
- package/package/profile-catalog.json +6 -2
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-fundamentals-telemetry
|
|
3
|
+
description: Instrument a .NET service with Cratis.Metrics.Roslyn generated counters, gauges, histograms and spans, and subscribe/export them through Cratis.OpenTelemetry. Use for partial instrumentation declarations, missing telemetry, host setup or metric-name upgrades. Do not use for operating a collector or for application audit records.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
8
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
9
|
+
|
|
10
|
+
# Generated metrics and traces
|
|
11
|
+
|
|
12
|
+
Use this when a service needs measurements and spans, not business facts. Keep event sourcing and audit records separate from telemetry.
|
|
13
|
+
|
|
14
|
+
## Verified product sources
|
|
15
|
+
|
|
16
|
+
All source locators below are in **Cratis/Fundamentals v7.23.0**. Read the tag, not a sibling checkout's HEAD.
|
|
17
|
+
|
|
18
|
+
| Package | Version | Source |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `Cratis.Fundamentals` | `7.23.0` | `Source/DotNET/Fundamentals/Metrics/{Counter,Gauge,Histogram}Attribute.cs`, `Traces/SpanAttribute.cs`, `Diagnostics/TagAttribute.cs` |
|
|
21
|
+
| `Cratis.Metrics.Roslyn` | `7.23.0` | `Source/DotNET/Metrics.Roslyn/MetricsSourceGenerator.cs:42-130,174-238,285-337,354-406`; `Templates/Metrics.hbs:11-123` |
|
|
22
|
+
| `Cratis.OpenTelemetry` | `7.23.0` | `Source/DotNET/OpenTelemetry/OpenTelemetryExtensions.cs:29-180,215-315`; `InstrumentationExtensions.cs:17-31`; `CratisOpenTelemetryOptions.cs:19-34` |
|
|
23
|
+
|
|
24
|
+
Registration evidence: `Source/DotNET/Fundamentals/DependencyInjection/ServiceCollectionExtensions.cs:27-54`. Unkeyed names: `Metrics/UnkeyedMeter.cs:17-23`, `Traces/UnkeyedActivitySource.cs:17-23` under the same Fundamentals source root. Upgrade context: `Documentation/csharp/metrics/roslyn.md` (Exported Metric Names) and `Documentation/csharp/opentelemetry.md`.
|
|
25
|
+
|
|
26
|
+
## 1. Declare instrumentation beside its owner
|
|
27
|
+
|
|
28
|
+
Reference `Cratis.Fundamentals` for runtime types and `Cratis.Metrics.Roslyn` for generation. Use a **static partial class in a namespace**, containing **static partial methods**. The generator's candidate filter requires static classes even for metrics; a merely partial instance class is not enough (`MetricsSourceGenerator.cs:42-53,84-93`). Prefer extension declarations with short, unqualified attribute/type names, as below.
|
|
29
|
+
|
|
30
|
+
This declaration file is complete; it assumes the application's existing `ReportWorker` class in `MyApplication.Reports`:
|
|
31
|
+
|
|
32
|
+
```csharp
|
|
33
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
34
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
35
|
+
|
|
36
|
+
using System.Diagnostics;
|
|
37
|
+
using Cratis.Diagnostics;
|
|
38
|
+
using Cratis.Metrics;
|
|
39
|
+
using Cratis.Traces;
|
|
40
|
+
|
|
41
|
+
namespace MyApplication.Reports;
|
|
42
|
+
|
|
43
|
+
public static partial class ReportInstrumentation
|
|
44
|
+
{
|
|
45
|
+
[Counter<int>("reports.completed", "Completed reports")]
|
|
46
|
+
internal static partial void Completed(this IMeter<ReportWorker> meter,
|
|
47
|
+
[Tag("reports.outcome")] string outcome);
|
|
48
|
+
|
|
49
|
+
[Gauge<int>("reports.pending", "Pending reports")]
|
|
50
|
+
internal static partial void Pending(this IMeter<ReportWorker> meter, int count);
|
|
51
|
+
|
|
52
|
+
[Histogram<double>("reports.duration", "Report duration", "s")]
|
|
53
|
+
internal static partial void Duration(this IMeter<ReportWorker> meter, double seconds);
|
|
54
|
+
|
|
55
|
+
[Span("reports.build", ActivityKind.Internal)]
|
|
56
|
+
internal static partial IActivityScope<ReportWorker> Build(
|
|
57
|
+
this IActivitySource<ReportWorker> source,
|
|
58
|
+
[Tag("reports.format")] string format);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- The first metric argument is `IMeter<T>` or `IMeterScope<T>`. A counter without a measurement increments by one. The first remaining parameter, in any position, whose type exactly matches the attribute's numeric type is the measurement, not a tag; avoid a second parameter of that type. Gauges and histograms require that measurement (`MetricsSourceGenerator.cs:285-337`).
|
|
63
|
+
- Remaining parameters are tags. Metric keys default to parameter names; span keys default to snake_case. `[Tag("key")]` from `Cratis.Diagnostics` overrides either (`:174-185,403`).
|
|
64
|
+
- `IMeterScope<T>` merges scope tags with method tags (`Templates/Metrics.hbs:34-38,67-71,96-100`). Do not put credentials, personal payloads or unbounded entity IDs in metric labels; choose bounded categories.
|
|
65
|
+
- Name, description and optional UCUM unit are distinct. A duration in seconds uses `"s"` and a measurement in seconds, not milliseconds (`HistogramAttribute.cs:14-29`, template `:109`).
|
|
66
|
+
- A span returns `IActivityScope<T>` and takes `IActivitySource<T>` first. Assign the returned scope with `using`; `CRT0001` checks disposal (`ActivityScopeUsingAnalyzer.cs:20`, `Templates/Metrics.hbs:118-122`).
|
|
67
|
+
|
|
68
|
+
Call-site excerpt inside the existing worker, with injected `IMeter<ReportWorker> meter` and `IActivitySource<ReportWorker> source`:
|
|
69
|
+
|
|
70
|
+
```csharp
|
|
71
|
+
using var activity = source.Build("pdf");
|
|
72
|
+
meter.Pending(3);
|
|
73
|
+
meter.Completed("succeeded");
|
|
74
|
+
meter.Duration(0.25);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This shows recording, not report execution. Both `AddBindingsByConvention()` and `AddSelfBindings()` register unkeyed `IMeter<>` and `IActivitySource<>` as singletons. For a plain host, use the normal type-discovery/convention setup from **cratis-fundamentals-type-discovery**; a generated method alone does not register its dependencies.
|
|
78
|
+
|
|
79
|
+
## 2. Subscribe the actual instrumentation name
|
|
80
|
+
|
|
81
|
+
**Unkeyed instruments use `typeof(T).FullName`**, not the metric name and not the host service name. `AddCratisInstrumentation()` subscribes only to `Cratis.*`. An application namespace such as `MyApplication.Reports.ReportWorker` needs an explicit meter/source subscription. This is the first check when generation works but nothing is exported.
|
|
82
|
+
|
|
83
|
+
Reference `Cratis.OpenTelemetry` explicitly for SDK/exporter setup. Host-setup excerpt for an existing `IHostApplicationBuilder builder` (including a `WebApplicationBuilder`), before building the host:
|
|
84
|
+
|
|
85
|
+
```csharp
|
|
86
|
+
using Cratis.OpenTelemetry;
|
|
87
|
+
using OpenTelemetry.Metrics;
|
|
88
|
+
using OpenTelemetry.Trace;
|
|
89
|
+
using MyApplication.Reports;
|
|
90
|
+
|
|
91
|
+
builder.AddCratisOpenTelemetry(options =>
|
|
92
|
+
{
|
|
93
|
+
options.ServiceName = "report-service";
|
|
94
|
+
options.ConfigureMetrics = metrics => metrics.AddMeter(typeof(ReportWorker).FullName!);
|
|
95
|
+
options.ConfigureTracing = tracing => tracing.AddSource(typeof(ReportWorker).FullName!);
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`AddCratisOpenTelemetry` extends `IHostApplicationBuilder`. When the host already owns an OpenTelemetry builder, use `services.AddOpenTelemetry().WithCratis(configuration, configure)` instead; `WithCratis` extends `OpenTelemetryBuilder`. Do not add both setups. If the application only wants subscription helpers, `AddCratisInstrumentation()` extends both `TracerProviderBuilder` and `MeterProviderBuilder`, not the service collection (`OpenTelemetryExtensions.cs:29-71`, `InstrumentationExtensions.cs:24-31`).
|
|
100
|
+
|
|
101
|
+
Shared setup adds Cratis sources/meters, ASP.NET Core and HTTP client instrumentation, runtime metrics, and scoped/formatted logs. Application callbacks run before DI is built (`OpenTelemetryExtensions.cs:77-180`).
|
|
102
|
+
|
|
103
|
+
## 3. Configure export, without baking endpoints or secrets into code
|
|
104
|
+
|
|
105
|
+
At this tag, OTLP export is **opt-in**: no configured endpoint means no OTLP exporter. Environment values take precedence over configuration keys. Relevant standard keys (`OpenTelemetryExtensions.cs:215-315`):
|
|
106
|
+
|
|
107
|
+
| Key | Effect |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES` | Resource values override application defaults |
|
|
110
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Shared absolute HTTP/HTTPS endpoint |
|
|
111
|
+
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, `_METRICS_ENDPOINT`, `_LOGS_ENDPOINT` | Signal-specific endpoints override the shared endpoint |
|
|
112
|
+
| `OTEL_EXPORTER_OTLP_PROTOCOL` (or signal-specific equivalent) | `http/protobuf` selects HTTP; otherwise gRPC |
|
|
113
|
+
| `OTEL_TRACES_EXPORTER`, `OTEL_METRICS_EXPORTER`, `OTEL_LOGS_EXPORTER` | If supplied, must include `otlp` for that signal to export |
|
|
114
|
+
| `OTEL_SDK_DISABLED=true` | Disables providers |
|
|
115
|
+
| `OTEL_METRIC_EXPORT_INTERVAL`, `OTEL_METRIC_EXPORT_TIMEOUT` | Metric reader timing in milliseconds |
|
|
116
|
+
| `OTEL_TRACES_SAMPLER`, `OTEL_TRACES_SAMPLER_ARG` | Sampler and ratio configuration |
|
|
117
|
+
|
|
118
|
+
For HTTP/protobuf, the shared endpoint gains `/v1/traces`, `/v1/metrics` or `/v1/logs`; signal-specific endpoints are used as supplied. An enabled invalid endpoint throws `InvalidOpenTelemetryEndpoint`. Treat exporter headers as secrets; obtain them through the approved environment/secret mechanism, never a snippet or committed configuration.
|
|
119
|
+
|
|
120
|
+
## Upgrade and prove it
|
|
121
|
+
|
|
122
|
+
- Fundamentals stopped bringing the OpenTelemetry SDK transitively in **7.20.2**; add the explicit telemetry package when migrating a consumer that relied on that dependency (`Documentation/csharp/opentelemetry.md`).
|
|
123
|
+
- Regeneration after the **7.20.0** unit correction can rename exported Prometheus metrics: descriptions are now HELP text, not units. Rebuild the consumer and update dashboards/alerts that used the old description-suffixed names (`metrics/roslyn.md`, template `:48-50,80,109`).
|
|
124
|
+
- Build with analyzers and warnings as errors. Inspect generated implementations for correct measurements/tags and scope disposal. Do not edit generated files.
|
|
125
|
+
- Exercise one code path with a listener or the configured collector: observe the counter increment, histogram unit/measurement and completed span. A build or `skill-contains` assertion is not proof of export. Check namespace subscriptions, SDK disablement, sampler and endpoint independently before diagnosing the collector.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "cratis-fundamentals-telemetry",
|
|
3
|
+
"input": "Generate service metrics and spans and diagnose why an application's unkeyed meter is not exported.",
|
|
4
|
+
"fixtures": [],
|
|
5
|
+
"assertions": [
|
|
6
|
+
{ "kind": "skill-contains", "value": "static partial class in a namespace" },
|
|
7
|
+
{ "kind": "skill-contains", "value": "Histogram<double>" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "IActivityScope<ReportWorker>" },
|
|
9
|
+
{ "kind": "skill-contains", "value": "CRT0001" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "typeof(T).FullName" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "AddCratisOpenTelemetry" },
|
|
12
|
+
{ "kind": "skill-contains", "value": "no configured endpoint means no OTLP exporter" },
|
|
13
|
+
{ "kind": "skill-contains", "value": "descriptions are now HELP text, not units" }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-orleans-jobs
|
|
3
|
+
description: Build recoverable work with Cratis.Orleans jobs and job-step grains, stage barriers, bounded startup and cleanup, storage scopes and state machines. Use for service or kernel job authors, job recovery, SQL schema setup, or grain lifecycle transitions. Framework guidance; do not use for operating live Chronicle jobs or for application command validation.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
8
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
9
|
+
|
|
10
|
+
# Recoverable Orleans jobs
|
|
11
|
+
|
|
12
|
+
For services and kernel contributors using Orleans. This is library-consumer guidance, not the application vertical-slice architecture. For live Chronicle operations use **cratis-chronicle-cli-operations** instead. Co-hosting conventions are in [orleans.md](../../rules/orleans.md).
|
|
13
|
+
|
|
14
|
+
## Verified product sources
|
|
15
|
+
|
|
16
|
+
All Orleans claims below are source reads from **Cratis/Orleans v1.16.3**.
|
|
17
|
+
|
|
18
|
+
| Packages | Version | Sources |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `Cratis.Orleans`, `Cratis.Orleans.Storage` | `1.16.3` | `Source/Orleans/Jobs/{Job.cs,Job.Steps.cs,Job.Stages.cs,JobStep.cs,IJobsManager.cs,JobsOptions.cs}`; `Source/Orleans.Storage/Jobs/*` |
|
|
21
|
+
| `Cratis.Orleans.Storage.MongoDB`, `Cratis.Orleans.Storage.Sql` | `1.16.3` | `Source/Orleans.Storage.MongoDB/{MongoDBJobsStorageOptions.cs,DatabaseNames.cs}`; `Source/Orleans.Storage.Sql/{SqlJobsStorage.cs,SqlJobsStorageOptions.cs,Jobs/Migrations/v1_0_0.cs}` |
|
|
22
|
+
| `Cratis.Orleans.Chronicle` | `1.16.3` | `Source/Orleans.Chronicle/StateMachines/EventSourcedStateMachine.cs:19-40,77-94,128-176`; [state-machine reference](references/state-machines.md) |
|
|
23
|
+
|
|
24
|
+
The API snippets cite their exact files and lines below. Product docs `Documentation/concepts/{jobs,storage-scopes,state-machines,event-sourced-state-machines}.md` supply the task vocabulary; source resolves contradictions.
|
|
25
|
+
|
|
26
|
+
## 1. Choose a job, not a detached task
|
|
27
|
+
|
|
28
|
+
A job plans recoverable work; each step is a grain and does the work. Use it for work that must outlive a request. It is **at-least-once**, not exactly-once: a step interrupted before a durable checkpoint can perform the same work again. Protect external effects with receiver-owned idempotency/deduplication; a checkpoint does not atomically commit an external write (`JobStep.cs:47-56`, `JobsOptions.cs:34-59`).
|
|
29
|
+
|
|
30
|
+
- Job: derive `Job<TRequest, TJobState>`; the request is a class implementing `IJobRequest`, the state derives `JobState` (`Job.cs:23-25`). Implement `PrepareSteps(TRequest)` returning `Task<IImmutableList<JobStepDetails>>` (`Job.Steps.cs:63-87`).
|
|
31
|
+
- Step: derive `JobStep<TRequest, TResult, TState>`; state derives `JobStepState`. Its constructor receives `[PersistentState(nameof(JobStepState), WellKnownGrainStorageProviders.JobSteps)] IPersistentState<TState>`, `IJobStepThrottle`, and `ILogger<JobStep<TRequest, TResult, TState>>` (`JobStep.cs:27-32`).
|
|
32
|
+
- Implement `PrepareStep(TRequest)` → `Task<Result<PrepareJobStepError>>`; `InitializeState(TRequest)` → `ValueTask`, called after preparation succeeds; and `PerformStep(TState, CancellationToken)` → `Task<Catch<JobStepResult>>` (`JobStep.cs:350-387`). `TResult` is the step's result type; the work method returns the framework's `JobStepResult` envelope, not `TResult` directly.
|
|
33
|
+
- Give the concrete job and step unique `[GrainType("product-specific-name")]` names. Use serializable request/state/result types. Do not reuse a class-derived grain name across assemblies (`Jobs/JobsManager.cs:31`, `Documentation/how-to/host-the-silo.md`, and `orleans.md`).
|
|
34
|
+
|
|
35
|
+
`PerformStep` runs **outside the activation context** with a state snapshot obtained through the grain reference. Mutating that snapshot does not persist it; do not access/write grain `State` directly from background work (`JobStep.cs:375,390-450`). Keep progress/state coordination on grain calls. Since 1.16.2 the framework owns stop reporting; do not report cancellation again from a `PerformWork` override (`:390-450`, `Documentation/concepts/jobs.md:117-120`).
|
|
36
|
+
|
|
37
|
+
## 2. Plan dependencies as stages
|
|
38
|
+
|
|
39
|
+
Default steps all use `JobStepStage.First`. Prefer independent fan-out. Use stages only when correctness requires earlier work to finish (`Job.Stages.cs:20-44`).
|
|
40
|
+
|
|
41
|
+
Planning excerpt inside an existing `Job<RecoveryRequest, RecoveryState>`. It assumes `RecoveryRequest : IJobRequest`, `RecoveryState : JobState`, and the two existing grain interfaces `IRepairIndex` and `IRebuildCatalog`, each deriving a typed `IJobStep<,,>`:
|
|
42
|
+
|
|
43
|
+
```csharp
|
|
44
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
45
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
46
|
+
|
|
47
|
+
protected override Task<IImmutableList<JobStepDetails>> PrepareSteps(RecoveryRequest request) =>
|
|
48
|
+
Task.FromResult<IImmutableList<JobStepDetails>>(
|
|
49
|
+
[
|
|
50
|
+
CreateStep<IRepairIndex>(request, stage: 0),
|
|
51
|
+
CreateStep<IRebuildCatalog>(request, stage: 10)
|
|
52
|
+
]);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Import `System.Collections.Immutable` and `Cratis.Orleans.Jobs` in that job file. `CreateStep<TStep>` takes the **grain interface**, not the implementation (`Job.Steps.cs:69-79`). Its stage overload accepts `JobStepStage`, with implicit integer conversion (`Source/Orleans.Storage/Jobs/Stages/JobStepStage.cs`).
|
|
56
|
+
|
|
57
|
+
Every step in a stage reaches an outcome before later stages start; steps within a stage run in parallel. All steps are prepared up front, so later-stage preparation cannot read the effects of earlier-stage execution. Read such dependencies in `PerformStep` (`Job.Stages.cs:27-33`).
|
|
58
|
+
|
|
59
|
+
A failed stage stops the pipeline by default: later steps become `Unreachable`, and the job completes with failures. A best-effort stage may override `GetFailureBehaviorFor(JobStepStage)` to return `JobStageFailureBehavior.ContinueWithNextStage` (`Job.Stages.cs:40-44,152-194`, namespace `Cratis.Orleans.Jobs.Stages`). Do not silently continue when a later stage relies on the failed output.
|
|
60
|
+
|
|
61
|
+
## 3. Start and resume through the scoped manager
|
|
62
|
+
|
|
63
|
+
`IGrainFactory.GetJobsManager(scope, namespace)` resolves the manager (`JobsManagerExtensions.cs:18-19`). `IJobsManager.Start<TJob,TRequest>(request)` returns `Result<JobId, StartJobError>`; inspect its error before telling a caller that a job started (`IJobsManager.cs:37-52`). Start success is not completed work; observe the job's status/outcomes.
|
|
64
|
+
|
|
65
|
+
`IJobsManager.Resume(JobId)` returns **`Task<bool>`**. False means nothing took the job; do not wait for a completion callback as though resumption succeeded. True can mean resumed, already running, or finalized as completed (`IJobsManager.cs:54-64`). `Rehydrate()` completes after loading/preparing interrupted jobs, **before** its bounded background drain has resumed them all (`:15-28`).
|
|
66
|
+
|
|
67
|
+
`StartStepsInBackground` defaults to false. Overriding it to true makes Start return once the job is durable, moving step preparation/start to the grain's next turn. Later startup errors belong to the job, not the Start response (`Job.cs:74-87,395-427`).
|
|
68
|
+
|
|
69
|
+
## 4. Know what storage retains
|
|
70
|
+
|
|
71
|
+
Successful jobs clear themselves by default; override `KeepAfterCompleted` to retain job state. Jobs completed with failures default to `KeepAfterCompletedWithFailures = true`, but retention cleanup still applies (`Job.cs:58-71`, `JobsOptions.cs:80-97`).
|
|
72
|
+
|
|
73
|
+
**Keeping a job does not keep successful step state.** The step provider deletes successful/removing state, moves failed state to failed-step storage, and saves stopped/scheduled/running/unreachable state (`JobStepGrainStorageProvider.cs:64-84`). SQL keeps failed step rows with a failure status rather than a separate collection (`Source/Orleans.Storage.Sql/Jobs/JobStepStorage.cs`, `MoveToFailed`). Copy results the job must retain through `OnStepCompletedOrStopped(JobStepId, JobStepResult)` into job state (`Job.Steps.cs:49-55,110-117`); do not read deleted successful step state later.
|
|
74
|
+
|
|
75
|
+
Deliver external callbacks to a method on the **job's grain interface**, update job state there and persist it with `WriteState()`. Preserve the returned `Catch` failure; do not acknowledge input whose write failed (`Job.State.cs:13-25`, `WriteState`; `Documentation/concepts/jobs.md:122-164`). Step state is not a callback inbox: a concurrent callback can race the final write, and successful completion deletes it. A running step's snapshot will not see later job/step writes automatically.
|
|
76
|
+
|
|
77
|
+
## 5. Register storage and budget concurrency
|
|
78
|
+
|
|
79
|
+
`IJobsStorage.GetFor(scope, namespace)` returns `JobsStorage`. Scope and namespace are opaque engine coordinates carried in grain keys; the host/provider owns their mapping (`Source/Orleans.Storage/Jobs/IJobsStorage.cs`, `Job.cs:120-125`). They are storage boundaries, not authorization checks.
|
|
80
|
+
|
|
81
|
+
Choose one storage registration alongside `AddCratisOrleans`:
|
|
82
|
+
|
|
83
|
+
| Registration | Package / namespace | Use |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `AddCratisOrleansMongoDBJobsStorage` | `.Storage.MongoDB` / `Cratis.Orleans.Setup` | Durable MongoDB; database-name resolver can override the default `{scope}+jobs[+{namespace}]` |
|
|
86
|
+
| `AddCratisOrleansSqlJobsStorage` | `.Storage.Sql` / `Cratis.Orleans.Setup` | Durable EF Core; `OptionsResolver(scope, namespace)` selects typed `DbContextOptions<JobsDbContext>` |
|
|
87
|
+
| `AddCratisOrleansInMemoryJobsStorage` | `.Storage` / `Cratis.Orleans.Storage` | Specs/development only; process restart loses state |
|
|
88
|
+
|
|
89
|
+
Evidence: each package's `*JobsStorageServiceCollectionExtensions.cs`; MongoDB `DatabaseNames.cs` and `MongoDBJobsStorageOptions.cs`; SQL `SqlJobsStorageOptions.cs:15-27`; in-memory `InMemory/InMemoryJobsStorage.cs`.
|
|
90
|
+
|
|
91
|
+
### SQL schema contradiction resolved
|
|
92
|
+
|
|
93
|
+
The product's SQL setup docs say to create the schema yourself. **At v1.16.3 the provider applies migrations automatically**: `SqlJobsStorage.GetFor` calls `EnsureSchema`, which calls `dbContext.Database.Migrate()` before constructing storage (`Source/Orleans.Storage.Sql/SqlJobsStorage.cs:33-49,58-74`). The migration is `Jobs/Migrations/v1_0_0.cs`; do not substitute `EnsureCreated` or hand-create those tables. The connection needs permissions to apply the migration, and scope-to-database mapping must be deliberate. Storage is cached per scope/namespace; `IJobsStorage.Reset()` clears the cache and the next resolution migrates again (`SqlJobsStorage.cs:52-56`). This resolves the documentation conflict from source, not from a release-note inference.
|
|
94
|
+
|
|
95
|
+
Configure `JobsOptions` through the host's options registration. Its properties are **init-only**: supply a constructed instance via the options mechanism rather than assigning them in a `Configure(options => options.Property = …)` callback (`JobsOptions.cs`).
|
|
96
|
+
|
|
97
|
+
| Option | Default | Purpose |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `MaxParallelSteps` | `max(1, ProcessorCount - 1)` when unset | Actual execution throttle |
|
|
100
|
+
| `MaxConcurrentStepStarts` | `16` | Startup/resume calls per job, not the execution limit |
|
|
101
|
+
| `MaxConcurrentRehydration` | `8` | Jobs resumed concurrently by a manager |
|
|
102
|
+
| `MaxConcurrentCleanup` | `4` | Dead-job deletions |
|
|
103
|
+
| `StepCheckpointBatchInterval` | `100` | Batches between durable progress checkpoints |
|
|
104
|
+
| `StepCheckpointFlushInterval` | `5 seconds` | Timed checkpoint flush; nonpositive disables it |
|
|
105
|
+
| `DeadJobThreshold`, `CleanupCadence` | `1 hour` each | Preparation-with-no-steps scavenging |
|
|
106
|
+
| `TerminalJobRetention` | `7 days` | Terminal history retention; `Timeout.InfiniteTimeSpan` retains indefinitely |
|
|
107
|
+
| `MaxTerminalJobsPerCleanup` | `100` | Bounded oldest-first cleanup page |
|
|
108
|
+
|
|
109
|
+
Source: `JobsOptions.cs:17-109`. The three concurrency options for starts/rehydration/cleanup treat values below one as one. Retention does not delete preparing/running/stopped/removing jobs. Larger checkpoint intervals mean more post-crash redelivery, not higher delivery guarantees.
|
|
110
|
+
|
|
111
|
+
## State machines and verification
|
|
112
|
+
|
|
113
|
+
Use [state-machines.md](references/state-machines.md) for `StateMachine<TStoredState>` and `EventSourcedStateMachine<TModel>`: jobs are recoverable work, state machines are lifecycle behavior. Neither makes external effects exactly-once.
|
|
114
|
+
|
|
115
|
+
Verify a new job with specs for planning, idempotent redelivery, stop/resume (including false), failed barriers/Unreachable, retention and persistence failures. Exercise the selected durable provider for restart recovery and SQL migration permissions. A build alone cannot establish crash recovery. Do not operate a live store merely to validate a code example.
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
# Grain lifecycle state machines
|
|
5
|
+
|
|
6
|
+
Verified from **Cratis/Orleans v1.16.3**. Use explicit states when entering/leaving a status has behavior; an enum is enough for a status with no behavior. Do not put long-running work on a grain turn just because `OnEnter` is available.
|
|
7
|
+
|
|
8
|
+
## Stored lifecycle
|
|
9
|
+
|
|
10
|
+
`StateMachine<TStoredState>` derives from a stateful grain. Override `InitialState` and `CreateStates()`; states derive `State<TStoredState>`. `AllowedTransitions` is an immutable list of target types, or override `CanTransitionTo<TTargetState>(storedState)` for a guard (`Source/Orleans/StateMachines/StateMachine.cs:63-67,120-192`; `State.cs:17-34`).
|
|
11
|
+
|
|
12
|
+
Declaration excerpt for an existing grain file with `ProcessState : StateMachineState`, the existing `IProcess` grain interface, and a registered `processes` grain storage provider:
|
|
13
|
+
|
|
14
|
+
```csharp
|
|
15
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
16
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
17
|
+
|
|
18
|
+
using System;
|
|
19
|
+
using System.Collections.Immutable;
|
|
20
|
+
using System.Threading.Tasks;
|
|
21
|
+
using Cratis.Orleans.StateMachines;
|
|
22
|
+
using Orleans;
|
|
23
|
+
using Orleans.Providers;
|
|
24
|
+
|
|
25
|
+
[GrainType("my-product-process")]
|
|
26
|
+
[StorageProvider(ProviderName = "processes")]
|
|
27
|
+
public class Process : StateMachine<ProcessState>, IProcess
|
|
28
|
+
{
|
|
29
|
+
protected override Type InitialState => typeof(Pending);
|
|
30
|
+
|
|
31
|
+
public override IImmutableList<IState<ProcessState>> CreateStates() =>
|
|
32
|
+
[new Pending(), new Finished()];
|
|
33
|
+
|
|
34
|
+
public Task Finish() => TransitionTo<Finished>();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public class Pending : State<ProcessState>
|
|
38
|
+
{
|
|
39
|
+
protected override IImmutableList<Type> AllowedTransitions => [typeof(Finished)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public class Finished : State<ProcessState>;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Split types into their house-style files in the consumer; the block collects declarations to show the API. Do not copy the `processes` provider name into Chronicle's kernel; use its registered constants.
|
|
46
|
+
|
|
47
|
+
A refused transition is a **no-op**, not an exception or a successful change. An unknown target throws `UnknownStateTypeInStateMachine`. The machine runs leaving hooks, `OnLeave`, entering hooks, `OnEnter`, then persistence (`StateMachine.cs:162-169,222-290`). Transitions requested while entering are scheduled; transitions while leaving throw `TransitioningDuringOnLeaveIsNotSupported`.
|
|
48
|
+
|
|
49
|
+
When stored state derives `StateMachineState`, `CurrentState` stores the state's full type name; activation resolves it, enters it again, and can repeat enter effects. Renames need a stored-value migration; an unknown recorded name throws `UnknownCurrentState` (`StateMachine.cs:183-191,261-264`). A self-transition with the **same state instance** skips persistence: return a new stored-state instance when a self-transition must write (`:266-272`).
|
|
50
|
+
|
|
51
|
+
For externally owned storage, the protected constructor accepts `Orleans.Core.IStorage<TStoredState>` and reads it on activation before the activation hook. Override `ResolveActivationState()` to derive lifecycle from authoritative state instead of persisting a second status (`:39-51,68-95,172-192`).
|
|
52
|
+
|
|
53
|
+
## Lifecycle following Chronicle facts
|
|
54
|
+
|
|
55
|
+
`EventSourcedStateMachine<TModel>` from `Cratis.Orleans.Chronicle.StateMachines` uses a Chronicle read model as authoritative state. Its interface derives `IEventSourcedStateMachine` (string-keyed). There is **no grain storage provider** to configure and writes are no-ops: `ReadModelStorage<TModel>` reads Chronicle; `ResolveState(TModel?)` derives activation state (`Source/Orleans.Chronicle/StateMachines/EventSourcedStateMachine.cs:19-40,77-94,155-176`; `ReadModelStorage.cs:68-95`).
|
|
56
|
+
|
|
57
|
+
- Define states with `CreateStates()` as above, over `TModel`.
|
|
58
|
+
- Define event transitions in `DefineTransitions(IStateMachineTransitions<TModel>)`, using `.On<TEvent>().When(model => predicate).TransitionTo<TState>()`. First applicable allowed transition wins. Make `ResolveState` agree with these transitions after restart (`EventSourcedStateMachine.cs:142-176`, `StateMachineTransitions.cs`).
|
|
59
|
+
- Use model-bound projection attributes, an already-discovered projection, or `DefineProjection(IProjectionBuilderFor<TModel>)`, not competing projections. `DefineProjection` and `DefineTransitions` run at startup **without a constructor**: use only the builder, never injected services or fields (`EventSourcedStateMachine.cs:34-37`).
|
|
60
|
+
- Register `AddEventSourcedStateMachines()` on `ISiloBuilder` or `IServiceCollection`, beside the application's Chronicle client registration; namespace `Cratis.Orleans.Chronicle.Hosting` (`Hosting/EventSourcedStateMachinesServiceCollectionExtensions.cs:30-55`).
|
|
61
|
+
- Grain keys include event store, namespace and event-source ID. Use `IGrainFactory.GetEventSourcedStateMachine<TInterface>(eventStore, eventSourceId)` from `Cratis.Orleans.Chronicle.StateMachines` so tenant coordinates come from the supplied event store (`StateMachines/EventSourcedStateMachineGrainFactoryExtensions.cs:21-52`).
|
|
62
|
+
- Prefer passive models. For events consumed by a materialized projection, the reactor waits for its sequence position before refreshing; `ModelCatchUpTimeout` defaults to 30 seconds and expiry fails with `ModelDidNotCatchUp` (`EventSourcedStateMachineReactor.cs:58-89`; `EventSourcedStateMachinesOptions.cs:26`).
|
|
63
|
+
|
|
64
|
+
This machine **follows facts**, it does not arbitrate whether a command may append one. Keep command rejection and append-time invariants at their owning boundary. Enter/model-change effects may repeat on activation, redelivery or replay: design them idempotently (`Documentation/concepts/event-sourced-state-machines.md:240-247`). Test guards, no-op refusals, reactivation and persistence failures separately from job recovery.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "cratis-orleans-jobs",
|
|
3
|
+
"input": "Author a staged recoverable job with SQL storage and distinguish lifecycle state machines from job execution.",
|
|
4
|
+
"fixtures": [],
|
|
5
|
+
"assertions": [
|
|
6
|
+
{ "kind": "skill-contains", "value": "at-least-once" },
|
|
7
|
+
{ "kind": "skill-contains", "value": "Task<Catch<JobStepResult>>" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "CreateStep<IRepairIndex>(request, stage: 0)" },
|
|
9
|
+
{ "kind": "skill-contains", "value": "Unreachable" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "Task<bool>" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "Keeping a job does not keep successful step state" },
|
|
12
|
+
{ "kind": "skill-contains", "value": "dbContext.Database.Migrate()" },
|
|
13
|
+
{ "kind": "skill-contains", "value": "MaxConcurrentStepStarts" },
|
|
14
|
+
{ "kind": "skill-contains", "value": "StateMachine<TStoredState>" },
|
|
15
|
+
{ "kind": "skill-contains", "value": "EventSourcedStateMachine<TModel>" },
|
|
16
|
+
{ "kind": "skill-contains", "file": "references/state-machines.md", "value": "A refused transition is a **no-op**" },
|
|
17
|
+
{ "kind": "skill-contains", "file": "references/state-machines.md", "value": "without a constructor" },
|
|
18
|
+
{ "kind": "skill-contains", "file": "references/state-machines.md", "value": "same state instance" }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -44,7 +44,9 @@ syntax and tool commands in `cratis-screenplay-toolchain`.
|
|
|
44
44
|
|
|
45
45
|
Current pins are listed once in `cratis-screenplay-toolchain` `references/versions.md`. The
|
|
46
46
|
statements below were checked at these older tags (Screenplay v4.66.0 `c89198b`, cratis CLI v3.28.3,
|
|
47
|
-
Stage v4.24.2, Arc v22.50.5, Chronicle v19.32.0), kept as provenance, not as current pins
|
|
47
|
+
Stage v4.24.2, Arc v22.50.5, Chronicle v19.32.0), kept as provenance, not as current pins.
|
|
48
|
+
Model notes use Screenplay `v4.144.0:Documentation/screenplay/model-notes.md:12-77`
|
|
49
|
+
and decision 0045:40-79; tool sources: model-authoring `references/model-notes.md`.
|
|
48
50
|
|
|
49
51
|
| Fact used here | Source |
|
|
50
52
|
| --- | --- |
|
|
@@ -87,10 +89,12 @@ With no input at all, open with: "Describe the starting situation, then the step
|
|
|
87
89
|
to reach the intended result." With a brief, restate domain and goal in two sentences and confirm.
|
|
88
90
|
|
|
89
91
|
**Unattended: assume visibly.** Never stop to ask. Take the most reasonable assumption,
|
|
90
|
-
record
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
record the open question and assumption as a `question` note anchored to the element,
|
|
93
|
+
not chat or `description` text. STATE.md carries its note ID and assumption for handoff;
|
|
94
|
+
settled reasoning belongs in `documentation`. Keep decisions apart from assumptions.
|
|
95
|
+
Read [discovery questions](references/plotting-and-handoff.md#durable-discovery-questions)
|
|
96
|
+
when recording or answering a question: `refs`, external decisions and realization.
|
|
97
|
+
Never put secrets or personal data in note text; `.screenplay/notes.json` is committed.
|
|
94
98
|
|
|
95
99
|
## Procedure
|
|
96
100
|
|
|
@@ -209,7 +213,8 @@ and the report says which verdicts were not run (V2 to V5 are not run in discove
|
|
|
209
213
|
Cannot lines or "none known"; every Cannot line is listed as a denial candidate.
|
|
210
214
|
- Essential fields present; personal data classified on concepts.
|
|
211
215
|
- Rules register, denial candidates, competition and duplicate candidates, hotspots, open
|
|
212
|
-
questions and assumptions are
|
|
216
|
+
questions and assumptions are carried in STATE.md; open questions are also committed
|
|
217
|
+
`question` notes with anchors, not only said in chat.
|
|
213
218
|
|
|
214
219
|
## Verify
|
|
215
220
|
|
|
@@ -19,8 +19,9 @@ that decision. If not, note the need as a view and move on.
|
|
|
19
19
|
|
|
20
20
|
- When people describe the same step differently, first check whether they describe different
|
|
21
21
|
situations (two branches, two roles). Often both are right.
|
|
22
|
-
- If it is a real disagreement,
|
|
23
|
-
|
|
22
|
+
- If it is a real disagreement, record both versions in an anchored `question`
|
|
23
|
+
note; carry its note ID and working assumption in STATE.md. Say which assumption
|
|
24
|
+
you use and continue. Do not stall the session on it.
|
|
24
25
|
- Domain experts decide business meaning; engineers decide feasibility. When only engineers
|
|
25
26
|
are present, mark business-meaning decisions as assumptions to confirm.
|
|
26
27
|
|
|
@@ -37,12 +38,15 @@ that decision. If not, note the need as a view and move on.
|
|
|
37
38
|
|
|
38
39
|
## Unattended runs
|
|
39
40
|
|
|
40
|
-
- Never block on a question. Take
|
|
41
|
-
|
|
41
|
+
- Never block on a question during discovery. Take a visible working assumption
|
|
42
|
+
and record it with its reason in an anchored `question` note, not chat or
|
|
43
|
+
`description` text. STATE.md carries the note ID.
|
|
42
44
|
- Prefer assumptions that keep the model honest (fewer, clearly named facts) over ones that
|
|
43
45
|
invent detail.
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
Read `plotting-and-handoff.md` "Durable discovery questions" when recording or
|
|
48
|
+
resolving a note with project decision `refs`. Never put secrets or personal data
|
|
49
|
+
in notes. See "Evidence and privacy" in SKILL.md.
|
|
46
50
|
|
|
47
51
|
## Closing a session
|
|
48
52
|
|
|
@@ -80,7 +80,7 @@ No event modeling experience is needed.
|
|
|
80
80
|
| Yellow: role | `persona` (with Does / Reads / Cannot) |
|
|
81
81
|
| Blue: command (later session) | `command` (`cratis-screenplay-slice-design`) |
|
|
82
82
|
| Green: view (later session) | `readmodel` and `query` (`cratis-screenplay-slice-design`) |
|
|
83
|
-
| Pink: question or hotspot |
|
|
83
|
+
| Pink: question or hotspot | anchored `question` note (note ID in STATE.md); hotspot in STATE.md |
|
|
84
84
|
|
|
85
85
|
Use the agreed colours consistently, whatever they are; the mapping is what matters.
|
|
86
86
|
|
package/package/corpus/skills/cratis-screenplay-discovery/references/plotting-and-handoff.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- 1. Sequence
|
|
6
6
|
- 2. Record after / caused by / only if
|
|
7
7
|
- 3. Ask what can happen instead
|
|
8
|
+
- Durable discovery questions
|
|
8
9
|
- 4. Plot output format
|
|
9
10
|
- Whose turn is it in the story?
|
|
10
11
|
- Storyline: one view walked through its states
|
|
@@ -61,6 +62,27 @@ Name every terminal state (completed, cancelled, expired, withdrawn) and every u
|
|
|
61
62
|
correction path. Each path with a way out feeds removal specs later (an ending removes the
|
|
62
63
|
thing from the views that listed it).
|
|
63
64
|
|
|
65
|
+
## Durable discovery questions
|
|
66
|
+
|
|
67
|
+
Every unsettled branch, dead end or assumption becomes a `question` note on the
|
|
68
|
+
affected element, not chat or `description` text. Commit `.screenplay/notes.json`
|
|
69
|
+
with the model. STATE.md and the plot report carry the returned note ID and
|
|
70
|
+
assumption, not the only copy of the question. Before a specific element exists,
|
|
71
|
+
anchor to the application or nearest identified container and name it in text.
|
|
72
|
+
Never put secrets or personal data in note text, author, refs or resolutions.
|
|
73
|
+
|
|
74
|
+
Read model-authoring [model notes](../../cratis-screenplay-model-authoring/references/model-notes.md)
|
|
75
|
+
when recording or resolving discovery questions: `list-model-notes`,
|
|
76
|
+
`propose-model-notes`, `read-proposal view=notes`, then authorized `apply`.
|
|
77
|
+
A missing notes capability is a reported gap plus a note request, not a successful
|
|
78
|
+
recording. For project decisions, cite `refs: [{ "id": "D123" }]`; resolve with
|
|
79
|
+
`by: "external-decision"` and `ref: { "id": "D123" }` when that record answers it.
|
|
80
|
+
Turn the answer into a specification, a description or a project-owned decision
|
|
81
|
+
record. A resolution alone implements nothing; carry behavioral answers to slice
|
|
82
|
+
design/scenario coverage. Reopen if the decision stops answering the question.
|
|
83
|
+
Evidence: Screenplay `v4.144.0:Documentation/screenplay/model-notes.md:12-77`,
|
|
84
|
+
`decisions/0045-model-notes-sidecar.md:40-79`; tool-source lines in the linked guide.
|
|
85
|
+
|
|
64
86
|
## 4. Plot output format
|
|
65
87
|
|
|
66
88
|
Put a compact story line in the feature `documentation` and report the plot per workflow:
|
|
@@ -78,11 +100,13 @@ The plot: SeasonalBerths
|
|
|
78
100
|
Critical path: 1 -> 2 -> 3 -> 4
|
|
79
101
|
Decision points: after 1 (assign or decline); after 2 (arrive or withdraw)
|
|
80
102
|
Terminal facts: BerthRequestDeclined, BoatDeparted, BerthAssignmentWithdrawn
|
|
81
|
-
Wait states: after 2, until the boat arrives (what if it never does?
|
|
82
|
-
Compensation: withdrawal
|
|
103
|
+
Wait states: after 2, until the boat arrives (report Q1: what if it never does?)
|
|
104
|
+
Compensation: report Q2: does withdrawal free the berth for the waiting list?
|
|
83
105
|
```
|
|
84
106
|
|
|
85
|
-
|
|
107
|
+
Q1 and Q2 are report shorthand for anchored `question` notes; do not copy
|
|
108
|
+
unanswered questions into the feature's `documentation`. The settled plot goes
|
|
109
|
+
there. For a diagram use `cratis-event-model-diagram`.
|
|
86
110
|
|
|
87
111
|
## Whose turn is it in the story?
|
|
88
112
|
|
|
@@ -145,7 +169,7 @@ What discovery contributes:
|
|
|
145
169
|
| Non-human causes | schedules, outside parties, own follow-up |
|
|
146
170
|
| Hotspots | where it goes wrong today |
|
|
147
171
|
| Set aside | non-events kept as view needs |
|
|
148
|
-
| Open questions | the blocking one first |
|
|
172
|
+
| Open questions | anchored note IDs and assumptions; the blocking one first |
|
|
149
173
|
| Decisions and assumptions | kept apart; each assumption with its reason |
|
|
150
174
|
| Verdicts | V1 result with tool and version; V2 to V5 "not run: discovery phase" |
|
|
151
175
|
|
|
@@ -122,6 +122,7 @@ module Moorings
|
|
|
122
122
|
reason String
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
Record anchored `question` notes for these uncertainties, carrying their note IDs
|
|
126
|
+
in STATE.md: is a declined request final or can it be
|
|
126
127
|
put on a waiting list; who may withdraw an assignment; must the season fee be remembered as
|
|
127
128
|
set at assignment time (a decision, so a fact) or recalculated (a view)?
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
"fixtures": [],
|
|
5
5
|
"assertions": [
|
|
6
6
|
{ "kind": "play-compiles" },
|
|
7
|
+
{ "kind": "skill-contains", "value": "record the open question and assumption as a `question` note" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "`refs: [{ \"id\": \"D123\" }]`", "file": "references/plotting-and-handoff.md" },
|
|
9
|
+
{ "kind": "skill-contains", "value": "`by: \"external-decision\"`", "file": "references/plotting-and-handoff.md" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "Turn the answer into a specification", "file": "references/plotting-and-handoff.md" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "Never put secrets or personal data", "file": "references/plotting-and-handoff.md" },
|
|
7
12
|
{ "kind": "skill-contains", "value": "## Interview phase" },
|
|
8
13
|
{ "kind": "skill-contains", "value": "Unattended: assume visibly" },
|
|
9
14
|
{ "kind": "skill-contains", "value": "Does / Reads / Cannot" },
|
|
@@ -21,7 +21,8 @@ same model root or workspace export to all checks. Workflow:
|
|
|
21
21
|
|
|
22
22
|
## Verified product sources
|
|
23
23
|
|
|
24
|
-
Screenplay v4.
|
|
24
|
+
Screenplay v4.144.0: model notes sources/lines in `references/model-notes.md`;
|
|
25
|
+
v4.142.0 add/rename/remove planner in `references/property-links.md`;
|
|
25
26
|
compliance in command-surface `references/compliance.md`. Other MCP evidence:
|
|
26
27
|
v4.139.0 `Documentation/screenplay/mcp/`.
|
|
27
28
|
Discover installed tools and schemas; CLI bundles can differ from the standalone
|
|
@@ -29,9 +30,15 @@ tool. Pins are in `cratis-screenplay-toolchain` `references/versions.md`.
|
|
|
29
30
|
Historical probes remain 4.66.0; registration example compilation/execution
|
|
30
31
|
remains 4.68.0. Attribution: `references/provenance.md`.
|
|
31
32
|
|
|
33
|
+
Record open modeling questions as `question` notes in committed
|
|
34
|
+
`.screenplay/notes.json`, not chat or `description` text. Never put secrets or
|
|
35
|
+
personal data in notes. Read [model notes](references/model-notes.md) when
|
|
36
|
+
listing, recording or resolving questions: `list-model-notes`,
|
|
37
|
+
`propose-model-notes`, `read-proposal view=notes`, refs and orphan review.
|
|
38
|
+
|
|
32
39
|
## Locate and connect
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
Use the declared `screenplay` MCP server (for example `open-workspace`).
|
|
35
42
|
|
|
36
43
|
Never ask the user which folder to use before trying. Call `open-workspace` with no
|
|
37
44
|
arguments first: a server started without a fixed root binds the folder the host or
|
|
@@ -139,7 +146,9 @@ and applies; others send an edit request ([MCP loop](references/mcp-loop.md)).
|
|
|
139
146
|
unresolved model debt and report that debt; it does not waive structural,
|
|
140
147
|
identity or binding-protection checks.
|
|
141
148
|
6. Inspect `read-proposal` before/after bytes and diagnostics, including the
|
|
142
|
-
identity-state change through `workspace-state`.
|
|
149
|
+
identity-state change through `workspace-state`. Review `orphanedNotes` on
|
|
150
|
+
source proposals; reanchor or resolve affected questions before applying
|
|
151
|
+
([notes and orphan impact](references/model-notes.md#source-proposals-can-orphan-notes)).
|
|
143
152
|
7. `apply` only the reviewed server-produced proposal within the user's requested
|
|
144
153
|
change. The proposal ID and before revisions are required; stale state rejects.
|
|
145
154
|
When the user asked for the change to be applied ("apply it when you're done",
|
|
@@ -152,23 +161,11 @@ Node handles are revision-bound occurrences, not durable IDs. A logical module
|
|
|
152
161
|
or feature can have multiple physical fragments. Do not edit one header and
|
|
153
162
|
assume every fragment changed. After apply, fetch fresh handles/revisions.
|
|
154
163
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
| `ProposalRejected` | Read `conflicts`; change the request |
|
|
161
|
-
| `FormattingConsentRequired` | Pass `formatting` |
|
|
162
|
-
| `UnknownProposal` | The connection was reopened or the proposal applied: re-propose |
|
|
163
|
-
| `LimitExceeded` | Discard unused proposals (16 cap) |
|
|
164
|
-
| `PendingOperation`, `RecoveryRequired` | `workspace-state`, then ask before `recover-workspace` |
|
|
165
|
-
| `RootChangeRefused` | Keep the fixed root, or start a new connection |
|
|
166
|
-
| `ApplyOutcomeUnknown`, or EOF during `apply` | **Never retry `apply`.** Reconnect, read `workspace-state`, ask the user |
|
|
167
|
-
|
|
168
|
-
Never delete `.screenplay/pending.json`. Limits reject and never truncate: 512
|
|
169
|
-
files, 8 MiB total, 2 MiB per file, 1 MiB structured result, 200 items or 192 KiB
|
|
170
|
-
per page. Excluded directories: `.git`, `.ai-work`, `.screenplay`, `bin`, `obj`,
|
|
171
|
-
`node_modules`.
|
|
164
|
+
Read [failures and limits](references/mcp-loop.md#failures-use-failurekind-never-message-text)
|
|
165
|
+
when a call refuses or paging hits a limit; use `failureKind`, never message text.
|
|
166
|
+
After revision/disk/identity drift, reopen and re-propose. For `ApplyOutcomeUnknown`
|
|
167
|
+
or EOF during `apply`, **never retry apply**: reconnect, inspect `workspace-state`
|
|
168
|
+
and ask. Never delete `.screenplay/pending.json`. Limits reject, never truncate.
|
|
172
169
|
|
|
173
170
|
Choose formatting explicitly and disclose dropped comments before applying;
|
|
174
171
|
read [formatting and limits](references/mcp-loop.md#formatting-and-limits) for
|
|
@@ -267,13 +264,11 @@ retry the same input.
|
|
|
267
264
|
|
|
268
265
|
## Code attachments
|
|
269
266
|
|
|
270
|
-
|
|
271
|
-
`file`
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
that the code compiles or behaves. Read `references/mcp-tools.md#code-attachments` when resolving an attachment or diagnosing attachment warnings. Path resolution and `PLAY0430`-`PLAY0434`:
|
|
276
|
-
[MCP tool guide](references/mcp-tools.md#code-attachments).
|
|
267
|
+
Code bodies and `file` constraints are opaque attachments. Screenplay never compiles or runs them.
|
|
268
|
+
A screen's `file` realizes UI, not an attachment. Read
|
|
269
|
+
[MCP code attachments](references/mcp-tools.md#code-attachments) when resolving
|
|
270
|
+
paths or diagnosing `PLAY0430`-`PLAY0434`; `read-workspace`/`read-proposal`
|
|
271
|
+
`view: "implementation-requirements"` hashes/maps are not behavioral evidence.
|
|
277
272
|
|
|
278
273
|
## Verify and report honestly
|
|
279
274
|
|
|
@@ -306,8 +301,7 @@ Record screen MCP transcripts in `.ai-work/`; render/runtime checks must use the
|
|
|
306
301
|
same source, never an app that silently drops authored UI. Use the Screenplay-owned canonical
|
|
307
302
|
corpus fixture path instead of copying available `.play` fixtures here.
|
|
308
303
|
|
|
309
|
-
|
|
310
|
-
commands, parameters, syntax nodes or downstream capabilities.
|
|
304
|
+
Never invent commands, parameters, syntax nodes or downstream capabilities.
|
|
311
305
|
|
|
312
306
|
## Route near misses
|
|
313
307
|
|