@cratis/pi 7.129.0 → 7.131.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/package/corpus/agents/coordinator.md +1 -1
  2. package/package/corpus/agents/orchestrator.md +1 -1
  3. package/package/corpus/agents/planner.md +1 -1
  4. package/package/corpus/harnesses/copilot/instructions/browser.instructions.md +4 -2
  5. package/package/corpus/harnesses/copilot/instructions/csharp.instructions.md +2 -2
  6. package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +1 -1
  7. package/package/corpus/harnesses/copilot/instructions/orleans.instructions.md +2 -1
  8. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -3
  9. package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +1 -1
  10. package/package/corpus/harnesses/pi/agents/coordinator.md +1 -1
  11. package/package/corpus/harnesses/pi/agents/orchestrator.md +1 -1
  12. package/package/corpus/harnesses/pi/agents/planner.md +1 -1
  13. package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +1 -1
  14. package/package/corpus/hooks/README.md +17 -5
  15. package/package/corpus/hooks/pre-commit.md +2 -2
  16. package/package/corpus/hooks/scripts/cratis-quality-gate.sh +1 -0
  17. package/package/corpus/hooks/scripts/cratis-run-specs.sh +147 -0
  18. package/package/corpus/hooks/scripts/quality-gates.json +5 -7
  19. package/package/corpus/rules/browser.md +4 -2
  20. package/package/corpus/rules/csharp.md +2 -2
  21. package/package/corpus/rules/framework.md +1 -1
  22. package/package/corpus/rules/general.md +2 -2
  23. package/package/corpus/rules/orleans.md +2 -1
  24. package/package/corpus/rules/pull-requests.md +2 -3
  25. package/package/corpus/rules/specs.csharp.md +1 -1
  26. package/package/corpus/skills/binlog-generation/SKILL.md +1 -1
  27. package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +1 -1
  28. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +1 -1
  29. package/package/corpus/skills/cratis-arc-command-execution/references/command-refusals.md +1 -1
  30. package/package/corpus/skills/cratis-arc-command-execution/references/occurrence-commands.md +1 -1
  31. package/package/corpus/skills/cratis-arc-command-operation/references/testing.md +1 -1
  32. package/package/corpus/skills/cratis-arc-command-validation/references/input-binding.md +1 -1
  33. package/package/corpus/skills/cratis-arc-command-validation/references/validation-severity.md +1 -1
  34. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/immediate-append-refusal.md +1 -1
  35. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/occurrences.md +1 -1
  36. package/package/corpus/skills/cratis-chronicle-event-constraints/references/storage-uniqueness.md +1 -1
  37. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +1 -1
  38. package/package/corpus/skills/cratis-chronicle-event-type-migration/references/explicit-migrations.md +1 -1
  39. package/package/corpus/skills/cratis-chronicle-reactor/references/starting-and-replaying.md +1 -1
  40. package/package/corpus/skills/cratis-chronicle-read-model/references/collection-observation.md +1 -1
  41. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +1 -1
  42. package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +1 -1
  43. package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +7 -3
  44. package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +1 -1
  45. package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +1 -1
  46. package/package/corpus/skills/cratis-commits-and-pull-requests/references/pr-labels-and-release.md +2 -2
  47. package/package/corpus/skills/cratis-engineering-ci-gates/SKILL.md +17 -6
  48. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +5 -1
  49. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +17 -8
  50. package/package/corpus/skills/cratis-engineering-release-trains/SKILL.md +37 -23
  51. package/package/corpus/skills/cratis-engineering-review-rounds/SKILL.md +24 -10
  52. package/package/corpus/skills/cratis-engineering-review-rounds/verification.json +4 -2
  53. package/package/corpus/skills/cratis-orchestration-lanes/references/context-handoff.md +1 -1
  54. package/package/corpus/skills/cratis-orchestration-lanes/references/lane-brief.md +2 -2
  55. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/delivery-protocol.md +2 -2
  56. package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +4 -3
  57. package/package/corpus/skills/cratis-screenplay-toolchain/references/fast-path.md +1 -1
  58. package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +1 -1
  59. package/package/corpus/skills/cratis-specifications-csharp/references/running-specs.md +7 -3
  60. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +2 -1
  61. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/render-example.md +1 -1
  62. package/package/corpus/skills/csharp-refactoring/SKILL.md +2 -2
  63. package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +2 -1
  64. package/package/corpus/skills/filter-syntax/SKILL.md +1 -1
  65. package/package/corpus/skills/msbuild/references/build-perf-baseline.md +4 -1
  66. package/package/corpus/skills/run-tests/SKILL.md +9 -0
  67. package/package.json +1 -1
@@ -139,7 +139,7 @@ When handing off to a specialist agent:
139
139
  For implementation, the applicable changed-lane gates must pass. Mark unrelated entries not applicable; this list is not a full-repository command mandate:
140
140
 
141
141
  - [ ] `dotnet build` — zero errors, zero warnings
142
- - [ ] `dotnet test` — all specs pass
142
+ - [ ] Built C# spec executables — all specs pass; `dotnet test <project> --no-build` only for VSTest (see specs.csharp)
143
143
  - [ ] `yarn lint` — zero errors (if frontend present)
144
144
  - [ ] `npx tsc -b` — zero TypeScript errors (if frontend present)
145
145
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
@@ -165,7 +165,7 @@ When handing off to any agent or sub-orchestrator:
165
165
  For implementation, the applicable changed-lane gates must pass. Mark unrelated entries not applicable; this list is not a full-repository command mandate:
166
166
 
167
167
  - [ ] `dotnet build` — zero errors, zero warnings
168
- - [ ] `dotnet test` — all specs pass
168
+ - [ ] Built C# spec executables — all specs pass; `dotnet test <project> --no-build` only for VSTest (see specs.csharp)
169
169
  - [ ] `yarn lint` — zero errors (if frontend present)
170
170
  - [ ] `npx tsc -b` — zero TypeScript errors (if frontend present)
171
171
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
@@ -133,7 +133,7 @@ For an implemented application slice, require the applicable changed-lane gates
133
133
  - [ ] `dotnet build` succeeds with zero errors and zero warnings
134
134
  - [ ] `yarn lint` passes with zero errors (if frontend is present)
135
135
  - [ ] `npx tsc -b` passes with zero errors (if frontend is present)
