@cratis/pi 7.103.5 → 7.103.7
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/agents/screenplay-modeler.md +1 -0
- package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +7 -7
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +6 -2
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +7 -5
- package/package/corpus/harnesses/pi/agents/screenplay-modeler.md +1 -0
- package/package/corpus/harnesses/pi/extensions/cratis-mcp/ServerBridge.ts +7 -5
- package/package/corpus/harnesses/pi/extensions/cratis-mcp/process.ts +15 -3
- package/package/corpus/hooks/scripts/cratis-cleanup-worktrees.sh +13 -5
- package/package/corpus/rules/local-work-artifacts.md +7 -7
- package/package/corpus/rules/profiles.md +6 -2
- package/package/corpus/rules/specs.csharp.md +7 -5
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +7 -3
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dependency-map.md +31 -14
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +3 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +6 -4
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/train-mode.md +43 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +5 -1
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +1065 -60
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/dependency-map.tsv +1 -1
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/file-layout.md +15 -6
- package/package/corpus/skills/cratis-screenplay-event-modeling/verification.json +4 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +7 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +15 -4
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +49 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/repairs.md +27 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +14 -0
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +6 -0
- package/package/corpus/skills/cratis-screenplay-model-review/references/checklist.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-review/verification.json +2 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +4 -0
- package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +4 -5
- package/package/corpus/skills/cratis-screenplay-read-surface/SKILL.md +4 -5
- package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +6 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet.md +13 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +10 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +3 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +8 -2
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/ui-profile-and-theme.md +28 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/verification.json +4 -0
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +20 -13
- package/package/corpus/skills/cratis-specifications-csharp/references/running-specs.md +90 -0
- package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +55 -23
- package/package/corpus/skills/cratis-specifications-csharp/verification.json +5 -3
- package/package/corpus/skills/filter-syntax/SKILL.md +3 -1
- package/package/corpus/skills/run-tests/SKILL.md +1 -1
- package/package.json +1 -1
|
@@ -33,6 +33,7 @@ After reading these instructions, read each file listed under `process.agents.sc
|
|
|
33
33
|
|
|
34
34
|
## Method
|
|
35
35
|
|
|
36
|
+
- Reuse existing model/workspace roots. Code-only `Source/` or `src/` is not a model root; absent model/workspace state, use repository-root `Screenplay/`, never `.cratis/`. Read model-authoring's MCP root-selection/version guidance before switching roots.
|
|
36
37
|
- Load `cratis-screenplay-modeling-lifecycle`, then exactly one phase skill per step: discovery, slice design, streams and consistency, automations and translations, scenario coverage. A legacy system enters through `cratis-screenplay-legacy-extraction` instead of discovery and modeling; later runs re-enter by recorded source identity, widen the candidate in layered passes, and promote locators, decisions and open questions into notes and `documentation` before acceptance (`references/re-entry-and-passes.md` there). Syntax, versions and verdict commands: `cratis-screenplay-toolchain`. Load the phase skill before authoring a construct family.
|
|
37
38
|
- For a change to an existing model, start from an impact walkthrough (`cratis-screenplay-model-review` `references/walkthrough.md`) so the brief names every affected slice.
|
|
38
39
|
- Write declarations in story order, one slice after another, never batched by kind. After each batch read `PLAY0516` and `PLAY0517` and repair, or record why the order stays (`cratis-screenplay-slice-design` `references/slicing.md`).
|
|
@@ -17,8 +17,9 @@ dumps, and similar coordination files. These are **work records, not documentati
|
|
|
17
17
|
If you find one tracked in git, move it into `.ai-work/` and remove it from
|
|
18
18
|
tracking in a dedicated commit.
|
|
19
19
|
- A genuine follow-up that must survive the session is **not** a work record — suggest
|
|
20
|
-
opening a GitHub issue for it
|
|
21
|
-
where everyone can see it, instead of leaving a planning file behind.
|
|
20
|
+
opening a GitHub issue for it so future work is tracked
|
|
21
|
+
where everyone can see it, instead of leaving a planning file behind. A follow-up
|
|
22
|
+
issue includes an `## Acceptance criteria` section of concrete, testable bullets.
|
|
22
23
|
- Knowledge that must outlive the session (real documentation, ADRs, operator
|
|
23
24
|
guides) is written deliberately into the repository's documentation structure
|
|
24
25
|
through normal review — not left behind as a work record.
|
|
@@ -37,16 +38,15 @@ dumps, and similar coordination files. These are **work records, not documentati
|
|
|
37
38
|
replace the prompt.
|
|
38
39
|
- **A worktree is a work record too.** Create it outside the repository tree, for
|
|
39
40
|
example in a sibling `../.worktrees/` directory. A nested worktree inherits the
|
|
40
|
-
parent's `.globalconfig
|
|
41
|
+
parent's `.globalconfig`, causing .NET's `MultipleGlobalAnalyzerKeys`
|
|
41
42
|
errors or silently changing analyzer severities. **Delivered work is cleaned up by
|
|
42
43
|
`.cratis/ai/hooks/scripts/cratis-cleanup-worktrees.sh`, not by memory:** run it from the
|
|
43
44
|
main checkout when a pull request merges or you finish a task (hooks also run it after
|
|
44
45
|
`gh pr merge` and at session start where the harness supports them, and **ship-changes**
|
|
45
46
|
ends with it). It removes a worktree and its branch only on evidence the work reached the
|
|
46
|
-
default branch — pushed is not merged — and never forces.
|
|
47
|
-
directories
|
|
48
|
-
|
|
49
|
-
automatic hooks never opt in. Pin a long-lived worktree with `git worktree lock <path>`;
|
|
47
|
+
default branch — pushed is not merged — and never forces. It keeps worktrees with non-empty
|
|
48
|
+
`.ai-work/` directories; manual `--include-work-records` deletes those records after you
|
|
49
|
+
preserve what you need, and automatic hooks never opt in. Pin a long-lived worktree with `git worktree lock <path>`;
|
|
50
50
|
locked worktrees are always kept, even with opt-in. Other ignored files such as `.env` files
|
|
51
51
|
and local databases can still be deleted with a removed worktree, so move anything you
|
|
52
52
|
still need out first. Do not hand-remove worktrees or `git branch -D` a delivered branch
|
|
@@ -313,8 +313,12 @@ The profile also selects the corpus-owned Screenplay MCP declaration from
|
|
|
313
313
|
`mcp-servers.json`. The Cratis CLI hosts the server as `cratis screenplay mcp`
|
|
314
314
|
and registers a scoped entry for supported clients without replacing their other
|
|
315
315
|
servers. Inspect install/status results for adapter support or configuration
|
|
316
|
-
conflicts.
|
|
317
|
-
`src
|
|
316
|
+
conflicts. Reuse the project's existing model/workspace root. A code-only `Source/`
|
|
317
|
+
or `src/` is not a model root; without model/workspace state use repository-root
|
|
318
|
+
`Screenplay/`, never `.cratis/`. Requires Cratis CLI 3.93.1 (`cratis --version`) for
|
|
319
|
+
CLI discovery and Screenplay 4.236.1 (`screenplay --version`) for standalone MCP
|
|
320
|
+
discovery; older versions can choose code-only folders. Fixed-root sibling admission
|
|
321
|
+
is narrowly scoped, not an arbitrary switch: see [root selection](../../../skills/cratis-screenplay-event-modeling/references/file-layout.md#locate-the-model).
|
|
318
322
|
Project-owned configuration can name a root explicitly.
|
|
319
323
|
|
|
320
324
|
Like `allowDependencyManifestWrites`, `modelFirst` (`frozen`, `reason`) and `process` (`decisions`, `workItems`, `acceptance`, `agents`) are project-owned keys in `.cratis/ai.json` that install and update preserve. Only `modelFirst.frozen` is read by code; the lifecycle skill's `references/project-process.md` defines the keys, defaults and routing.
|
|
@@ -18,9 +18,9 @@ Extends the base [specs.md](./specs.instructions.md) (folder structure, naming,
|
|
|
18
18
|
|
|
19
19
|
## Frameworks
|
|
20
20
|
|
|
21
|
-
- [xUnit](https://xunit.net/) **
|
|
22
|
-
-
|
|
23
|
-
-
|
|
21
|
+
- [xUnit v3](https://xunit.net/docs/getting-started/v3/migration) is the house default for new and migrated spec projects: reference `Cratis.Specifications.XUnit.v3` **5.0** with `xunit.v3.mtp-off`, `Microsoft.NET.Test.Sdk` and `xunit.runner.visualstudio`, and set `<OutputType>Exe</OutputType>` in the project. The package supplies the `Specification` base, convention lifecycle and `ShouldXxx` assertions; the namespace remains `Cratis.Specifications`. Its `Cratis.Specifications` dependency supplies `Catch` and `SpecificationMethods`.
|
|
22
|
+
- 5.0 targets `net10.0` only. A spec project that must stay on net8.0/net9.0 stays on 4.x, and net48 on 3.x. A project that stays on xUnit v2 uses `Cratis.Specifications.XUnit`, with the same base and conventions.
|
|
23
|
+
- `.NUnit`/`.NUnit5`, `.TUnit` and the experimental `.Runner` are alternatives for repositories already built on them.
|
|
24
24
|
- [NSubstitute](https://nsubstitute.github.io/) for mocking collaborators.
|
|
25
25
|
- Spec projects are named `<Source>.Specs`.
|
|
26
26
|
|
|
@@ -48,9 +48,11 @@ public class when_combining_parts : Specification
|
|
|
48
48
|
| `[Fact] void should_*()` | one assertion per fact | use the `ShouldXxx()` extension methods |
|
|
49
49
|
| `void Destroy()` | teardown after each fact by default | once per eligible class with shared lifecycle opt-in |
|
|
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 parameterless constructor, do not implement `IDisposable`, and contain only plain facts. Construction, Establish/Because and Destroy run once; all facts share the instance and must not mutate it. Initialization failure fails every fact. Theories and `ITestOutputHelper` constructors keep per-fact execution; `[PerFactSpecificationLifecycle]` opts a class out. `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
|
-
|
|
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).
|
|
54
|
+
|
|
55
|
+
CRSPEC0001–CRSPEC0007 are warnings bundled in `.Specifications`, `.XUnit`, `.XUnit.v3`, and `.NUnit`. 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.
|
|
54
56
|
|
|
55
57
|
## Reusable layered contexts
|
|
56
58
|
|
|
@@ -28,6 +28,7 @@ After reading these instructions, read each file listed under `process.agents.sc
|
|
|
28
28
|
|
|
29
29
|
## Method
|
|
30
30
|
|
|
31
|
+
- Reuse existing model/workspace roots. Code-only `Source/` or `src/` is not a model root; absent model/workspace state, use repository-root `Screenplay/`, never `.cratis/`. Read model-authoring's MCP root-selection/version guidance before switching roots.
|
|
31
32
|
- Load `cratis-screenplay-modeling-lifecycle`, then exactly one phase skill per step: discovery, slice design, streams and consistency, automations and translations, scenario coverage. A legacy system enters through `cratis-screenplay-legacy-extraction` instead of discovery and modeling; later runs re-enter by recorded source identity, widen the candidate in layered passes, and promote locators, decisions and open questions into notes and `documentation` before acceptance (`references/re-entry-and-passes.md` there). Syntax, versions and verdict commands: `cratis-screenplay-toolchain`. Load the phase skill before authoring a construct family.
|
|
32
33
|
- For a change to an existing model, start from an impact walkthrough (`cratis-screenplay-model-review` `references/walkthrough.md`) so the brief names every affected slice.
|
|
33
34
|
- Write declarations in story order, one slice after another, never batched by kind. After each batch read `PLAY0516` and `PLAY0517` and repair, or record why the order stays (`cratis-screenplay-slice-design` `references/slicing.md`).
|
|
@@ -44,22 +44,24 @@ export function createServerBridge(pi: ExtensionAPI, corpus: string, connect: (p
|
|
|
44
44
|
connections.clear();
|
|
45
45
|
deactivate();
|
|
46
46
|
};
|
|
47
|
-
interface Started { server: CorpusServer; connection
|
|
47
|
+
interface Started { server: CorpusServer; connection?: StdioConnection; catalog?: ServerCatalog; messages: string[] }
|
|
48
48
|
/** Connect and discover only; registration happens afterwards in selection order so name claims are deterministic. */
|
|
49
49
|
const start = async (context: ExtensionContext, server: CorpusServer): Promise<Started> => {
|
|
50
|
-
|
|
51
|
-
connections.set(server.id, connection);
|
|
50
|
+
let connection: StdioConnection | undefined;
|
|
52
51
|
try {
|
|
52
|
+
connection = connect(context.cwd, server);
|
|
53
|
+
connections.set(server.id, connection);
|
|
53
54
|
const catalog = await withDeadline(discoverServer(connection, server.id), deadlineMilliseconds, server.id);
|
|
54
55
|
if (connections.get(server.id) !== connection || connection.stopped) throw new Error(`MCP server '${server.id}' session ended during discovery.`);
|
|
55
56
|
return { server, connection, catalog, messages: [...catalog.messages] };
|
|
56
57
|
} catch (error) {
|
|
57
|
-
connection
|
|
58
|
-
if (connections.get(server.id) === connection) connections.delete(server.id);
|
|
58
|
+
connection?.dispose();
|
|
59
|
+
if (connection && connections.get(server.id) === connection) connections.delete(server.id);
|
|
59
60
|
return { server, connection, messages: [`MCP server '${server.id}' is unavailable: ${error instanceof Error ? error.message : String(error)}`] };
|
|
60
61
|
}
|
|
61
62
|
};
|
|
62
63
|
const register = ({ server, connection, catalog, messages }: Started, claimed: Set<string>): void => {
|
|
64
|
+
if (!connection) return;
|
|
63
65
|
const existing = new Set(pi.getAllTools().map(tool => tool.name));
|
|
64
66
|
for (const tool of catalog?.tools ?? []) {
|
|
65
67
|
if (claimed.has(tool.nativeName) || (existing.has(tool.nativeName) && !registered.has(tool.nativeName))) {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { execFile, spawn } from 'node:child_process';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
5
7
|
import type { CorpusServer } from './CorpusServer.ts';
|
|
6
8
|
import { StdioConnection } from './StdioConnection.ts';
|
|
7
9
|
|
|
@@ -45,9 +47,19 @@ export function startConnection(project: string, launch: typeof spawn = spawn):
|
|
|
45
47
|
}));
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
/** Launch a corpus server
|
|
49
|
-
export function startServer(project: string, server: CorpusServer, launch: typeof spawn = spawn): StdioConnection {
|
|
50
|
-
|
|
50
|
+
/** Launch a corpus server without a shell or interpolation; Windows npx uses npm's JavaScript entry point. */
|
|
51
|
+
export function startServer(project: string, server: CorpusServer, launch: typeof spawn = spawn, platform: NodeJS.Platform = process.platform, fileExists: (path: string) => boolean = existsSync): StdioConnection {
|
|
52
|
+
let command = server.command;
|
|
53
|
+
let args = server.args;
|
|
54
|
+
if (platform === 'win32' && /^npx(?:\.cmd)?$/i.test(command)) {
|
|
55
|
+
const entryPoint = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npx-cli.js');
|
|
56
|
+
if (!fileExists(entryPoint)) {
|
|
57
|
+
throw new Error(`MCP server '${server.id}' requires npm's npx-cli.js at '${entryPoint}'. Install Node.js with npm; Windows command shims cannot be launched without a shell.`);
|
|
58
|
+
}
|
|
59
|
+
command = process.execPath;
|
|
60
|
+
args = [entryPoint, ...args];
|
|
61
|
+
}
|
|
62
|
+
return new StdioConnection(launch(command, args, {
|
|
51
63
|
cwd: project,
|
|
52
64
|
env: { ...childEnvironment(), ...server.env },
|
|
53
65
|
shell: false,
|
|
@@ -158,12 +158,19 @@ cached_pulls() {
|
|
|
158
158
|
}
|
|
159
159
|
take_lock() {
|
|
160
160
|
mkdir "$state/lock" 2>/dev/null && return 0
|
|
161
|
-
local modified retired acquired
|
|
162
|
-
read -r
|
|
161
|
+
local inode modified retired acquired current
|
|
162
|
+
read -r inode modified < <(lock_stat "$state/lock")
|
|
163
163
|
[[ "$modified" =~ ^[0-9]+$ ]] && [ "$now" -ge "$((modified + 900))" ] || return 1
|
|
164
164
|
# Rename atomically: a killed reclaimer can leave only a retired directory, never a blocking marker.
|
|
165
165
|
retired="$state/lock.stale.$$.$RANDOM"
|
|
166
166
|
mv "$state/lock" "$retired" 2>/dev/null || return 1
|
|
167
|
+
read -r current _ < <(lock_stat "$retired")
|
|
168
|
+
if [ "$current" != "$inode" ]; then
|
|
169
|
+
# Another reclaimer replaced the stale directory. Restore its lock if possible;
|
|
170
|
+
# never acquire or delete a directory that was not the stale one we inspected.
|
|
171
|
+
[ -e "$state/lock" ] || mv "$retired" "$state/lock" 2>/dev/null
|
|
172
|
+
return 1
|
|
173
|
+
fi
|
|
167
174
|
acquired=1
|
|
168
175
|
mkdir "$state/lock" 2>/dev/null && acquired=0
|
|
169
176
|
rm -rf "$retired"
|
|
@@ -282,9 +289,10 @@ in_use=$(git worktree list --porcelain | sed -n 's#^branch refs/heads/##p')
|
|
|
282
289
|
planned_branches=$(printf '%s\n' "${PLAN_BRANCH[@]+"${PLAN_BRANCH[@]}"}")
|
|
283
290
|
while IFS='|' read -r b bsha; do
|
|
284
291
|
if [ -z "$b" ] || [ "$b" = "$default_local" ]; then continue; fi
|
|
285
|
-
|
|
292
|
+
# Drain producers under pipefail: grep -q can turn a match into a SIGPIPE failure.
|
|
293
|
+
if printf '%s\n' "$in_use" | grep -x "$b" >/dev/null; then
|
|
286
294
|
[ "$DRY" = 0 ] || continue
|
|
287
|
-
printf '%s\n' "$planned_branches" | grep -
|
|
295
|
+
printf '%s\n' "$planned_branches" | grep -x "$b" >/dev/null || continue
|
|
288
296
|
fi
|
|
289
297
|
plan_item branch "$b" "$bsha" "$b"
|
|
290
298
|
done < <(git for-each-ref --format='%(refname:short)|%(objectname)' refs/heads)
|
|
@@ -307,7 +315,7 @@ git worktree prune >/dev/null 2>&1
|
|
|
307
315
|
for ((i=0; i<${#PLAN_KIND[@]}; i++)); do
|
|
308
316
|
kind=${PLAN_KIND[i]}; path=${PLAN_PATH[i]}; sha=${PLAN_SHA[i]}; br=${PLAN_BRANCH[i]}; ev=${PLAN_EVIDENCE[i]}
|
|
309
317
|
if [ "$kind" = branch ]; then
|
|
310
|
-
git worktree list --porcelain | sed -n 's#^branch refs/heads/##p' | grep -
|
|
318
|
+
git worktree list --porcelain | sed -n 's#^branch refs/heads/##p' | grep -x "$br" >/dev/null && continue
|
|
311
319
|
fi
|
|
312
320
|
if [ -z "$ev" ]; then
|
|
313
321
|
if [ "$kind" = worktree ]; then note_kept "worktree $path (${br:-detached}): work not delivered to $default"
|
|
@@ -16,8 +16,9 @@ dumps, and similar coordination files. These are **work records, not documentati
|
|
|
16
16
|
If you find one tracked in git, move it into `.ai-work/` and remove it from
|
|
17
17
|
tracking in a dedicated commit.
|
|
18
18
|
- A genuine follow-up that must survive the session is **not** a work record — suggest
|
|
19
|
-
opening a GitHub issue for it
|
|
20
|
-
where everyone can see it, instead of leaving a planning file behind.
|
|
19
|
+
opening a GitHub issue for it so future work is tracked
|
|
20
|
+
where everyone can see it, instead of leaving a planning file behind. A follow-up
|
|
21
|
+
issue includes an `## Acceptance criteria` section of concrete, testable bullets.
|
|
21
22
|
- Knowledge that must outlive the session (real documentation, ADRs, operator
|
|
22
23
|
guides) is written deliberately into the repository's documentation structure
|
|
23
24
|
through normal review — not left behind as a work record.
|
|
@@ -36,16 +37,15 @@ dumps, and similar coordination files. These are **work records, not documentati
|
|
|
36
37
|
replace the prompt.
|
|
37
38
|
- **A worktree is a work record too.** Create it outside the repository tree, for
|
|
38
39
|
example in a sibling `../.worktrees/` directory. A nested worktree inherits the
|
|
39
|
-
parent's `.globalconfig
|
|
40
|
+
parent's `.globalconfig`, causing .NET's `MultipleGlobalAnalyzerKeys`
|
|
40
41
|
errors or silently changing analyzer severities. **Delivered work is cleaned up by
|
|
41
42
|
`.cratis/ai/hooks/scripts/cratis-cleanup-worktrees.sh`, not by memory:** run it from the
|
|
42
43
|
main checkout when a pull request merges or you finish a task (hooks also run it after
|
|
43
44
|
`gh pr merge` and at session start where the harness supports them, and **ship-changes**
|
|
44
45
|
ends with it). It removes a worktree and its branch only on evidence the work reached the
|
|
45
|
-
default branch — pushed is not merged — and never forces.
|
|
46
|
-
directories
|
|
47
|
-
|
|
48
|
-
automatic hooks never opt in. Pin a long-lived worktree with `git worktree lock <path>`;
|
|
46
|
+
default branch — pushed is not merged — and never forces. It keeps worktrees with non-empty
|
|
47
|
+
`.ai-work/` directories; manual `--include-work-records` deletes those records after you
|
|
48
|
+
preserve what you need, and automatic hooks never opt in. Pin a long-lived worktree with `git worktree lock <path>`;
|
|
49
49
|
locked worktrees are always kept, even with opt-in. Other ignored files such as `.env` files
|
|
50
50
|
and local databases can still be deleted with a removed worktree, so move anything you
|
|
51
51
|
still need out first. Do not hand-remove worktrees or `git branch -D` a delivered branch
|
|
@@ -312,8 +312,12 @@ The profile also selects the corpus-owned Screenplay MCP declaration from
|
|
|
312
312
|
`mcp-servers.json`. The Cratis CLI hosts the server as `cratis screenplay mcp`
|
|
313
313
|
and registers a scoped entry for supported clients without replacing their other
|
|
314
314
|
servers. Inspect install/status results for adapter support or configuration
|
|
315
|
-
conflicts.
|
|
316
|
-
`src
|
|
315
|
+
conflicts. Reuse the project's existing model/workspace root. A code-only `Source/`
|
|
316
|
+
or `src/` is not a model root; without model/workspace state use repository-root
|
|
317
|
+
`Screenplay/`, never `.cratis/`. Requires Cratis CLI 3.93.1 (`cratis --version`) for
|
|
318
|
+
CLI discovery and Screenplay 4.236.1 (`screenplay --version`) for standalone MCP
|
|
319
|
+
discovery; older versions can choose code-only folders. Fixed-root sibling admission
|
|
320
|
+
is narrowly scoped, not an arbitrary switch: see [root selection](../skills/cratis-screenplay-event-modeling/references/file-layout.md#locate-the-model).
|
|
317
321
|
Project-owned configuration can name a root explicitly.
|
|
318
322
|
|
|
319
323
|
Like `allowDependencyManifestWrites`, `modelFirst` (`frozen`, `reason`) and `process` (`decisions`, `workItems`, `acceptance`, `agents`) are project-owned keys in `.cratis/ai.json` that install and update preserve. Only `modelFirst.frozen` is read by code; the lifecycle skill's `references/project-process.md` defines the keys, defaults and routing.
|
|
@@ -17,9 +17,9 @@ Extends the base [specs.md](./specs.md) (folder structure, naming, philosophy) w
|
|
|
17
17
|
|
|
18
18
|
## Frameworks
|
|
19
19
|
|
|
20
|
-
- [xUnit](https://xunit.net/) **
|
|
21
|
-
-
|
|
22
|
-
-
|
|
20
|
+
- [xUnit v3](https://xunit.net/docs/getting-started/v3/migration) is the house default for new and migrated spec projects: reference `Cratis.Specifications.XUnit.v3` **5.0** with `xunit.v3.mtp-off`, `Microsoft.NET.Test.Sdk` and `xunit.runner.visualstudio`, and set `<OutputType>Exe</OutputType>` in the project. The package supplies the `Specification` base, convention lifecycle and `ShouldXxx` assertions; the namespace remains `Cratis.Specifications`. Its `Cratis.Specifications` dependency supplies `Catch` and `SpecificationMethods`.
|
|
21
|
+
- 5.0 targets `net10.0` only. A spec project that must stay on net8.0/net9.0 stays on 4.x, and net48 on 3.x. A project that stays on xUnit v2 uses `Cratis.Specifications.XUnit`, with the same base and conventions.
|
|
22
|
+
- `.NUnit`/`.NUnit5`, `.TUnit` and the experimental `.Runner` are alternatives for repositories already built on them.
|
|
23
23
|
- [NSubstitute](https://nsubstitute.github.io/) for mocking collaborators.
|
|
24
24
|
- Spec projects are named `<Source>.Specs`.
|
|
25
25
|
|
|
@@ -47,9 +47,11 @@ public class when_combining_parts : Specification
|
|
|
47
47
|
| `[Fact] void should_*()` | one assertion per fact | use the `ShouldXxx()` extension methods |
|
|
48
48
|
| `void Destroy()` | teardown after each fact by default | once per eligible class with shared lifecycle opt-in |
|
|
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 parameterless constructor, do not implement `IDisposable`, and contain only plain facts. Construction, Establish/Because and Destroy run once; all facts share the instance and must not mutate it. Initialization failure fails every fact. Theories and `ITestOutputHelper` constructors keep per-fact execution; `[PerFactSpecificationLifecycle]` opts a class out. `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
|
-
|
|
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).
|
|
53
|
+
|
|
54
|
+
CRSPEC0001–CRSPEC0007 are warnings bundled in `.Specifications`, `.XUnit`, `.XUnit.v3`, and `.NUnit`. 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.
|
|
53
55
|
|
|
54
56
|
## Reusable layered contexts
|
|
55
57
|
|
|
@@ -57,6 +57,7 @@ commands before choosing the build, regression spec and full gate commands.
|
|
|
57
57
|
- [ ] Reproduce the consumer bug and select its regression spec/app action.
|
|
58
58
|
- [ ] Read [references/dependency-map.md](references/dependency-map.md) for the chain between the named repositories, generate one stamp, then pack each producer in upstream order.
|
|
59
59
|
- [ ] If needed, publish the kernel and assets, then build a matching local image.
|
|
60
|
+
- [ ] Run `closure` for the consumer and producers, follow the printed commands, and run its one `verify-pack` after the last pack.
|
|
60
61
|
- [ ] Activate the consumer overlay and select the local image.
|
|
61
62
|
- [ ] Build the consumer, run its regression specs/app action and required gates.
|
|
62
63
|
- [ ] On failure, diagnose, fix the producer, create a distinct stamp and repeat.
|
|
@@ -94,9 +95,11 @@ exit `0`. Inspect package dependencies: every locally changed dependency ID must
|
|
|
94
95
|
resolve to the stamp, not a published fallback. Chronicle's client solution filter
|
|
95
96
|
can omit `Cratis.Chronicle.CodeAnalysis`; pack its code-analysis package project
|
|
96
97
|
separately. The package IDs a producer should ship are listed in
|
|
97
|
-
[dependency-map.tsv](scripts/dependency-map.tsv)
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
[dependency-map.tsv](scripts/dependency-map.tsv); `closure CONSUMER=PATH PRODUCER=PATH...` prints
|
|
99
|
+
the stamp, every overlay, one `pack-*` command per map row (with its `--property` arguments) and
|
|
100
|
+
ONE `verify-pack` for the whole plan (see [references/dependency-map.md](references/dependency-map.md)).
|
|
101
|
+
Run the printed commands in order. A successful pack alone does not prove dependency closure;
|
|
102
|
+
`verify-pack` does.
|
|
100
103
|
|
|
101
104
|
### 3. Build an image when needed
|
|
102
105
|
|
|
@@ -242,6 +245,7 @@ minutes, instead of after a release and a pin bump.
|
|
|
242
245
|
- Read [references/docker-images.md](references/docker-images.md) when publishing a kernel or selecting images in a consumer.
|
|
243
246
|
- Read [references/go-jvm-python-elixir.md](references/go-jvm-python-elixir.md) when the boundary uses another language toolchain.
|
|
244
247
|
- Read [references/worked-examples.md](references/worked-examples.md) when following Chronicle → Studio, Fundamentals → Arc, Components → Studio, Chronicle → Direct, Screenplay → Stage and Studio, Arc → Chronicle, or Fundamentals → Arc → Chronicle.
|
|
248
|
+
- Read [references/train-mode.md](references/train-mode.md) when verifying a downstream repository against a local release train (`stamp --train`).
|
|
245
249
|
- Read [references/other-environments.md](references/other-environments.md) when running on another developer machine or an agent host.
|
|
246
250
|
|
|
247
251
|
Read `scripts/npm-restore-check.cjs` when inspecting the read-only npm restoration
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
`scripts/dependency-map.tsv` is a reference table of the producer-to-consumer edges between
|
|
7
7
|
Cratis repositories, the pack targets of each producer and the package IDs each ships.
|
|
8
|
-
`cratis-local-packages.sh`
|
|
9
|
-
in which order, and which IDs
|
|
10
|
-
from the consumer manifests (`Directory.Packages.props`,
|
|
8
|
+
`cratis-local-packages.sh` reads it (or the file in `CRATIS_LOCAL_MAP`, or `--map FILE`) to derive
|
|
9
|
+
one plan: which repositories to pack, in which order, and which IDs the feed must hold. It holds
|
|
10
|
+
no versions; read pins live from the consumer manifests (`Directory.Packages.props`,
|
|
11
|
+
`package.json`, `Dockerfile`).
|
|
11
12
|
|
|
12
13
|
## Rows
|
|
13
14
|
|
|
@@ -15,7 +16,7 @@ Tab-separated, `#` starts a comment.
|
|
|
15
16
|
|
|
16
17
|
| Row | Fields | Meaning |
|
|
17
18
|
| --- | --- | --- |
|
|
18
|
-
| `pack` | repo, eco, target, `PROP=VAL,...` | A pack target; the optional MSBuild properties are the `-p:` arguments the producer's own publish workflow passes |
|
|
19
|
+
| `pack` | repo, eco, target, `PROP=VAL,...` | A pack target; the optional MSBuild properties are the `-p:` arguments the producer's own publish workflow passes. `closure` prints each as one `--property NAME=VALUE` argument of that row's `pack-nuget` command |
|
|
19
20
|
| `ids` | repo, eco, comma-separated IDs | The packages the producer ships in that ecosystem |
|
|
20
21
|
| `uses` | consumer, producer, eco, manifest | An edge; the manifest is the pin file, or the Yarn project directory for npm |
|
|
21
22
|
| `image` | repo, dockerfile, tag suffix | A producer image a consumer builds on |
|
|
@@ -29,15 +30,30 @@ Tab-separated, `#` starts a comment.
|
|
|
29
30
|
(Fundamentals reaches Chronicle through Arc as well as directly).
|
|
30
31
|
- Pack upstream first. Overlay the earlier producer into the next repository with
|
|
31
32
|
`use-nuget`, pack that, then `restore` it. Use one stamp for the whole chain: `stamp`
|
|
32
|
-
takes
|
|
33
|
-
-
|
|
34
|
-
run
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
takes several producers, so generate it once and reuse the string.
|
|
34
|
+
- Read the closure: `closure [--train] CONSUMER=PATH PRODUCER=PATH...` (producers in dependency
|
|
35
|
+
order) prints the commands to run, in order, from the plan; `plan CONSUMER PRODUCER...` prints
|
|
36
|
+
the plan rows alone. Both exit 2 and print nothing when the plan is invalid.
|
|
37
|
+
- Pack properties are `pack-nuget --property NAME=VALUE` arguments, one per `-p:` of that command
|
|
38
|
+
only; never export them (an exported `IsPackaging=true` reaches Chronicle's nested `dotnet run`
|
|
39
|
+
code generator and fails with a multi-target error).
|
|
40
|
+
- `--train` prints the train stamp (see [train-mode.md](train-mode.md)) instead of a local stamp.
|
|
41
|
+
|
|
42
|
+
## Pack plan and verification scope
|
|
43
|
+
|
|
44
|
+
The plan is a set of `(producer, ecosystem)` pack rows, overlay rows (a producer and the upstream
|
|
45
|
+
producers it consumes) and consume rows (the consumer's own edges), derived from the `uses` rows
|
|
46
|
+
for the names you list. It is rejected (exit 2) for an unknown or repeated name, no edge, a wrong
|
|
47
|
+
order, or a planned pack without `ids` or `pack` rows.
|
|
48
|
+
|
|
49
|
+
- Run `verify-pack STAMP --consumer C --producer P...` once, after the last pack. It checks the
|
|
50
|
+
planned IDs exist at exactly STAMP and each planned NuGet dependency is exactly STAMP, [STAMP] or [STAMP, ) (an unreadable dependency element fails).
|
|
51
|
+
- After `use-*` and restore, `verify-assets CONSUMER-PATH STAMP --consumer C --producer P...`
|
|
52
|
+
checks the same plan inside the consumer: no planned ID resolved at another version, and no
|
|
53
|
+
producer the consumer uses left unconsumed. `--json` lists the resolved IDs only.
|
|
54
|
+
- Each command derives and checks the plan itself and keeps no state between runs.
|
|
55
|
+
- `verify-pack STAMP NAME...` and `verify-assets CONSUMER STAMP` without a plan are manual and
|
|
56
|
+
weaker: they check the named repositories' IDs, or every stamped package the consumer resolves.
|
|
41
57
|
|
|
42
58
|
## Facts about the chains
|
|
43
59
|
|
|
@@ -50,7 +66,8 @@ Tab-separated, `#` starts a comment.
|
|
|
50
66
|
- A repository you did not name keeps its released version: Stage reaches Arc, Chronicle
|
|
51
67
|
reaches Orleans. A binary break through that hop is a finding about the consumer's pins.
|
|
52
68
|
- The current Screenplay rows list only npm IDs although Stage and Studio consume its NuGet
|
|
53
|
-
packages;
|
|
69
|
+
packages; `closure` exits 2 for them until the map has the Screenplay NuGet `ids`. Read the
|
|
70
|
+
IDs from the producer's publish workflow and add them first.
|
|
54
71
|
|
|
55
72
|
## Extend it
|
|
56
73
|
|
|
@@ -28,6 +28,9 @@ dotnet restore path/to/consumer.slnx
|
|
|
28
28
|
dotnet build path/to/consumer.slnx -c Release --no-restore -warnaserror
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Transitive pinning stays as the consumer sets it; `--pin-transitive` forces it on so stamped IDs the
|
|
32
|
+
consumer does not pin still take effect (it can raise NU1109 floors).
|
|
33
|
+
|
|
31
34
|
The printed properties are `DirectoryPackagesPropsPath` and
|
|
32
35
|
`RestoreAdditionalProjectSources`. For an IDE, launch it from that shell, or pass
|
|
33
36
|
the printed `-p:` properties explicitly to its build. Verify restore uses the
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
## Pack the package, not a link
|
|
7
7
|
|
|
8
|
-
Requires node,
|
|
9
|
-
not run
|
|
8
|
+
Requires node, npm and Yarn 4. Run the producer's normal build first. The helper does
|
|
9
|
+
not build or run lifecycle scripts: it packs with `npm pack --ignore-scripts`, the publish
|
|
10
|
+
workflows' tool, so the tarball matches the published one.
|
|
10
11
|
|
|
11
12
|
```bash
|
|
12
13
|
stamp=$(bash "$local" stamp "$producer")
|
|
@@ -15,8 +16,9 @@ bash "$local" pack-npm "$producer" "$stamp"
|
|
|
15
16
|
|
|
16
17
|
It requires clean tracked workspace manifests, temporarily changes all non-private
|
|
17
18
|
workspace versions and packs actual tarballs. It restores those manifests on
|
|
18
|
-
success and failure
|
|
19
|
-
|
|
19
|
+
success and failure. Tarballs are named `<scope>-<name>-<version>.tgz` (`@cratis/foo` becomes
|
|
20
|
+
`cratis-foo-<version>.tgz`). `verify-assets --npm [--dir REL]` reads installed copies under
|
|
21
|
+
`node_modules`, or under `REL` when the consumer's Yarn project is a subfolder.
|
|
20
22
|
Inspect dependency ranges in packed manifests: the helper changes versions, not
|
|
21
23
|
arbitrary inter-package dependency constraints. Include every required workspace
|
|
22
24
|
and verify the consumer resolves the intended tarballs.
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
# Train mode
|
|
5
|
+
|
|
6
|
+
A local release train combines several branches into one pull request, one CI run and one
|
|
7
|
+
release. Verify a downstream repository against the upstream train's packages, not against
|
|
8
|
+
per-branch packages.
|
|
9
|
+
|
|
10
|
+
## Stamp
|
|
11
|
+
|
|
12
|
+
`stamp --train PRODUCER...` prints `X.Y.Z-train.g<sha7>`. X.Y.Z is the highest next patch over the
|
|
13
|
+
producers' latest reachable tags, so no stamped package sorts below a released pin. sha7 is the first
|
|
14
|
+
seven hex characters of the SHA-1 of the sorted `<repository> <full HEAD sha>` lines of every
|
|
15
|
+
producer (the repository is its origin's last path part, else its folder name). There is no minute: one
|
|
16
|
+
combined code state yields one stamp, a change in any producer changes it, and the order of the
|
|
17
|
+
producers does not matter. The grammar is fixed so tools can rely on it. Every command that takes a
|
|
18
|
+
stamp accepts it: `pack-*`, `use-*`, `verify-*`, `check` and `clean --stamp`. A train stamp
|
|
19
|
+
sorts above the released pin like a local stamp does.
|
|
20
|
+
|
|
21
|
+
## Rules
|
|
22
|
+
|
|
23
|
+
- One stamp per train state. The train tool records it in the train manifest; read it from there
|
|
24
|
+
instead of recomputing it for a different HEAD.
|
|
25
|
+
- A fold into the upstream train changes the upstream HEAD and so the stamp. Downstream
|
|
26
|
+
verification against the old stamp is stale: pack again, overlay the new stamp, rebuild and
|
|
27
|
+
re-verify. Do not carry a pass over a fold.
|
|
28
|
+
- Train versions are as local as local stamps. `check` reports `-train.g<sha7>` in changed
|
|
29
|
+
tracked, staged and untracked files, so a train version never reaches a commit or pull request.
|
|
30
|
+
- Upstream first. The downstream ships only after the upstream has released and the downstream pin
|
|
31
|
+
is bumped to the released version; run `check` (0 leaks) before the downstream push.
|
|
32
|
+
|
|
33
|
+
## Recording consumed versions
|
|
34
|
+
|
|
35
|
+
`verify-pack STAMP --consumer C --producer P... --json` and
|
|
36
|
+
`verify-assets CONSUMER STAMP [--npm] [--consumer C --producer P...] --json` print one line:
|
|
37
|
+
`{"stamp":"...","packages":[{"id":"...","version":"..."}]}`. Findings go to stderr and the exit status
|
|
38
|
+
is unchanged, so a train tool can record the consumed versions without scraping text.
|
|
39
|
+
|
|
40
|
+
## Clean
|
|
41
|
+
|
|
42
|
+
`clean --stamp STAMP` lists and removes the feed files, cache folders and Yarn cache entries of
|
|
43
|
+
that one train stamp; a local stamp is not matched. Review the dry run before `--apply`.
|
|
@@ -162,6 +162,10 @@ bash "$local" use-nuget "$stage" "$stamp"; bash "$local" use-nuget "$studio" "$s
|
|
|
162
162
|
bash "$local" use-npm "$studio" "$stamp" # at the workspace root, not the nested app
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
`closure` exits 2 for this chain until `dependency-map.tsv` has the Screenplay NuGet `ids` (it lists
|
|
166
|
+
only npm IDs); add them from the publish workflow first, then use the printed commands and its one
|
|
167
|
+
`verify-pack`.
|
|
168
|
+
|
|
165
169
|
Gates: Stage's frontend `npm ci`, test and build, then `dotnet build` in Debug and the repository's
|
|
166
170
|
own spec-shard helper; skip probes that need a live kernel or database and say so. Studio:
|
|
167
171
|
`dotnet build` of its solution in Debug with warnings as errors, then the frontend `yarn ci` and
|
|
@@ -171,7 +175,7 @@ own spec-shard helper; skip probes that need a live kernel or database and say s
|
|
|
171
175
|
- Pitfall: a repository without a committed lockfile fails `yarn install --immutable`; use plain
|
|
172
176
|
`yarn install` there.
|
|
173
177
|
- Pitfall: yarn hoists to the root `node_modules`; check and install at the root.
|
|
174
|
-
- Pitfall: the packed language tarball must contain its built bundles. `pack-npm` uses `
|
|
178
|
+
- Pitfall: the packed language tarball must contain its built bundles. `pack-npm` uses `npm pack`;
|
|
175
179
|
a package whose `files` lists a gitignored folder can ship only part of it. Compare the
|
|
176
180
|
tarball file list with the package's `dist` folder.
|
|
177
181
|
- Pitfall: multi-hop. Studio keeps its released Stage; a binary break is a finding.
|