@cratis/pi 7.102.0 → 7.103.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +26 -1
- package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +17 -0
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +17 -0
- package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +8 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +1 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/fast-path.md +7 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +1 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +40 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/response-continuations.md +1 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/worked-flow.md +1 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +3 -2
- package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +85 -1
- package/package.json +1 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Shared, report-identity-scoped Cobertura normalization. This does not attest
|
|
2
|
+
# that a report was collected from the current checkout.
|
|
3
|
+
|
|
4
|
+
function New-CoverageMap {
|
|
5
|
+
return [System.Collections.Generic.Dictionary[string, object]]::new([StringComparer]::Ordinal)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function Add-CoverageDiagnostic($Diagnostics, [string]$Code, [string]$Message, [string]$Path) {
|
|
9
|
+
$Diagnostics.Add([pscustomobject]@{ code = $Code; severity = 'warning'; message = $Message; path = $Path })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function Read-CoverageInteger([string]$Text, [string]$Name, [long]$Minimum = 0) {
|
|
13
|
+
$value = 0L
|
|
14
|
+
if (-not [long]::TryParse($Text, [Globalization.NumberStyles]::Integer,
|
|
15
|
+
[Globalization.CultureInfo]::InvariantCulture, [ref]$value) -or $value -lt $Minimum) {
|
|
16
|
+
throw [FormatException]::new("Invalid $Name value '$Text'.")
|
|
17
|
+
}
|
|
18
|
+
return $value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Add-CoverageLine($Map, $Node, [string]$Key, [string]$ReportId) {
|
|
22
|
+
$number = Read-CoverageInteger $Node.GetAttribute('number') 'line number' 1
|
|
23
|
+
$hits = Read-CoverageInteger $Node.GetAttribute('hits') 'line hits'
|
|
24
|
+
$lineKey = "$Key`n$number"
|
|
25
|
+
if (-not $Map.ContainsKey($lineKey)) {
|
|
26
|
+
$Map[$lineKey] = @{
|
|
27
|
+
Covered = $false
|
|
28
|
+
NonBranch = $false
|
|
29
|
+
ConflictingBranches = $false
|
|
30
|
+
Branches = [System.Collections.Generic.List[object]]::new()
|
|
31
|
+
Observations = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
$entry = $Map[$lineKey]
|
|
35
|
+
$entry.Covered = $entry.Covered -or $hits -gt 0
|
|
36
|
+
if (-not $entry.Observations.Add($ReportId)) { return }
|
|
37
|
+
$branch = $Node.GetAttribute('branch')
|
|
38
|
+
if ($branch -and $branch -notin @('true', 'false')) {
|
|
39
|
+
throw [FormatException]::new("Invalid branch flag '$branch'.")
|
|
40
|
+
}
|
|
41
|
+
if ($branch -ne 'true') {
|
|
42
|
+
$entry.NonBranch = $true
|
|
43
|
+
if ($entry.Branches.Count) { $entry.ConflictingBranches = $true }
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if ($entry.NonBranch) { $entry.ConflictingBranches = $true }
|
|
47
|
+
$raw = $Node.GetAttribute('condition-coverage')
|
|
48
|
+
if ($raw -notmatch '^\s*[\d.]+%\s*\((\d+)/(\d+)\)\s*$') {
|
|
49
|
+
$entry.Branches.Add(@{ Total = $null; Covered = $null; Conditions = $null })
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
$covered = Read-CoverageInteger $Matches[1] 'covered branches'
|
|
53
|
+
$total = Read-CoverageInteger $Matches[2] 'total branches' 1
|
|
54
|
+
if ($covered -gt $total) { throw [FormatException]::new('Covered branches exceed total branches.') }
|
|
55
|
+
|
|
56
|
+
# Cobertura jump conditions describe two outcomes, not which outcome a
|
|
57
|
+
# 50% observation took. Keep that ambiguity even when condition ids match.
|
|
58
|
+
$conditions = New-CoverageMap
|
|
59
|
+
$nodes = @($Node.SelectNodes('conditions/condition'))
|
|
60
|
+
$conditionSum = 0
|
|
61
|
+
$usable = $nodes.Count -gt 0 -and $total -eq 2 * $nodes.Count
|
|
62
|
+
foreach ($condition in $nodes) {
|
|
63
|
+
$id = $condition.GetAttribute('number')
|
|
64
|
+
$coverage = $condition.GetAttribute('coverage')
|
|
65
|
+
if ($condition.GetAttribute('type') -ne 'jump' -or -not $id -or
|
|
66
|
+
$conditions.ContainsKey($id) -or $coverage -notmatch '^(0|50|100)(?:\.0+)?%$') {
|
|
67
|
+
$usable = $false
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
$count = [int]$Matches[1] / 50
|
|
71
|
+
$conditions[$id] = $count
|
|
72
|
+
$conditionSum += $count
|
|
73
|
+
}
|
|
74
|
+
if (-not $usable -or $conditionSum -ne $covered) { $conditions = $null }
|
|
75
|
+
$entry.Branches.Add(@{ Total = $total; Covered = $covered; Conditions = $conditions })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function Get-CoverageMeasure($Lines) {
|
|
79
|
+
$coveredLines = @($Lines.Values | Where-Object Covered).Count
|
|
80
|
+
$linePercent = if ($Lines.Count) { [math]::Round(100.0 * $coveredLines / $Lines.Count, 1) } else { $null }
|
|
81
|
+
$lower = 0L
|
|
82
|
+
$upper = 0L
|
|
83
|
+
$total = 0L
|
|
84
|
+
$unknown = $Lines.Count -eq 0
|
|
85
|
+
foreach ($line in $Lines.Values) {
|
|
86
|
+
$observations = @($line.Branches)
|
|
87
|
+
if (-not $observations.Count) { continue }
|
|
88
|
+
if ($line.ConflictingBranches -or @($observations | Where-Object { $null -eq $_.Total }).Count) {
|
|
89
|
+
$unknown = $true
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
$totals = @($observations | ForEach-Object { $_.Total } | Sort-Object -Unique)
|
|
93
|
+
if ($totals.Count -ne 1 -or $null -eq $totals[0]) { $unknown = $true; continue }
|
|
94
|
+
$capacity = $totals[0]
|
|
95
|
+
$total += $capacity
|
|
96
|
+
$useConditions = $true
|
|
97
|
+
$conditionIds = $null
|
|
98
|
+
foreach ($observation in $observations) {
|
|
99
|
+
if ($null -eq $observation.Conditions) { $useConditions = $false; break }
|
|
100
|
+
$ids = @($observation.Conditions.Keys | Sort-Object -CaseSensitive)
|
|
101
|
+
$identity = ConvertTo-Json -InputObject $ids -Compress
|
|
102
|
+
if ($null -ne $conditionIds -and $conditionIds -cne $identity) { $useConditions = $false; break }
|
|
103
|
+
$conditionIds = $identity
|
|
104
|
+
}
|
|
105
|
+
if ($useConditions) {
|
|
106
|
+
foreach ($id in $observations[0].Conditions.Keys) {
|
|
107
|
+
$counts = @($observations | ForEach-Object { $_.Conditions[$id] })
|
|
108
|
+
$lower += ($counts | Measure-Object -Maximum).Maximum
|
|
109
|
+
$upper += [math]::Min(2, ($counts | Measure-Object -Sum).Sum)
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
$counts = @($observations | ForEach-Object { $_.Covered })
|
|
113
|
+
$lower += ($counts | Measure-Object -Maximum).Maximum
|
|
114
|
+
$upper += [math]::Min($capacity, ($counts | Measure-Object -Sum).Sum)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
$branchStatus = if ($unknown) { 'unknown' } elseif (-not $total) { 'not-applicable' } elseif ($lower -eq $upper) { 'exact' } else { 'bounded' }
|
|
118
|
+
return [pscustomobject]@{
|
|
119
|
+
TotalLines = $Lines.Count
|
|
120
|
+
CoveredLines = $coveredLines
|
|
121
|
+
LineCoverage = $linePercent
|
|
122
|
+
LineCoverageStatus = $(if ($Lines.Count) { 'exact' } else { 'unknown' })
|
|
123
|
+
TotalBranches = $(if ($unknown) { $null } else { $total })
|
|
124
|
+
CoveredBranches = $(if ($branchStatus -in @('exact', 'not-applicable')) { [long]$lower } else { $null })
|
|
125
|
+
BranchCoverage = $(if ($branchStatus -eq 'exact') { [math]::Round(100.0 * $lower / $total, 1) } else { $null })
|
|
126
|
+
BranchCoverageStatus = $branchStatus
|
|
127
|
+
CoveredBranchesLower = $(if (-not $unknown) { [long]$lower } else { $null })
|
|
128
|
+
CoveredBranchesUpper = $(if (-not $unknown) { [long]$upper } else { $null })
|
|
129
|
+
BranchCoverageLower = $(if (-not $unknown -and $total) { [math]::Round(100.0 * $lower / $total, 1) } else { $null })
|
|
130
|
+
BranchCoverageUpper = $(if (-not $unknown -and $total) { [math]::Round(100.0 * $upper / $total, 1) } else { $null })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function Get-CoverageData([string[]]$Paths) {
|
|
135
|
+
if (-not $Paths.Count) { throw [ArgumentException]::new('At least one Cobertura report is required.') }
|
|
136
|
+
$methods = New-CoverageMap
|
|
137
|
+
$classLines = New-CoverageMap
|
|
138
|
+
$methodLines = New-CoverageMap
|
|
139
|
+
$inputs = [System.Collections.Generic.List[object]]::new()
|
|
140
|
+
$reports = [System.Collections.Generic.List[object]]::new()
|
|
141
|
+
$diagnostics = [System.Collections.Generic.List[object]]::new()
|
|
142
|
+
$seen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
|
143
|
+
$allClassesHaveLines = $true
|
|
144
|
+
foreach ($path in ($Paths | Sort-Object -Unique -CaseSensitive)) {
|
|
145
|
+
$resolved = (Get-Item -LiteralPath $path -ErrorAction Stop).FullName
|
|
146
|
+
$bytes = [IO.File]::ReadAllBytes($resolved)
|
|
147
|
+
$hash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($bytes)).ToLowerInvariant()
|
|
148
|
+
$inputs.Add([pscustomobject]@{ path = $resolved; sha256 = $hash })
|
|
149
|
+
if (-not $seen.Add($hash)) { continue }
|
|
150
|
+
$xml = [xml]::new()
|
|
151
|
+
$xml.XmlResolver = $null
|
|
152
|
+
$stream = [IO.MemoryStream]::new($bytes, $false)
|
|
153
|
+
try { $xml.Load($stream) } finally { $stream.Dispose() }
|
|
154
|
+
if ($xml.DocumentElement.Name -ne 'coverage') { throw [FormatException]::new("Not a Cobertura report: $path") }
|
|
155
|
+
$coverageRoot = $xml.DocumentElement
|
|
156
|
+
$report = @{ line = $null; branch = $null }
|
|
157
|
+
foreach ($metric in @('line', 'branch')) {
|
|
158
|
+
$prefix = if ($metric -eq 'line') { 'lines' } else { 'branches' }
|
|
159
|
+
$valid = $coverageRoot.GetAttribute("$prefix-valid")
|
|
160
|
+
$covered = $coverageRoot.GetAttribute("$prefix-covered")
|
|
161
|
+
if ($valid -or $covered) {
|
|
162
|
+
$denominator = Read-CoverageInteger $valid "$prefix-valid"
|
|
163
|
+
$numerator = Read-CoverageInteger $covered "$prefix-covered"
|
|
164
|
+
if ($numerator -gt $denominator) { throw [FormatException]::new("$prefix-covered exceeds $prefix-valid.") }
|
|
165
|
+
$report[$metric] = @{ Total = $denominator; Covered = $numerator }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
$reports.Add($report)
|
|
169
|
+
$reportMethods = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
|
170
|
+
foreach ($class in $xml.SelectNodes('/coverage/packages/package/classes/class')) {
|
|
171
|
+
$filename = $class.GetAttribute('filename').Replace('\', '/')
|
|
172
|
+
$className = $class.GetAttribute('name')
|
|
173
|
+
$module = $class.ParentNode.ParentNode.GetAttribute('name')
|
|
174
|
+
if (-not $filename -or -not $className) { throw [FormatException]::new('Class name and filename are required.') }
|
|
175
|
+
$sourceKey = ConvertTo-Json -InputObject @($module, $filename) -Compress
|
|
176
|
+
$classNodes = @($class.SelectNodes('lines/line'))
|
|
177
|
+
if (-not $classNodes.Count) { $allClassesHaveLines = $false }
|
|
178
|
+
foreach ($line in $classNodes) { Add-CoverageLine $classLines $line $sourceKey $hash }
|
|
179
|
+
foreach ($method in $class.SelectNodes('methods/method')) {
|
|
180
|
+
$name = $method.GetAttribute('name')
|
|
181
|
+
if (-not $name) { throw [FormatException]::new('Method name is required.') }
|
|
182
|
+
$signature = $method.GetAttribute('signature')
|
|
183
|
+
$key = ConvertTo-Json -InputObject @($module, $filename, $className, $name, $signature) -Compress
|
|
184
|
+
if (-not $reportMethods.Add($key)) {
|
|
185
|
+
throw [FormatException]::new("Duplicate method identity for $className.$name; signatures or source mapping are ambiguous.")
|
|
186
|
+
}
|
|
187
|
+
if (-not $methods.ContainsKey($key)) {
|
|
188
|
+
$methods[$key] = @{
|
|
189
|
+
Module = $module; Class = $className; Method = $name; Signature = $signature; File = $filename
|
|
190
|
+
Lines = (New-CoverageMap)
|
|
191
|
+
Complexities = [System.Collections.Generic.HashSet[long]]::new()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
$entry = $methods[$key]
|
|
195
|
+
$complexity = $method.GetAttribute('complexity')
|
|
196
|
+
if ($complexity) { [void]$entry.Complexities.Add((Read-CoverageInteger $complexity 'complexity' 1)) }
|
|
197
|
+
foreach ($line in $method.SelectNodes('lines/line')) {
|
|
198
|
+
Add-CoverageLine $entry.Lines $line '' $hash
|
|
199
|
+
Add-CoverageLine $methodLines $line $sourceKey $hash
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
$result = [System.Collections.Generic.List[object]]::new()
|
|
205
|
+
foreach ($entry in $methods.Values) {
|
|
206
|
+
$measure = Get-CoverageMeasure $entry.Lines
|
|
207
|
+
$complexity = if ($entry.Complexities.Count -eq 1) { @($entry.Complexities)[0] } else { $null }
|
|
208
|
+
$item = [ordered]@{
|
|
209
|
+
Class = $entry.Class; Method = $entry.Method; Signature = $entry.Signature; File = $entry.File
|
|
210
|
+
Module = $entry.Module; Complexity = $complexity
|
|
211
|
+
ComplexityStatus = $(if ($null -ne $complexity) { 'exact' } else { 'unknown' })
|
|
212
|
+
}
|
|
213
|
+
foreach ($property in $measure.PSObject.Properties) { $item[$property.Name] = $property.Value }
|
|
214
|
+
$item.UncoveredLines = $measure.TotalLines - $measure.CoveredLines
|
|
215
|
+
if ($null -eq $complexity) {
|
|
216
|
+
Add-CoverageDiagnostic $diagnostics 'COMPLEXITY_UNKNOWN' "Missing or conflicting complexity for $($entry.Class).$($entry.Method)." $entry.File
|
|
217
|
+
}
|
|
218
|
+
if ($measure.LineCoverageStatus -eq 'unknown') {
|
|
219
|
+
Add-CoverageDiagnostic $diagnostics 'METHOD_LINES_UNKNOWN' "No executable-line evidence for $($entry.Class).$($entry.Method)." $entry.File
|
|
220
|
+
}
|
|
221
|
+
if ($measure.BranchCoverageStatus -in @('unknown', 'bounded')) {
|
|
222
|
+
Add-CoverageDiagnostic $diagnostics 'BRANCH_UNION_UNCERTAIN' "Branch outcomes cannot be uniquely merged for $($entry.Class).$($entry.Method)." $entry.File
|
|
223
|
+
}
|
|
224
|
+
$result.Add([pscustomobject]$item)
|
|
225
|
+
}
|
|
226
|
+
$aggregate = Get-CoverageMeasure $classLines
|
|
227
|
+
$aggregateScope = 'class-lines'
|
|
228
|
+
if (-not $allClassesHaveLines -or -not $classLines.Count) {
|
|
229
|
+
$aggregateScope = if ($reports.Count -eq 1) { 'report' } else { 'method-lines-only' }
|
|
230
|
+
$aggregate = [pscustomobject]@{ LineCoverage = $null; LineCoverageStatus = 'unknown'; BranchCoverage = $null; BranchCoverageStatus = 'unknown' }
|
|
231
|
+
if ($reports.Count -eq 1) {
|
|
232
|
+
foreach ($metric in @('line', 'branch')) {
|
|
233
|
+
$counter = $reports[0][$metric]
|
|
234
|
+
$name = if ($metric -eq 'line') { 'LineCoverage' } else { 'BranchCoverage' }
|
|
235
|
+
if ($null -ne $counter) {
|
|
236
|
+
$aggregate.$name = if ($counter.Total) { [math]::Round(100.0 * $counter.Covered / $counter.Total, 1) } else { $null }
|
|
237
|
+
$aggregate."${name}Status" = if ($counter.Total) { 'exact' } else { 'not-applicable' }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (-not $result.Count) {
|
|
243
|
+
Add-CoverageDiagnostic $diagnostics 'METHODS_UNAVAILABLE' 'The report has no method-level coverage evidence.' ''
|
|
244
|
+
}
|
|
245
|
+
return [pscustomobject]@{
|
|
246
|
+
Methods = @($result | Sort-Object -CaseSensitive Module, File, Class, Method, Signature)
|
|
247
|
+
Inputs = @($inputs)
|
|
248
|
+
Diagnostics = $diagnostics
|
|
249
|
+
Summary = [ordered]@{
|
|
250
|
+
overall_line_coverage = $aggregate.LineCoverage
|
|
251
|
+
overall_line_coverage_status = $aggregate.LineCoverageStatus
|
|
252
|
+
overall_branch_coverage = $aggregate.BranchCoverage
|
|
253
|
+
overall_branch_coverage_status = $aggregate.BranchCoverageStatus
|
|
254
|
+
coverage_scope = $aggregateScope
|
|
255
|
+
method_line_coverage = (Get-CoverageMeasure $methodLines).LineCoverage
|
|
256
|
+
total_methods = $result.Count
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function Write-CoverageJson([string]$Tool, [string]$Status, $Scope, $Inputs, $Diagnostics, $Data) {
|
|
262
|
+
[ordered]@{
|
|
263
|
+
schema_version = '1'; tool = $Tool; tool_version = '1.0'; status = $Status
|
|
264
|
+
scope = $Scope; inputs = @($Inputs); diagnostics = @($Diagnostics); data = $Data
|
|
265
|
+
} | ConvertTo-Json -Depth 30
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function Write-CoverageDiagnostics($Diagnostics) {
|
|
269
|
+
foreach ($diagnostic in $Diagnostics) {
|
|
270
|
+
[Console]::Error.WriteLine("$($diagnostic.code): $($diagnostic.message)")
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Extract Cobertura method coverage without inventing missing measurements.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Legacy output is a JSON array; diagnostics and summaries go to stderr.
|
|
6
|
+
Use -OutputFormat json-v1 for provenance, null/unknown metrics and branch bounds.
|
|
7
|
+
Merging is scoped to reported module/file/class/signature identities, not an
|
|
8
|
+
attestation of source revision. Duplicate report bytes are idempotent.
|
|
9
|
+
Exit codes: 0 complete, 2 invalid input, 3 incomplete/unrepresentable evidence.
|
|
10
|
+
#>
|
|
11
|
+
param(
|
|
12
|
+
[Parameter(Mandatory=$true)][string[]]$CoberturaPath,
|
|
13
|
+
[ValidateRange(0,100)][int]$CoverageThreshold = 80,
|
|
14
|
+
[ValidateRange(0,100)][int]$BranchThreshold = 70,
|
|
15
|
+
[ValidateSet('uncovered', 'below-threshold', 'all')][string]$Filter = 'all',
|
|
16
|
+
[ValidateSet('legacy', 'json-v1')][string]$OutputFormat = 'legacy'
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
. "$PSScriptRoot\CoverageData.ps1"
|
|
21
|
+
} catch {
|
|
22
|
+
$diagnostic = @{ code = 'DEPENDENCY_UNAVAILABLE'; severity = 'error'; message = $_.Exception.Message; path = "$PSScriptRoot\CoverageData.ps1" }
|
|
23
|
+
[Console]::Error.WriteLine("$($diagnostic.code): $($diagnostic.message)")
|
|
24
|
+
if ($OutputFormat -eq 'json-v1') {
|
|
25
|
+
@{
|
|
26
|
+
schema_version = '1'; tool = 'Extract-MethodCoverage'; tool_version = '1.0'; status = 'error'
|
|
27
|
+
scope = @{}; inputs = @(); diagnostics = @($diagnostic); data = @{}
|
|
28
|
+
} | ConvertTo-Json -Depth 5
|
|
29
|
+
}
|
|
30
|
+
exit 2
|
|
31
|
+
}
|
|
32
|
+
$scope = @{ identity = 'reported-module-file-class-signature'; source_revision = $null; filter = $Filter }
|
|
33
|
+
try {
|
|
34
|
+
$coverage = Get-CoverageData $CoberturaPath
|
|
35
|
+
} catch {
|
|
36
|
+
$diagnostics = @([pscustomobject]@{ code = 'INVALID_INPUT'; severity = 'error'; message = $_.Exception.Message })
|
|
37
|
+
Write-CoverageDiagnostics $diagnostics
|
|
38
|
+
if ($OutputFormat -eq 'json-v1') { Write-CoverageJson 'Extract-MethodCoverage' 'error' $scope @() $diagnostics @{} }
|
|
39
|
+
exit 2
|
|
40
|
+
}
|
|
41
|
+
$partial = -not $coverage.Methods.Count -or @($coverage.Methods | Where-Object {
|
|
42
|
+
$_.LineCoverageStatus -eq 'unknown' -or $_.BranchCoverageStatus -in @('unknown', 'bounded') -or $_.ComplexityStatus -eq 'unknown'
|
|
43
|
+
}).Count -gt 0
|
|
44
|
+
$methods = @($coverage.Methods | Where-Object {
|
|
45
|
+
if ($Filter -eq 'all') { return $true }
|
|
46
|
+
if ($_.LineCoverageStatus -eq 'unknown' -or $_.BranchCoverageStatus -in @('unknown', 'bounded')) { return $true }
|
|
47
|
+
if ($Filter -eq 'uncovered') { return $_.LineCoverage -eq 0 }
|
|
48
|
+
return (100.0 * $_.CoveredLines / $_.TotalLines) -lt $CoverageThreshold -or
|
|
49
|
+
($_.TotalBranches -gt 0 -and (100.0 * $_.CoveredBranches / $_.TotalBranches) -lt $BranchThreshold)
|
|
50
|
+
} | Sort-Object -Property @{Expression='UncoveredLines';Descending=$true}, @{Expression='LineCoverage';Descending=$false}, Class, Method, File, Signature)
|
|
51
|
+
Write-CoverageDiagnostics $coverage.Diagnostics
|
|
52
|
+
if ($OutputFormat -eq 'json-v1') {
|
|
53
|
+
Write-CoverageJson 'Extract-MethodCoverage' $(if ($partial) { 'partial' } else { 'complete' }) $scope $coverage.Inputs $coverage.Diagnostics @{
|
|
54
|
+
methods = $methods; summary = $coverage.Summary
|
|
55
|
+
}
|
|
56
|
+
} elseif (-not $partial) {
|
|
57
|
+
$legacy = @($methods | Select-Object Class, Method, Signature, File, Complexity, LineCoverage,
|
|
58
|
+
@{Name='BranchCoverage';Expression={if ($_.BranchCoverageStatus -eq 'not-applicable') { 0 } else { $_.BranchCoverage }}},
|
|
59
|
+
CoveredLines, TotalLines, UncoveredLines, CoveredBranches, TotalBranches)
|
|
60
|
+
ConvertTo-Json -InputObject $legacy -Depth 5
|
|
61
|
+
[Console]::Error.WriteLine("METHODS_FILTERED:$($methods.Count)")
|
|
62
|
+
[Console]::Error.WriteLine("UNCOVERED_METHODS:$(@($methods | Where-Object { $_.LineCoverageStatus -eq 'exact' -and $_.LineCoverage -eq 0 }).Count)")
|
|
63
|
+
}
|
|
64
|
+
if ($partial) { exit 3 }
|
|
65
|
+
exit 0
|
|
@@ -121,7 +121,32 @@ At most one command property may carry `identifier`. It names the value the
|
|
|
121
121
|
runtime uses as the event source id, so **choosing it is choosing the stream
|
|
122
122
|
boundary** — the highest-consequence decision in the slice. Leave it out only
|
|
123
123
|
when the runtime should allocate a fresh `Uuid` (a command that creates
|
|
124
|
-
something whose identity the caller does not supply)
|
|
124
|
+
something whose identity the caller does not supply); for a create command prefer
|
|
125
|
+
`generated identifier` below, which also keeps the id out of the rendered form.
|
|
126
|
+
|
|
127
|
+
**A command that creates something has no caller-supplied id.** Declare
|
|
128
|
+
`<id> <IdConcept> generated identifier` (ESM v7, Screenplay 4.68.0 or later) and `returns <id>` when the
|
|
129
|
+
caller needs it back. Symptom of getting it wrong: `organizationId OrganizationId identifier` on a create
|
|
130
|
+
command makes the rendered form ask the user to type an Organization Id. Keep plain `identifier` for
|
|
131
|
+
commands that act on an existing stream (the id comes from the selected row). Specifications pin the
|
|
132
|
+
allocated value with `for "<value>"` in `when` and `then` (see the generated-values reference). Read
|
|
133
|
+
[generated values](references/generated-values-and-responses.md) before writing one.
|
|
134
|
+
|
|
135
|
+
```screenplay excerpt
|
|
136
|
+
command RegisterOrganization
|
|
137
|
+
organizationId OrganizationId generated identifier
|
|
138
|
+
name OrganizationName
|
|
139
|
+
produces event OrganizationRegistered
|
|
140
|
+
name OrganizationName = name
|
|
141
|
+
returns organizationId
|
|
142
|
+
specification RegistersAnOrganization
|
|
143
|
+
when RegisterOrganization
|
|
144
|
+
for "11111111-1111-1111-1111-111111111111"
|
|
145
|
+
name = "Acme"
|
|
146
|
+
then OrganizationRegistered
|
|
147
|
+
for "11111111-1111-1111-1111-111111111111"
|
|
148
|
+
name = "Acme"
|
|
149
|
+
```
|
|
125
150
|
|
|
126
151
|
- A second `identifier` on the same command is an error: *only one property can be
|
|
127
152
|
the identifier*.
|
|
@@ -282,6 +282,23 @@ of a document a reader cannot check at a glance.
|
|
|
282
282
|
|
|
283
283
|
Read [pdl-grammar.md](references/pdl-grammar.md) when checking exact syntax, interpreting a projection diagnostic code, or needing a worked example.
|
|
284
284
|
|
|
285
|
+
## `STAGE-ESM-017` on a projection passes `validate` and fails at `render`
|
|
286
|
+
|
|
287
|
+
Symptom: `cratis screenplay validate --executable` exits 0, then `cratis render` (or `render --check`)
|
|
288
|
+
refuses with `STAGE-ESM-017` and a generic message whose location is a `sem1:<hash>`, not a source line
|
|
289
|
+
(seen on Stage 4.192.0 and CLI 3.94.0). Cause: `validate --executable` binds only Screenplay's model;
|
|
290
|
+
Stage's admission runs only in `render`. Check every `from` block in turn:
|
|
291
|
+
|
|
292
|
+
1. **Key.** An event property or the event source. A root `from` also accepts a constant text key (nonempty, only letters, digits, `_`, space and `. / : * + -`); any other literal key, and a literal key outside a root, is refused.
|
|
293
|
+
2. **Do not overwrite the key.** A mapping onto the key property must restate the key itself (`from E` keyed by the event source with `berthId = $eventSourceId`, or the key's own event property); mapping a different value onto it is refused (`MatchesKey`). Simplest is to leave the key property unmapped.
|
|
294
|
+
3. **`$eventSourceId` into a non-key target** is admitted in a `from` block keyed by an event property, when every command and reaction producing the event addresses it with a non-collection value of exactly the target's type (optionality ignored). It is refused in `join`s, for a mismatched type, and for an event nothing produces.
|
|
295
|
+
4. **No `$context.*`.** Map an event property instead and have the command set it. The compiler may refuse it first (`PLAY0152`); Stage refuses a context value in a projection with `STAGE-ESM-017`.
|
|
296
|
+
5. **Targets.** A target path through an optional or non-composite intermediate is refused; an optional leaf and a path through required composites are fine. The event property mapped from must exist on the event.
|
|
297
|
+
|
|
298
|
+
Bisect: run `cratis render <root> --target cratis --name <App> --destination <dir> --check` (plans,
|
|
299
|
+
writes nothing), comment out `from` blocks one at a time until the error clears, then apply the list to
|
|
300
|
+
the block that cleared it. Reference: `cratis-stage-rendering-and-sandbox` `references/admission.md`.
|
|
301
|
+
|
|
285
302
|
## Verify
|
|
286
303
|
|
|
287
304
|
- [ ] Standalone `screenplay <model> --warnaserror` reports zero errors and zero
|
|
@@ -178,6 +178,23 @@ Adapters and non-renderable scope follow the contract rules of `cratis-applicati
|
|
|
178
178
|
Never `.cratis-render/` (the journal directory) and never `.ai-work/`.
|
|
179
179
|
- Limits stated in this text are prompt policy unless a guard enforces them; say so.
|
|
180
180
|
|
|
181
|
+
## Hand-written query logic and re-render
|
|
182
|
+
|
|
183
|
+
Symptom: a today-relative query (overdue, due today) was written by editing the body of a generated query
|
|
184
|
+
file; the next `render` refused with `Managed artifact '<file>' was modified by the user; pass --force to
|
|
185
|
+
replace it.` Query filters are equality only and there is no `today` in a filter or projection, so this
|
|
186
|
+
logic is gap-fill. Cause: a managed file's hash is in `.cratis-render.json`, and an edited one blocks the
|
|
187
|
+
whole re-render. What to do:
|
|
188
|
+
|
|
189
|
+
- Never edit a managed file. Put the logic in a new unmanaged file, for example `Customizations/<Name>.cs`
|
|
190
|
+
(compiled with the project, never replaced by `render`), with its own endpoint or query class, and record it
|
|
191
|
+
as gap-fill (`references/gap-fill-handoff.md`).
|
|
192
|
+
- `--force` replaces the edited managed file with the generated one and discards the edit; use it only with
|
|
193
|
+
approval that names the file.
|
|
194
|
+
- Screens reported as `PLAY0269` are information that the backend profile defers them. They are not
|
|
195
|
+
proof of a rendered screen: check the running app (`cratis-screenplay-toolchain` `references/fast-path.md`)
|
|
196
|
+
and list any authored screen that is missing as not rendered.
|
|
197
|
+
|
|
181
198
|
## Gate
|
|
182
199
|
|
|
183
200
|
Before reporting done:
|
|
@@ -277,11 +277,19 @@ For view specifications:
|
|
|
277
277
|
- [ ] Queries cover out-of-rule concept arguments with `then error`; a valid
|
|
278
278
|
absent key uses `then no result`. Projections still cannot reject events.
|
|
279
279
|
|
|
280
|
+
## Run the specs before calling the work done
|
|
281
|
+
|
|
282
|
+
Symptom: a model delivered with specifications that were never run. `screenplay test <root>` runs the
|
|
283
|
+
reference evaluator; after a render, run `dotnet test` (Debug) in the destination for the generated
|
|
284
|
+
specification classes. Report both results separately (`cratis-screenplay-toolchain` V4 and V5.tests); a
|
|
285
|
+
build that succeeds is not a test run, and `unbound`/`unsupported` is not a pass.
|
|
286
|
+
|
|
280
287
|
## Verify
|
|
281
288
|
|
|
282
289
|
- [ ] Validate the complete root with warnings as errors, naming tool/version.
|
|
283
290
|
Standalone tool version: `cratis-screenplay-toolchain/SKILL.md`. Run specification examples through
|
|
284
291
|
`screenplay test`; expected unadmitted syntax reports unbound, not passed.
|
|
292
|
+
- [ ] `screenplay test` and, after a render, `dotnet test` were run and their counts reported.
|
|
285
293
|
- [ ] Executable diagnostics are clean too: `PLAY0350`, `PLAY0352`, `PLAY0388`,
|
|
286
294
|
`PLAY0389` and `PLAY0273` are only reported at binding.
|
|
287
295
|
- [ ] Every slice has at least one boundary specification.
|
|
@@ -218,7 +218,7 @@ unresolved questions: the standalone tool has `--open-questions fail`; `cratis s
|
|
|
218
218
|
does through `--open-questions report|fail`. It is separate from compilation and `--warnaserror`.
|
|
219
219
|
Requires Cratis CLI 3.60.0 or later (`cratis --version`) for that option; older CLIs lack it.
|
|
220
220
|
|
|
221
|
-
Read [fast path](references/fast-path.md) for the ordered requirements-to-running-app checklist (validate, `render --check`, render, build, run).
|
|
221
|
+
Read [fast path](references/fast-path.md) for the ordered requirements-to-running-app checklist (validate, `render --check`, render, build, run, check in a browser, run the specs).
|
|
222
222
|
|
|
223
223
|
## Route near misses
|
|
224
224
|
|
|
@@ -20,5 +20,12 @@ Run these in order and stop at the first failure; each step catches what the one
|
|
|
20
20
|
8. **Frontend:** in `<dir>`, `yarn` then `yarn dev` (npm: `npm install`, `npm run dev`); open the URL Vite
|
|
21
21
|
prints. It proxies API calls to the backend.
|
|
22
22
|
|
|
23
|
+
9. **Verify in a browser:** open the Vite URL, load every screen the model authored (sidebar and each screen title),
|
|
24
|
+
run one create command and one query, and read the console for errors. The check is the app, not the build:
|
|
25
|
+
a screen reported as `PLAY0269` (deferred from the backend profile, information only) is not evidence that
|
|
26
|
+
it was rendered. List any authored screen that is absent as not rendered; do not call it delivered.
|
|
27
|
+
10. **Specs:** `dotnet test` in `<dir>` (the generated specifications) and `screenplay test <root>`; see
|
|
28
|
+
`cratis-screenplay-specifications`.
|
|
29
|
+
|
|
23
30
|
The rendered `README.md` repeats steps 6-8 for the pinned scaffold. Re-render and gap-fill rules:
|
|
24
31
|
`cratis-screenplay-render-and-gap-fill`.
|
|
@@ -72,3 +72,4 @@ Traps 39 to 44 are tool traps.
|
|
|
72
72
|
45. A reducer cannot be re-keyed: its instances follow the event-source id, so a query `by` another identity type warns PLAY0690 (`--warnaserror` fails it; editors only show it when the host supplies native findings). Aggregate by a payload value with a projection keyed on each `from`, or a join: projections [reducer identity](../../cratis-screenplay-projections/references/reducer-identity.md).
|
|
73
73
|
46. Enum members are lowercase identifiers (`hourly`, not `Hourly`; PLAY0009). A specification states every property of the command and events, optional ones too (PLAY0524). A read model with two `*Id` properties needs one keyed query, `query OrderById => Order optional` with `by orderId String` (PLAY0268 otherwise). A `require` message goes on the next indented line (trap 38). Screen list queries are `observable`.
|
|
74
74
|
47. Cratis events have no optional properties (STAGE-ESM-005), and `validate --executable` does not catch it: run `cratis render <folder> --target cratis --name <App> --destination <dir> --check` right after validate, since it runs the renderer's admission and writes nothing.
|
|
75
|
+
48. `STAGE-ESM-017` on a projection also passes `validate --executable` and shows only a `sem1:` location, not the `from` block. Run `render --check` after validate, then follow the checklist and bisect in projections `SKILL.md` ("`STAGE-ESM-017` on a projection").
|
|
@@ -270,6 +270,44 @@ precedence, PLAY0485 and examples.
|
|
|
270
270
|
|
|
271
271
|
A `ui profile` selects the application shell with `root template` (an own layout or application-scope screen template, or `blueprint <B> <Name>` with an optional `version` pin), its shell `option` lines, an `appearance` block (`color scheme`, `accent`), the template new screens start from (`new screens use template`), the theme and the `packages`, listed in override-priority order (a later package shadows an earlier one); `target platform` and `target size` accept any identifier, so a typo is silent. The legacy `layout <Name>` line inside a profile still reads but warns `PLAY0791`: write `root template`. `PLAY0790` to `PLAY0799` check the shell. Read [references/ui-profile-and-theme.md](references/ui-profile-and-theme.md) when writing a profile or theme, or fixing a shell diagnostic.
|
|
272
272
|
|
|
273
|
+
## A deliverable app declares its identity, titles and labels
|
|
274
|
+
|
|
275
|
+
Symptom: a rendered app titled "Cratis Stage" whose buttons and dialogs show raw command names. The renderer
|
|
276
|
+
defaults to those when the model is silent, so declare, in every deliverable app:
|
|
277
|
+
|
|
278
|
+
- the layout's `logo image "<path>" title "<text>"` inside `navigation topbar` or `navigation sidebar` (the
|
|
279
|
+
application title the shell shows; `logo initials` is only valid in a profile's `identity`);
|
|
280
|
+
- a `ui profile` with `root template`, shell `option` lines such as `option showFooter false`, and an `identity`
|
|
281
|
+
block (`name`, `logo initials`);
|
|
282
|
+
- a `title "<text>"` in each screen and a navigation `item <id> title "<text>" target <Module.Feature>` per screen;
|
|
283
|
+
- `label "<text>"` on **every** `action`.
|
|
284
|
+
|
|
285
|
+
```screenplay excerpt
|
|
286
|
+
layout AppShell
|
|
287
|
+
category application
|
|
288
|
+
content
|
|
289
|
+
|
|
290
|
+
navigation sidebar
|
|
291
|
+
logo image "/assets/solocrm.svg" title "SoloCRM"
|
|
292
|
+
item organizations title "Organizations" target Crm.Organizations
|
|
293
|
+
|
|
294
|
+
ui profile Desktop
|
|
295
|
+
target platform web
|
|
296
|
+
root template AppShell
|
|
297
|
+
option showFooter false
|
|
298
|
+
identity
|
|
299
|
+
name "SoloCRM"
|
|
300
|
+
logo initials "SC"
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```screenplay excerpt
|
|
304
|
+
screen Organizations
|
|
305
|
+
data OrganizationOverview[] via query AllOrganizations
|
|
306
|
+
title "Organizations"
|
|
307
|
+
action RegisterOrganization
|
|
308
|
+
label "New organization"
|
|
309
|
+
```
|
|
310
|
+
|
|
273
311
|
## Localized strings
|
|
274
312
|
|
|
275
313
|
Localized text lives in companion `.strings` files and is referenced unquoted as `$strings.<dotted.key>`. Read [references/localized-strings.md](references/localized-strings.md) when localizing messages, labels or titles.
|
|
@@ -294,6 +332,8 @@ Localized text lives in companion `.strings` files and is referenced unquoted as
|
|
|
294
332
|
- [ ] Every theme a profile selects is `compatible with` one of its packages.
|
|
295
333
|
- [ ] `target platform` and `target size` values are spelled correctly; nothing
|
|
296
334
|
checks them.
|
|
335
|
+
- [ ] A deliverable app declares layout `logo ... title`, a `ui profile` with `option` lines and `identity`,
|
|
336
|
+
a `title` per screen and a `label` on every `action`.
|
|
297
337
|
- [ ] User-visible text is `$strings.` where the application is localized.
|
|
298
338
|
- [ ] No `file` reference is doing work the declaration should be doing.
|
|
299
339
|
- [ ] Every click, submit and screen entry that does something has an `on` or
|
|
@@ -251,8 +251,9 @@ Stage renders two projection forms; the restrictions differ.
|
|
|
251
251
|
inside a child are admitted), `nested`
|
|
252
252
|
over an optional composite property, and `every`/`all` with set-from-event-source mappings. Keys
|
|
253
253
|
are an event property or the event source. The key establishes the identifier, so it must
|
|
254
|
-
end up unmapped: a mapping onto it is refused unless it
|
|
255
|
-
property,
|
|
254
|
+
end up unmapped: a mapping onto it is refused unless it restates the key (the key's own
|
|
255
|
+
event property, `$eventSourceId` for an event-source key, or the same literal)
|
|
256
|
+
(`STAGE-ESM-017`). Auto-map
|
|
256
257
|
counts as a mapping: if the event carries a property with the identifier's name and type,
|
|
257
258
|
declare `no automap` and map the other properties explicitly. `from E` and
|
|
258
259
|
`from E key $eventSourceId` render when the identifier ends up unmapped, whether or not
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) .NET Foundation and Contributors
|
|
4
|
+
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
C# Refactoring (behavior-preserving)
|
|
2
|
+
Copyright (c) .NET Foundation and Contributors
|
|
3
|
+
Licensed under the MIT License. See LICENSE.
|
|
4
|
+
|
|
5
|
+
Upstream source: https://github.com/dotnet/skills/blob/a6bf43ab61967c30919334986515163ab24d7635/plugins/dotnet/skills/csharp-refactoring/SKILL.md
|
|
6
|
+
|
|
7
|
+
Changes made by Cratis:
|
|
8
|
+
|
|
9
|
+
LICENSE:
|
|
10
|
+
- Copy upstream MIT license and copyright verbatim.
|
|
11
|
+
|
|
12
|
+
SKILL.md:
|
|
13
|
+
- Normalize frontmatter serialization, add MIT license where absent; preserve name and description.
|
|
14
|
+
- Add marked modification/source comment and applicability/Cratis precedence line.
|
|
15
|
+
- Add the requested Cratis persisted-event, proxy, Orleans identity and framework-public-API contract section.
|
|
16
|
+
- Rewrite the description to the Cratis shape (what it does, Use when, Not for) and drop references to skills that are not part of this corpus.
|
|
17
|
+
- Remove references to the dotnet-upgrade skill; point to the repository's own upgrade workflow.
|
|
18
|
+
|
|
19
|
+
references/operation-catalog.md:
|
|
20
|
+
- Replace the upstream issue reference with a plain statement of the behavior.
|
|
21
|
+
|
|
22
|
+
NOTICE: Add this attribution, change list, and any download/execution disclosure.
|