136
- - [ ] All integration specs pass (`dotnet test`)
136
+ - [ ] All integration specs pass (built C# spec executables; `dotnet test <project> --no-build` only for VSTest; see specs.csharp)
137
137
  - [ ] All TypeScript specs pass (`yarn test`) if applicable
138
138
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
139
139
  - [ ] `Documentation/verify-markdown.sh` passes when documentation is added or changed
@@ -16,8 +16,10 @@ Authority in [`general.md`](../../../rules/general.md). Read this before any bro
16
16
  request or an applicable policy names that exact action. A configured browser tool is not
17
17
  a permit.
18
18
  - **[contract] One production tab per session.** No parallel polling or parallel findings on
19
- production. Run the health check first, close idle browsers, and look for stale tabs from
20
- other sessions before opening one.
19
+ production. Run the health check first and close your own idle pages before opening one.
20
+ - **[contract] Close only what you opened.** Promptly close the browser backend you
21
+ started; if no shutdown tool exists, close your pages and report it. Subagents do the
22
+ same. Never touch a user's or shared session; no `pkill`/`killall`. One browser per lane.
21
23
  - **[convention] Prefer staging or a local copy** for interactive and visual smoke runs.
22
24
  - **[contract] Only the `browser-operator` agent receives browser tools.** Hand it the URL,
23
25
  scope, permit and artifact directory. The main session does not do browser work itself
@@ -13,8 +13,8 @@ The goal is minimal ceremony, maximum clarity. Modern C# (13+) gives us records,
13
13
  ## Building
14
14
 
15
15
  - Use `dotnet build` from the command line.
16
- - Use `dotnet format` to format code.
17
- - Use `dotnet test` to run tests.
16
+ - Follow `.editorconfig`; run `dotnet format` only when CI requires it.
17
+ - Run affected spec executables; use `dotnet test <project> --no-build` only for VSTest (see `specs.csharp.md`).
18
18
 
19
19
  ## Formatting
20
20
 
@@ -60,7 +60,7 @@ Repo conventions follow from this: `Source/DotNET` + `Source/JavaScript` for dua
60
60
  ## Quality gates (framework)
61
61
 
62
62
  - Build clean (Debug and Release) with **zero warnings, zero errors**.
63
- - Specs pass for affected projects (C# via `dotnet test`; TS packages via their test command; Components also `build-storybook`).
63
+ - Specs pass for affected projects (C# spec executables (VSTest only: `dotnet test <project> --no-build`); TS packages via their test command; Components also `build-storybook`).
64
64
  - For public-facing changes (APIs, attributes, generated output, component props): update the product documentation and verify it.
65
65
  - Match the repo's existing patterns; when a deep architectural question isn't answered by the universal rules or this file, consult the **repo's own docs/CONTRIBUTING** or ask — do not infer framework internals from a single call site.
66
66
 
@@ -63,7 +63,8 @@ using Microsoft.AspNetCore.Builder;
63
63
  using MongoDB.Driver;
64
64
 
65
65
  var builder = WebApplication.CreateBuilder(args);
66
- const string connectionString = "mongodb://localhost:27017";
66
+ var connectionString = builder.Configuration.GetConnectionString("Mongo")
67
+ ?? throw new InvalidOperationException("Configure the Mongo connection string.");
67
68
  builder.AddCratisOrleans(new CratisOrleansOptions
68
69
  {
69
70
  ClusterId = "my-service",
@@ -32,6 +32,5 @@ applyTo: "**/*"
32
32
  ## Before you create or edit a PR
33
33
 
34
34
  - Run `node .cratis/ai/hooks/scripts/cratis-check-pr.mjs --body-file <file> --label <intent>`; it applies the exact `policy / verify` release-note rules and the Bash hook runs it on `gh pr create` and `gh pr edit`. If it fails, fix the description, not the code. Before every body edit, run `git diff --stat origin/main...HEAD` and delete bullets the diff no longer supports.
35
- - Run the affected-project gates before marking work ready (`dotnet build`/`dotnet test` with zero warnings, `yarn lint`, `npx tsc -b`), and the repository's required CI checks after an authorized push. `no-release` and documentation-only PRs still satisfy every required check; none is a blanket exemption from red CI.
36
- - Group small related changes into one branch and one PR; see the skill for consolidation and the release-action windows.
37
- - Open the PR once, when verified: each push starts CI. One PR per body of work, opened by the orchestrator (`cratis-engineering-release-trains`), with `--assignee @me`.
35
+ - Run the affected-project gates before marking work ready (build, built specs, lint and type-check; zero warnings), and the repository's required CI checks after an authorized push. `no-release` and documentation-only PRs still satisfy every required check; none is a blanket exemption from red CI.
36
+ - The release train is the PR: fold finished branches into the repository's one train (see the skill). Open it once, when verified, as the orchestrator (`cratis-engineering-release-trains`) with `--assignee @me`. Split only for a named major, an urgent security fix authorized in writing, or an accepted outsider issue (patch or minor, own release).
@@ -50,7 +50,7 @@ public class when_combining_parts : Specification
50
50
 
51
51
  The shared lifecycle is the default on xUnit v2 and v3 since 5.0; the package registers the framework, so delete any hand-written `[assembly: Xunit.TestFramework(...)]` (it fails with `CS0579`). Eligible classes derive from `Specification`, have one public constructor with no arguments or only framework-managed fixture arguments, and contain only plain facts. Construction, Establish/Because and Destroy run once; all facts share the instance and must not mutate it. `IDisposable` specifications also share the lifecycle since Cratis.Specifications 4.4.0: one construction and one `Dispose` after the last fact. Initialization failure fails every fact. Theories and `ITestOutputHelper` constructors keep per-fact execution. Keep shared lifecycle on; `[PerFactSpecificationLifecycle]` opts out only the class whose facts genuinely mutate shared state and cannot move the mutation into `Because`. `CRSPEC0008` (a fact modifies the shared context) is an error by default: move the change into `Establish` or `Because`. See the **cratis-specifications-csharp** skill's shared-lifecycle reference.
52
52
 
53
- Run specifications as the built executable, not `dotnet test`: restore and build once, then run `bin/<Config>/net10.0/<Project>.Specs`. In a solution that mixes Microsoft.Testing.Platform and VSTest projects, run each project on its own; `dotnet test` on the whole solution does not work there. Filters and the 4.x → 5.0 checklist are in the **cratis-specifications-csharp** skill's [running-specs reference](../../../skills/cratis-specifications-csharp/references/running-specs.md).
53
+ Run xUnit v3, TUnit and native-runner specifications as each project's built executable: restore and build once, then run its matching configuration/TFM output. Only identified VSTest projects use `dotnet test <project> --no-build`. Never default to solution-wide `dotnet test` in a mixed suite. Filters and the 4.x → 5.0 checklist are in the **cratis-specifications-csharp** skill's [running-specs reference](../../../skills/cratis-specifications-csharp/references/running-specs.md).
54
54
 
55
55
  CRSPEC0001–CRSPEC0007 are warnings. Since 4.5 the analyzers ship only in `Cratis.Specifications`, transitively used by the framework packages; excluding analyzers on a framework package does not remove them. Derived xUnit/NUnit test attributes are recognized since 4.1.2. `Given`/`When` are aliases since 4.4 (declaring both aliases for one phase is CRSPEC0009), but house style stays `Establish`/`Because`. With warnings as errors, a non-`should_` fact, multiple specs per file (given contexts exempt), a fact on a given context, a `should_` without a test attribute, explicit base lifecycle calls, non-public specs with facts, or `Because` on a given context fail the build. Follow **cratis-specification-by-example**; do not suppress these diagnostics.
56
56
 
@@ -135,7 +135,7 @@ When handing off to a specialist agent:
135
135
  For implementation, the applicable changed-lane gates must pass. Mark unrelated entries not applicable; this list is not a full-repository command mandate:
136
136
 
137
137
  - [ ] `dotnet build` — zero errors, zero warnings
138
- - [ ] `dotnet test` — all specs pass
138
+ - [ ] Built C# spec executables — all specs pass; `dotnet test <project> --no-build` only for VSTest (see specs.csharp)
139
139
  - [ ] `yarn lint` — zero errors (if frontend present)
140
140
  - [ ] `npx tsc -b` — zero TypeScript errors (if frontend present)
141
141
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
@@ -161,7 +161,7 @@ When handing off to any agent or sub-orchestrator:
161
161
  For implementation, the applicable changed-lane gates must pass. Mark unrelated entries not applicable; this list is not a full-repository command mandate:
162
162
 
163
163
  - [ ] `dotnet build` — zero errors, zero warnings
164
- - [ ] `dotnet test` — all specs pass
164
+ - [ ] Built C# spec executables — all specs pass; `dotnet test <project> --no-build` only for VSTest (see specs.csharp)
165
165
  - [ ] `yarn lint` — zero errors (if frontend present)
166
166
  - [ ] `npx tsc -b` — zero TypeScript errors (if frontend present)
167
167
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
@@ -129,7 +129,7 @@ For an implemented application slice, require the applicable changed-lane gates
129
129
  - [ ] `dotnet build` succeeds with zero errors and zero warnings
130
130
  - [ ] `yarn lint` passes with zero errors (if frontend is present)
131
131
  - [ ] `npx tsc -b` passes with zero errors (if frontend is present)
132
- - [ ] All integration specs pass (`dotnet test`)
132
+ - [ ] All integration specs pass (built C# spec executables; `dotnet test <project> --no-build` only for VSTest; see specs.csharp)
133
133
  - [ ] All TypeScript specs pass (`yarn test`) if applicable
134
134
  - [ ] Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
135
135
  - [ ] `Documentation/verify-markdown.sh` passes when documentation is added or changed
@@ -394,7 +394,7 @@ export default function (pi: ExtensionAPI) {
394
394
  const last = latestGateLog(gateLogDirectory(sessionId), started);
395
395
  const tail = last ? tailLines(last.file, TAIL_LINES) : "";
396
396
  throw new Error(`The Cratis quality gate TIMED OUT after ${duration(timeoutSeconds * 1000)}${last ? ` while running '${last.gate}'${last.cwd ? ` (cwd: ${last.cwd})` : ""}` : ""}. This is not a pass: nothing was verified.` +
397
- (last ? `\nRun a narrower command instead: ${last.gate === "backend-specs" ? "dotnet test <affected.Specs.csproj-or-Tests.csproj> --configuration Debug --no-build (replace the placeholder; repeat for affected dependents)." : "run the affected project alone using this gate's configured command."}\nConfigure it in .cratis/ai/quality-gates.project.json.\n\nGate log: ${last.file}` : "") +
397
+ (last ? `\nRun a narrower command instead: ${last.gate === "backend-specs" ? "./<affected-project-dir>/bin/Debug/<TFM>/<AssemblyName> (or dotnet <TargetPath.dll> without apphost); dotnet test <project> --configuration Debug --no-build only for VSTest. Repeat for affected dependents." : "run the affected project alone using this gate's configured command."}\nConfigure it in .cratis/ai/quality-gates.project.json.\n\nGate log: ${last.file}` : "") +
398
398
  (tail ? `\n\n--- last ${TAIL_LINES} lines ---\n${tail}\n--- end ---` : ""));
399
399
  };
400
400
  const reportInterrupted = () => {
@@ -636,17 +636,29 @@ the script stops the command's process group (TERM, then KILL after one second),
636
636
  reports **NOT VERIFIED**, the gate, its directory, log tail, and `timeoutHint` (advice, never executed).
637
637
  Commands that create separate sessions/process groups can escape this cleanup; this is not a sandbox.
638
638
 
639
- For a large solution, replace the placeholder below with the affected test project and repeat for
640
- its affected dependents. Override `command` with that narrower argv array (or a repository-owned
641
- runner covering all affected projects), rather than raising the timeout without diagnosing the delay:
639
+ The default runs `cratis-run-specs.sh` through the absolute `CRATIS_HOOKS_SCRIPTS_DIR`
640
+ exported by the gate, including nested solutions and bundled Pi installations. The runner
641
+ requires Node, Bash and Git, the same prerequisites as the hooks (not an additional runtime). It evaluates
642
+ MSBuild metadata without building, runs supported built xUnit v3/TUnit/native executables
643
+ (or `dotnet <TargetPath.dll>` without apphost), and uses project-scoped `dotnet test --no-build`
644
+ only for identified VSTest projects. Restore/build matching Debug outputs first. Missing or
645
+ ambiguous metadata/output, custom application entrypoints, failed suites and zero-selected
646
+ suites are not passes; unsupported runners need a repository-owned invocation override.
647
+
648
+ For a large solution, replace the placeholder below with the affected project directory,
649
+ TFM and AssemblyName and repeat for affected dependents. Use that executable's filter/report
650
+ flags (see [running-specs](../skills/cratis-specifications-csharp/references/running-specs.md)),
651
+ not VSTest logger switches. Override `command` rather than raising a timeout without diagnosis:
642
652
 
643
653
  ```json
644
- { "gates": [{ "id": "backend-specs", "command": ["dotnet", "test", "<affected.Specs.csproj>", "--configuration", "Debug", "--no-build"], "timeoutSeconds": 120, "timeoutHint": "Run dotnet test for each affected Specs/Tests project separately." }] }
654
+ { "gates": [{ "id": "backend-specs", "command": ["./<project-dir>/bin/Debug/<TFM>/<AssemblyName>"], "timeoutSeconds": 120, "timeoutHint": "Run each affected project's built spec executable separately; dotnet test <project> --configuration Debug --no-build only for VSTest." }] }
645
655
  ```
646
656
 
647
657
  This is not automatic affected-project selection: imported/conditional MSBuild references and
648
658
  linked or shared sources require evaluation to establish coverage safely. The default retains
649
- solution-wide tests rather than silently missing dependents, and a timeout is never a pass.
659
+ all solution projects, executing each test project independently rather than silently missing
660
+ dependents. Non-test projects are explicitly skipped; a solution with none reports no verified
661
+ specifications. A timeout is never a pass. Reports are local under `.ai-work/spec-runs/`.
650
662
 
651
663
  **Profile note.** The C# patterns are application-profile and scoped to `Source/**/*.cs`, which is
652
664
  the application source root [`../rules/general.md`](../rules/general.md) documents. A
@@ -26,9 +26,9 @@ Apply before an authorized `git commit`, including `rtk git commit` or `rtk prox
26
26
 
27
27
  3. **Run specs for each affected .NET project**:
28
28
  ```
29
- dotnet test <specs-project-path> --no-build
29
+ ./<project-dir>/bin/<Config>/<TFM>/<AssemblyName>
30
30
  ```
31
- Use `--no-build` only when matching build outputs are current; otherwise incrementally build the affected specs project first. If the owning specs project cannot be identified, inspect project references or report the uncertainty; do not default to a root-wide test run.
31
+ Use the project's verified built spec executable (or `dotnet <TargetPath.dll>` without apphost; see [running-specs](../skills/cratis-specifications-csharp/references/running-specs.md)). Only VSTest uses `dotnet test <project> --no-build`. Require current matching outputs; otherwise incrementally build the affected specs project first. If the owning specs project cannot be identified, inspect project references or report the uncertainty; do not default to a root-wide test run.
32
32
 
33
33
  4. **Run specs for each affected TypeScript project**:
34
34
  ```
@@ -32,6 +32,7 @@ fi
32
32
 
33
33
  root="$(hook_repo_root)"
34
34
  here="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
35
+ export CRATIS_HOOKS_SCRIPTS_DIR="$here"
35
36
  config="${CRATIS_HOOKS_GATES:-$here/quality-gates.json}"
36
37
  dryrun="${CRATIS_HOOKS_GATE_DRYRUN:-0}"
37
38
 
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env bash
2
+ # Copyright (c) Cratis. All rights reserved.
3
+ # Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+
5
+ # Read-only MSBuild evaluation followed by built runners. Children inherit the gate's process group.
6
+ exec node --input-type=module - "$@" <<'NODE'
7
+ import { spawnSync } from 'node:child_process';
8
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync } from 'node:fs';
9
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
10
+ import { parseArgs } from 'node:util';
11
+
12
+ function refuse(message) {
13
+ console.error(`cratis-run-specs: ${message}`);
14
+ process.exit(1);
15
+ }
16
+
17
+ const { values } = parseArgs({ args: process.argv.slice(2), options: {
18
+ project: { type: 'string' }, solution: { type: 'string' }, configuration: { type: 'string', default: 'Debug' },
19
+ framework: { type: 'string' }, 'no-build': { type: 'boolean' },
20
+ } });
21
+ if (!values['no-build']) refuse('--no-build is required; restore and build once before running specs.');
22
+ if (values.project && values.solution) refuse('Select either --project or --solution, not both.');
23
+
24
+ function dotnet(arguments_) {
25
+ const result = spawnSync('dotnet', arguments_, { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
26
+ if (result.error || result.status !== 0) refuse(`Metadata evaluation failed: dotnet ${arguments_.join(' ')}\n${result.error?.message ?? result.stderr}`);
27
+ return result.stdout;
28
+ }
29
+
30
+ const properties = 'IsTestProject,IsTestingPlatformApplication,UseMicrosoftTestingPlatformRunner,OutputType,TargetPath,TargetFramework,TargetFrameworks,AssemblyName,XunitAutoGeneratedEntryPoint';
31
+ function evaluate(project, framework) {
32
+ const arguments_ = ['msbuild', project, '-nologo', `-p:Configuration=${values.configuration}`];
33
+ if (framework) arguments_.push(`-p:TargetFramework=${framework}`);
34
+ arguments_.push(`-getProperty:${properties}`, '-getItem:PackageReference');
35
+ try {
36
+ const metadata = JSON.parse(dotnet(arguments_));
37
+ if (!metadata.Properties || !Array.isArray(metadata.Items?.PackageReference)) throw new Error('Missing evaluated Properties or PackageReference items');
38
+ return metadata;
39
+ } catch (error) {
40
+ refuse(`Cannot evaluate ${project}: ${error.message}. Restore/build the matching configuration first; configure a repository-owned gate override for unsupported projects.`);
41
+ }
42
+ }
43
+
44
+ let projects;
45
+ if (values.project) projects = [resolve(values.project)];
46
+ else {
47
+ const solutions = values.solution ? [values.solution] : readdirSync('.').filter(name => /\.slnx?$/.test(name));
48
+ if (solutions.length !== 1) refuse('Expected one solution in the working directory; select --solution or --project explicitly in a gate override.');
49
+ const solution = resolve(solutions[0]);
50
+ // Ignore localized headings; accept only actual project entries, relative to the selected solution.
51
+ projects = dotnet(['sln', solution, 'list']).split(/\r?\n/).map(line => line.trim())
52
+ .filter(line => /\.(?:cs|fs|vb)proj$/i.test(line)).map(line => resolve(dirname(solution), line.replaceAll('\\', '/')));
53
+ if (!projects.length) refuse(`No project entries could be established for ${solution}; no specifications verified.`);
54
+ }
55
+
56
+ function classify(metadata, project) {
57
+ const properties = metadata.Properties;
58
+ const packages = new Set(metadata.Items.PackageReference.map(item => String(item.Identity).toLowerCase()));
59
+ const platform = properties.IsTestingPlatformApplication?.toLowerCase() === 'true';
60
+ const xunit = ['xunit.v3', 'xunit.v3.core', 'xunit.v3.mtp-off', 'xunit.v3.core.mtp-off',
61
+ 'xunit.v3.mtp-v1', 'xunit.v3.core.mtp-v1', 'xunit.v3.mtp-v2', 'xunit.v3.core.mtp-v2']
62
+ .some(name => packages.has(name));
63
+ const native = packages.has('cratis.specifications.runner');
64
+ const tunit = packages.has('tunit');
65
+ const legacy = ['xunit', 'nunit', 'mstest.testframework'].some(name => packages.has(name));
66
+ const test = properties.IsTestProject?.toLowerCase() === 'true' || platform || xunit || native || tunit || legacy;
67
+ if (!test) return 'non-test';
68
+ if (properties.XunitAutoGeneratedEntryPoint?.toLowerCase() === 'false') refuse(`${project}: custom application entrypoint; configure a repository-owned spec invocation override (do not start the server).`);
69
+ if (['exe', 'winexe'].includes(properties.OutputType?.toLowerCase())) {
70
+ if (xunit && (packages.has('xunit.v3.mtp-off') || packages.has('xunit.v3.core.mtp-off')
71
+ || properties.UseMicrosoftTestingPlatformRunner?.toLowerCase() !== 'true')) return 'xunit-mtp-off';
72
+ if (platform || xunit || native || tunit) return 'mtp';
73
+ }
74
+ // Test.Sdk can generate an Exe entrypoint for legacy VSTest projects too.
75
+ if (properties.IsTestProject?.toLowerCase() === 'true' && !platform && !xunit && !native && !tunit && legacy && packages.has('microsoft.net.test.sdk')) return 'vstest';
76
+ refuse(`${project}: unsupported/unknown test runner; configure a repository-owned backend-specs override instead of falling back to dotnet test.`);
77
+ }
78
+
79
+ let reportRoot;
80
+ function reportPath() {
81
+ if (!reportRoot) {
82
+ const repository = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' });
83
+ const root = repository.status === 0 ? repository.stdout.trim() : process.cwd();
84
+ const artifacts = join(root, '.ai-work', 'spec-runs');
85
+ mkdirSync(artifacts, { recursive: true });
86
+ reportRoot = mkdtempSync(join(artifacts, 'run-'));
87
+ }
88
+ return join(reportRoot, `${executed}.trx`);
89
+ }
90
+
91
+ function verifyCounts(runner, output, report, project) {
92
+ let total;
93
+ let passed;
94
+ let failed;
95
+ let skipped;
96
+ if (runner === 'xunit-mtp-off' && existsSync(report)) {
97
+ const counters = /<(?:\w+:)?Counters\b([^>]*)\/?\s*>/.exec(readFileSync(report, 'utf8'))?.[1];
98
+ const count = name => { const value = new RegExp(`\\b${name}="(\\d+)"`).exec(counters ?? ''); return value ? Number(value[1]) : undefined; };
99
+ total = count('total'); passed = count('passed'); failed = count('failed'); skipped = count('notExecuted') ?? 0;
100
+ } else if (runner !== 'xunit-mtp-off') {
101
+ const count = expression => { const match = expression.exec(output); return match ? Number(match[1]) : undefined; };
102
+ total = count(/\btotal:\s*(\d+)/i);
103
+ passed = count(/\b(?:succeeded|passed):\s*(\d+)/i);
104
+ failed = count(/\bfailed:\s*(\d+)/i);
105
+ skipped = count(/\bskipped:\s*(\d+)/i) ?? 0;
106
+ }
107
+ if (total === 0) refuse(`${project}: zero tests executed; no specifications verified.`);
108
+ if (total === undefined || passed === undefined || failed === undefined) refuse(`${project}: UNVERIFIED — runner count evidence unavailable. Configure a repository-owned invocation with verified reports.`);
109
+ if (failed !== 0 || passed + skipped !== total || passed === 0) refuse(`${project}: not all specifications passed (total=${total}, passed=${passed}, failed=${failed}).`);
110
+ console.log(`cratis-run-specs: verified total=${total}, passed=${passed}, failed=${failed}`);
111
+ }
112
+
113
+ let executed = 0;
114
+ for (const project of projects) {
115
+ if (!existsSync(project)) refuse(`Solution project does not exist: ${project}`);
116
+ const initial = evaluate(project);
117
+ const frameworks = (initial.Properties.TargetFrameworks || initial.Properties.TargetFramework || '').split(';').filter(Boolean);
118
+ const runner = classify(initial, project);
119
+ if (runner === 'non-test') { console.log(`cratis-run-specs: SKIP non-test ${project}`); continue; }
120
+ if (!frameworks.length) refuse(`${project}: target framework unavailable; evaluate the restored project or supply an override.`);
121
+ if (values.framework && !frameworks.includes(values.framework)) refuse(`${project}: requested framework ${values.framework} not in ${frameworks.join(', ')}.`);
122
+ for (const framework of values.framework ? [values.framework] : frameworks) {
123
+ const metadata = initial.Properties.TargetFrameworks ? evaluate(project, framework) : initial;
124
+ const selectedRunner = classify(metadata, project);
125
+ if (selectedRunner === 'non-test') refuse(`${project} (${framework}): inconsistent test metadata; no suite may be omitted.`);
126
+ const target = metadata.Properties.TargetPath;
127
+ if (!target || !isAbsolute(target)) refuse(`${project}: evaluated TargetPath unavailable; perform the matching ${values.configuration}/${framework} build first.`);
128
+ const apphost = target.replace(/\.dll$/i, process.platform === 'win32' ? '.exe' : '');
129
+ const useApphost = apphost !== target && existsSync(apphost);
130
+ if (!useApphost && !existsSync(target)) refuse(`${project}: missing built output ${target}; perform the matching ${values.configuration}/${framework} build first.`);
131
+ const report = selectedRunner === 'xunit-mtp-off' ? reportPath() : undefined;
132
+ const command = selectedRunner === 'vstest' || !useApphost ? 'dotnet' : apphost;
133
+ const arguments_ = selectedRunner === 'vstest'
134
+ ? ['test', project, '--configuration', values.configuration, '--no-build', '--framework', framework]
135
+ : [...(useApphost ? [] : [target]), ...(report ? ['-trx', report] : [])];
136
+ console.log(`cratis-run-specs: RUN ${project} runner=${selectedRunner} framework=${framework} configuration=${values.configuration}`);
137
+ const result = spawnSync(command, arguments_, { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
138
+ process.stdout.write(result.stdout ?? '');
139
+ process.stderr.write(result.stderr ?? '');
140
+ if (result.error) refuse(`${project}: ${result.error.message}`);
141
+ if (result.status !== 0) process.exit(result.status ?? 1);
142
+ verifyCounts(selectedRunner, (result.stdout ?? '') + (result.stderr ?? ''), report, project);
143
+ executed++;
144
+ }
145
+ }
146
+ console.log(executed ? `cratis-run-specs: verified ${executed} project/framework suite(s).` : 'cratis-run-specs: NO-OP — no test projects; no specifications verified.');
147
+ NODE
@@ -117,9 +117,9 @@
117
117
  },
118
118
  {
119
119
  "id": "backend-specs",
120
- "description": "dotnet test (Debug, --no-build) \u2014 zero failures",
120
+ "description": "Built C# spec executables (Debug, no rebuild); VSTest-only fallback \u2014 zero failures",
121
121
  "timeoutSeconds": 120,
122
- "timeoutHint": "dotnet test <affected.Specs.csproj-or-Tests.csproj> --configuration Debug --no-build (replace the placeholder with the affected test project; repeat for its affected dependents)",
122
+ "timeoutHint": "./<affected-project-dir>/bin/Debug/<TFM>/<AssemblyName> (or dotnet <TargetPath.dll> without apphost); only VSTest: dotnet test <project> --configuration Debug --no-build. Repeat for affected dependents.",
123
123
  "changed": [
124
124
  "**/*.cs",
125
125
  "**/*.csproj",
@@ -145,11 +145,9 @@
145
145
  "**/*.sln"
146
146
  ],
147
147
  "command": [
148
- "dotnet",
149
- "test",
150
- "--configuration",
151
- "Debug",
152
- "--no-build"
148
+ "bash",
149
+ "-c",
150
+ "exec bash \"$CRATIS_HOOKS_SCRIPTS_DIR/cratis-run-specs.sh\" --configuration Debug --no-build"
153
151
  ]
154
152
  },
155
153
  {
@@ -15,8 +15,10 @@ Authority in [`general.md`](./general.md). Read this before any browser call.
15
15
  request or an applicable policy names that exact action. A configured browser tool is not
16
16
  a permit.
17
17
  - **[contract] One production tab per session.** No parallel polling or parallel findings on
18
- production. Run the health check first, close idle browsers, and look for stale tabs from
19
- other sessions before opening one.
18
+ production. Run the health check first and close your own idle pages before opening one.
19
+ - **[contract] Close only what you opened.** Promptly close the browser backend you
20
+ started; if no shutdown tool exists, close your pages and report it. Subagents do the
21
+ same. Never touch a user's or shared session; no `pkill`/`killall`. One browser per lane.
20
22
  - **[convention] Prefer staging or a local copy** for interactive and visual smoke runs.
21
23
  - **[contract] Only the `browser-operator` agent receives browser tools.** Hand it the URL,
22
24
  scope, permit and artifact directory. The main session does not do browser work itself
@@ -12,8 +12,8 @@ The goal is minimal ceremony, maximum clarity. Modern C# (13+) gives us records,
12
12
  ## Building
13
13
 
14
14
  - Use `dotnet build` from the command line.
15
- - Use `dotnet format` to format code.
16
- - Use `dotnet test` to run tests.
15
+ - Follow `.editorconfig`; run `dotnet format` only when CI requires it.
16
+ - Run affected spec executables; use `dotnet test <project> --no-build` only for VSTest (see `specs.csharp.md`).
17
17
 
18
18
  ## Formatting
19
19
 
@@ -59,7 +59,7 @@ Repo conventions follow from this: `Source/DotNET` + `Source/JavaScript` for dua
59
59
  ## Quality gates (framework)
60
60
 
61
61
  - Build clean (Debug and Release) with **zero warnings, zero errors**.
62
- - Specs pass for affected projects (C# via `dotnet test`; TS packages via their test command; Components also `build-storybook`).
62
+ - Specs pass for affected projects (C# spec executables (VSTest only: `dotnet test <project> --no-build`); TS packages via their test command; Components also `build-storybook`).
63
63
  - For public-facing changes (APIs, attributes, generated output, component props): update the product documentation and verify it.
64
64
  - Match the repo's existing patterns; when a deep architectural question isn't answered by the universal rules or this file, consult the **repo's own docs/CONTRIBUTING** or ask — do not infer framework internals from a single call site.
65
65
 
@@ -63,9 +63,9 @@ obtaining credentials, and other local conventions ("Product policy" above).
63
63
 
64
64
  Default to agentic behavior: inspect local rules, skills, code, tests, and generated patterns; make conservative assumptions supported by that context; implement and verify end to end when feasible. A user's direct request authorizes every clearly named in-scope action, including external effects such as pushing, opening or merging a pull request, applying the requested label, deployment, issue mutation, and operations against a named environment. The user is the authority for their request; do not require them to identify another approver, decision record, or formal operation profile.
65
65
 
66
- Don't interrupt with questions the repository can answer. Make reversible implementation choices and report them rather than asking the user to approve an implementation plan they already asked you to carry out. Once the user authorizes an action, do not ask them to authorize it again unless the target or consequence materially changes. Ask only when the answer can't be found locally, reasonable product or domain choices have meaningfully different consequences, a consequential effect was not included in the request, the change is hard to reverse and its scope is unclear, or the user asked for checkpoints.
66
+ Decide what the repository or research can answer, record the evidence, and get a cross-provider check when the decision is risky. Report reversible choices instead of asking approval for a plan the user requested; do not re-ask for an authorized action unless its target or consequence materially changes. Deciding never widens the task's authorization ([capability-is-not-authority.md](./capability-is-not-authority.md)). Ask only about human-only matters (credentials, money, production access, legal, merging majors), undecided product choices with different consequences, an effect the request did not include, a hard-to-reverse change with unclear scope, or requested checkpoints.
67
67
 
68
- When a question is necessary, write it for the person doing the work, not for the governance system: explain the concrete choice, why it matters now, the consequence of each option, and the recommended option in plain language. Never present unexplained internal labels such as "execution authority," "capability contract," "delegation architecture," "operation profile," or "decider." Do not bundle unrelated verdicts. Ask who should be named in a durable decision record only after explaining that a significant decision has been made and why preserving it is warranted; a decision record is never a prerequisite for carrying out the user's direct request.
68
+ Write a necessary question plainly: the choice, why now, each option's consequence, your recommendation. No unexplained internal labels ("execution authority," "operation profile," "decider"); do not bundle verdicts. A decision record never gates a direct request; offer one only after explaining the decision.
69
69
 
70
70
  ## Destructive operations
71
71
 
@@ -62,7 +62,8 @@ using Microsoft.AspNetCore.Builder;
62
62
  using MongoDB.Driver;
63
63
 
64
64
  var builder = WebApplication.CreateBuilder(args);
65
- const string connectionString = "mongodb://localhost:27017";
65
+ var connectionString = builder.Configuration.GetConnectionString("Mongo")
66
+ ?? throw new InvalidOperationException("Configure the Mongo connection string.");
66
67
  builder.AddCratisOrleans(new CratisOrleansOptions
67
68
  {
68
69
  ClusterId = "my-service",
@@ -31,6 +31,5 @@ applyTo: "**/*"
31
31
  ## Before you create or edit a PR
32
32
 
33
33
  - Run `node .cratis/ai/hooks/scripts/cratis-check-pr.mjs --body-file <file> --label <intent>`; it applies the exact `policy / verify` release-note rules and the Bash hook runs it on `gh pr create` and `gh pr edit`. If it fails, fix the description, not the code. Before every body edit, run `git diff --stat origin/main...HEAD` and delete bullets the diff no longer supports.
34
- - Run the affected-project gates before marking work ready (`dotnet build`/`dotnet test` with zero warnings, `yarn lint`, `npx tsc -b`), and the repository's required CI checks after an authorized push. `no-release` and documentation-only PRs still satisfy every required check; none is a blanket exemption from red CI.
35
- - Group small related changes into one branch and one PR; see the skill for consolidation and the release-action windows.
36
- - Open the PR once, when verified: each push starts CI. One PR per body of work, opened by the orchestrator (`cratis-engineering-release-trains`), with `--assignee @me`.
34
+ - Run the affected-project gates before marking work ready (build, built specs, lint and type-check; zero warnings), and the repository's required CI checks after an authorized push. `no-release` and documentation-only PRs still satisfy every required check; none is a blanket exemption from red CI.
35
+ - The release train is the PR: fold finished branches into the repository's one train (see the skill). Open it once, when verified, as the orchestrator (`cratis-engineering-release-trains`) with `--assignee @me`. Split only for a named major, an urgent security fix authorized in writing, or an accepted outsider issue (patch or minor, own release).
@@ -49,7 +49,7 @@ public class when_combining_parts : Specification
49
49
 
50
50
  The shared lifecycle is the default on xUnit v2 and v3 since 5.0; the package registers the framework, so delete any hand-written `[assembly: Xunit.TestFramework(...)]` (it fails with `CS0579`). Eligible classes derive from `Specification`, have one public constructor with no arguments or only framework-managed fixture arguments, and contain only plain facts. Construction, Establish/Because and Destroy run once; all facts share the instance and must not mutate it. `IDisposable` specifications also share the lifecycle since Cratis.Specifications 4.4.0: one construction and one `Dispose` after the last fact. Initialization failure fails every fact. Theories and `ITestOutputHelper` constructors keep per-fact execution. Keep shared lifecycle on; `[PerFactSpecificationLifecycle]` opts out only the class whose facts genuinely mutate shared state and cannot move the mutation into `Because`. `CRSPEC0008` (a fact modifies the shared context) is an error by default: move the change into `Establish` or `Because`. See the **cratis-specifications-csharp** skill's shared-lifecycle reference.
51
51
 
52
- Run specifications as the built executable, not `dotnet test`: restore and build once, then run `bin/<Config>/net10.0/<Project>.Specs`. In a solution that mixes Microsoft.Testing.Platform and VSTest projects, run each project on its own; `dotnet test` on the whole solution does not work there. Filters and the 4.x → 5.0 checklist are in the **cratis-specifications-csharp** skill's [running-specs reference](../skills/cratis-specifications-csharp/references/running-specs.md).
52
+ Run xUnit v3, TUnit and native-runner specifications as each project's built executable: restore and build once, then run its matching configuration/TFM output. Only identified VSTest projects use `dotnet test <project> --no-build`. Never default to solution-wide `dotnet test` in a mixed suite. Filters and the 4.x → 5.0 checklist are in the **cratis-specifications-csharp** skill's [running-specs reference](../skills/cratis-specifications-csharp/references/running-specs.md).
53
53
 
54
54
  CRSPEC0001–CRSPEC0007 are warnings. Since 4.5 the analyzers ship only in `Cratis.Specifications`, transitively used by the framework packages; excluding analyzers on a framework package does not remove them. Derived xUnit/NUnit test attributes are recognized since 4.1.2. `Given`/`When` are aliases since 4.4 (declaring both aliases for one phase is CRSPEC0009), but house style stays `Establish`/`Because`. With warnings as errors, a non-`should_` fact, multiple specs per file (given contexts exempt), a fact on a given context, a `should_` without a test attribute, explicit base lifecycle calls, non-public specs with facts, or `Because` on a given context fail the build. Follow **cratis-specification-by-example**; do not suppress these diagnostics.
55
55
 
@@ -16,7 +16,7 @@ Cratis rules in `.cratis/ai/rules/` (including `csharp.md`, `specs.csharp.md`, a
16
16
 
17
17
  You MUST add the `/bl:{}` flag to:
18
18
  - `dotnet build`
19
- - `dotnet test`
19
+ - `dotnet test` when it is the selected VSTest runner; built spec executables do not produce MSBuild binlogs
20
20
  - `dotnet pack`
21
21
  - `dotnet publish`
22
22
  - `dotnet restore`
@@ -202,7 +202,7 @@ if ($testProjects.Count -eq 0) {
202
202
  Write-Host "TEST_OUTPUT_ROOT:$testOutputRoot"
203
203
  ```
204
204
 
205
- - If `ENTRY_TYPE:NotFound` and SDK-style test projects were found → use the test projects directly as `dotnet test` entry points.
205
+ - If `ENTRY_TYPE:NotFound` and SDK-style test projects were found → pass the selected projects to `run-tests` discovery: supported executable suites use their built runners; only VSTest uses `dotnet test <project> --no-build`.
206
206
  - If `ENTRY_TYPE:NotFound` and classic test projects were found → use only the repository's documented coverage command; do not infer `dotnet test`.
207
207
  - If `ENTRY_TYPE:NotFound` and no test projects found → stop: `No .sln, .slnx, or test projects found under <path>. Provide the path to your .NET solution, filter, or project.`
208
208
  - A missing/unreadable requested path, failed lookup, or ambiguous entry point is a discovery blocker, not measured zero coverage or evidence of an empty suite. Do not continue collection after a discovery error.
@@ -302,6 +302,6 @@ ideas recorded in `references/provenance.md`. Read `references/provenance.md` wh
302
302
  ## How it is proven
303
303
 
304
304
  `dotnet build -c Debug` — it compiles the `#if DEBUG` specification code — then
305
- `dotnet test`, both clean, with a specification for the happy path and one per
305
+ the owning project's built spec executable (see [specs.csharp](../../rules/specs.csharp.md)), both clean, with a specification for the happy path and one per
306
306
  rejection. Before trusting a rejection specification green, remove the condition
307
307
  that causes the rejection once and confirm it goes red.
@@ -59,7 +59,7 @@ These non-refusal failures and unhandled refusals fail the observer for recovery
59
59
  ## Verify
60
60
 
61
61
  Run `dotnet build -c Debug` and the affected specification project with
62
- `dotnet test`. Cover selector ordering, each handled refusal, unchanged
62
+ the owning project's built spec executable (see [specs.csharp](../../../rules/specs.csharp.md)). Cover selector ordering, each handled refusal, unchanged
63
63
  remaining-command collaborators, branch append failure, and a concurrency or
64
64
  exception failure that must not be acknowledged. Assert earlier committed effects
65
65
  separately; a failed result does not prove rollback.
@@ -76,4 +76,4 @@ append behavior. Deliver the same occurrence twice under the same effect name:
76
76
  assert one committed effect and a recorded result on the second delivery. Cover
77
77
  a terminal refusal, a retryable concurrency conflict, insufficient capability,
78
78
  and commit-then-lost-reply recovery. Run the owning specification project with
79
- `dotnet test`; passing means those assertions ran, not just that compilation succeeded.
79
+ the owning project's built spec executable (see [specs.csharp](../../../rules/specs.csharp.md)); passing means those assertions ran, not just that compilation succeeded.
@@ -237,7 +237,7 @@ not uncertain remote acknowledgments or the external provider's durability.
237
237
  `UncompensatedCount`/`FailedCompensationCount`, and remaining callbacks attempted.
238
238
 
239
239
  Arc's [failure recipes](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/testing/command-operation-failures.md)
240
- contain the full cases. Run `dotnet test <Your.Specs.csproj>`; assert behavior,
240
+ contain the full cases. Run the owning project's built spec executable (see [specs.csharp](../../../rules/specs.csharp.md)); assert behavior,
241
241
  not just command success. These corpus excerpts are source-checked, not compiled
242
242
  by the corpus text-verification gate. Test provider ownership, repeated finalized
243
243
  requests, conflicting keys, and delayed creates at the provider integration boundary.
@@ -61,6 +61,6 @@ Arc can fail to bind such results to the matching form field.
61
61
 
62
62
  Add specs for offset-less rejection, an accepted offset, malformed input under
63
63
  both authorized and denied callers, and nested/indexed member paths. Assert
64
- server `Members` using camelCase and run `dotnet test` on the owning spec project.
64
+ server `Members` using camelCase and run the owning spec project's built executable (see [specs.csharp](../../../rules/specs.csharp.md)).
65
65
  For form behavior, assert the rejection appears on its intended field, not only
66
66
  in a command-wide error list.
@@ -18,4 +18,4 @@ remove the floor, and an explicit attribute wins. Check with
18
18
 
19
19
  Specify both the default refusal and a caller sending a permissive severity.
20
20
  Assert the command did not write events; a displayed message alone is not proof
21
- that validation blocked execution. Run `dotnet test` on the owning spec project.
21
+ that validation blocked execution. Run the owning spec project's built executable (see [specs.csharp](../../../rules/specs.csharp.md)).
@@ -36,5 +36,5 @@ In a spec, attempt an immediate append through both the current event log and a
36
36
  previously captured reference; assert that neither reaches transport. Assert
37
37
  that transactional work commits with its receipt, that an unsupported kernel
38
38
  fails closed, and that borrowed frames cannot complete another owner's unit.
39
- Run `dotnet test` on the owning project. In-process proof is not a substitute for
39
+ Run the owning project's built spec executable (see [specs.csharp](../../../rules/specs.csharp.md)). In-process proof is not a substitute for
40
40
  integration coverage of the storage provider's transaction boundary.
@@ -81,7 +81,7 @@ a handler through in-process ingress, claims, receipts and completion:
81
81
 
82
82
  There are no background workers, automatic lease renewals or reminders in this
83
83
  scenario; it does not prove behavior in a real multi-node cluster. Run
84
- `dotnet test` on the owning spec project and assert duplicate ingress, redelivery,
84
+ the owning spec project's built executable (see [specs.csharp](../../../rules/specs.csharp.md)) and assert duplicate ingress, redelivery,
85
85
  terminal refusal, lost reply and unsupported-capability paths.
86
86
 
87
87
  Requires Chronicle 19.78.0 for `Defaults` and `EventStoreForTesting` constructors
@@ -22,7 +22,7 @@ complete: the first claimant in event-log order retains the value, later duplica
22
22
  claims are logged and skipped. Reindexing does not retract their stored events.
23
23
  Older kernels can fail the reindex instead.
24
24
 
25
- Run the affected specifications with `dotnet test`, asserting a second source's
25
+ Run the affected specifications with the owning project's built spec executable (see [specs.csharp](../../../rules/specs.csharp.md)), asserting a second source's
26
26
  rejection, unchanged original claim after a failed move, and successful reuse
27
27
  after release. Real SQL integration must prove two competing claims cannot both
28
28
  succeed; an in-memory pass does not prove the SQL index was upgraded.