@cratis/pi 7.103.2 → 7.103.4

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.
@@ -19,8 +19,9 @@ paths:
19
19
  jobs on the Free plan, 5 for macOS). Every queued job competes with every repository.
20
20
  - **Public repositories** run free on hosted runners; **private repositories bill every
21
21
  minute** — Linux 1×, Windows 2×, macOS 10× — against a small monthly allowance.
22
- - The organization runs its own scale set (`cratis-arc`). Jobs there cost nothing and
23
- do not touch the hosted pool.
22
+ - The organization runs self-hosted scale sets on UpCloud (`cratis-arc` and
23
+ `cratis-arc-small`). They do not touch the hosted pool, but share infrastructure
24
+ capacity and cost across Cratis repositories.
24
25
 
25
26
  ## Scheduling
26
27
 
@@ -54,6 +55,9 @@ paths:
54
55
  `cancel-in-progress` is false or unset. Keep a single pending slot only for a documented
55
56
  design where each run releases everything since the last release, so a replaced run
56
57
  loses nothing; say so in a comment beside the `concurrency` block.
58
+ - Avoid duplicate `push` and `pull_request` verification for the same feature-branch
59
+ commit: use pull requests for branch verification and restrict push verification
60
+ to the default branch. Filter unrelated heavy jobs by changed paths.
57
61
  - **A required check must not be skippable by `paths:` alone.** A workflow that never
58
62
  starts never reports, and the pull request hangs. Gate jobs on a change-detection job
59
63
  and require one always-run aggregator job (`if: always()`, checks every `needs.*.result`).
@@ -74,9 +78,26 @@ paths:
74
78
 
75
79
  ## Runners
76
80
 
77
- - Private-repository jobs belong on the organization scale set: `runs-on: cratis-arc`,
78
- or a repository variable such as `${{ vars.<REPO>_RUNNER || 'ubuntu-latest' }}` so
79
- routing changes without a code change.
81
+ These are judgment-shaped routing and cache choices. Read the
82
+ [Infrastructure runner guide](https://github.com/Cratis/Infrastructure/blob/main/Documentation/deployment/github-runners.md)
83
+ for UpCloud runner resources, ceilings and storage; link it rather than copying
84
+ capacity numbers into workflows or guidance.
85
+
86
+ - Use GitHub-hosted runners for public repositories (free hosted execution) and jobs
87
+ needing Windows or macOS. Private Linux jobs use `cratis-arc` for builds, specs,
88
+ audits, deployments or Docker; `cratis-arc-small` is only for lightweight script/API
89
+ guards. The small set has no Docker: no container actions, services or Testcontainers.
90
+ A repository runner variable can make routing configurable.
91
+ - Compare job memory and in-job parallelism with the documented limits before
92
+ increasing fan-out. Both sets share nodes; pod ceilings and node cold starts can
93
+ make extra jobs wait. Use the `cratis-ci-pipeline-performance` skill for sizing.
94
+ - Runner pods are ephemeral: workspaces, Docker data and local package downloads do
95
+ not persist between jobs. Use the shared Cratis cache actions, `actions/cache` or
96
+ `setup-*` caching keyed on lockfiles; never assume a warm local package cache.
97
+ - When testing locally packed `0.0.0-ci` packages, a restored global NuGet cache can
98
+ supply stale bytes for the same package ID/version. Use a unique version per run,
99
+ a per-run packages folder, or remove that package from the restored cache before
100
+ restoring consumers. See the skill's `references/caching.md`.
80
101
  - On self-hosted runners, `actions/setup-dotnet` cannot write `/usr/share/dotnet`.
81
102
  Set `DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet` on the setup step.
82
103
  - **Never** route `pull_request`-triggered jobs of a *public* repository to self-hosted
@@ -24,7 +24,9 @@ exact commands.
24
24
 
25
25
  - Read the `go` directive, build constraints and CI before choosing a feature.
26
26
  The minimum is not the newest locally installed compiler; a `toolchain`
27
- preference does not replace minimum-version testing. Do not raise it incidentally.
27
+ preference does not replace minimum-version testing. Do not raise it incidentally
28
+ or rewrite unrelated code to modernize syntax. Verify unfamiliar APIs against the
29
+ documentation for that minimum, and keep experimental features opt-in.
28
30
  - Iterators require Go 1.23; `T.Context` and `B.Loop` require 1.24;
29
31
  stable `testing/synctest` and `WaitGroup.Go` require 1.25.
30
32
  - Prefer the standard library when it meets the contract. Review dependency
@@ -33,6 +35,10 @@ exact commands.
33
35
  including indirect minimum-version changes; keep sums consistent. A consumer
34
36
  must not need a developer's workspace or local replacement.
35
37
  - Never edit generated code; change its authoritative inputs and pinned generator.
38
+ - Start with a small public package surface aligned with caller workflows; put
39
+ implementation-only packages in `internal/` and keep optional adapters apart from
40
+ core behavior. Do not mandate `pkg/`. Avoid package initialization that performs
41
+ I/O, starts goroutines or mutates globals.
36
42
 
37
43
  ## Names, public APIs and values
38
44
 
@@ -44,11 +50,23 @@ exact commands.
44
50
  - Document exported declarations with comments beginning with their names.
45
51
  Explain nil/zero semantics, concurrency, ownership, cancellation, error identities
46
52
  and cleanup. Add executable examples for important public workflows.
47
- - Make zero values useful where possible; reject invalid construction early.
48
- Document option defaults, validation, ordering and duplicates.
53
+ - Design exported APIs from realistic caller examples first and export only what
54
+ the library will support. Make zero values useful where possible; reject invalid
55
+ construction early. Use plain parameters or config structs for simple
56
+ configuration and functional options when extensibility outweighs the added
57
+ namespace; document option defaults, validation, ordering and duplicates.
58
+ - Reuse standard interfaces such as `io.Reader` and `http.Handler`; prefer concrete
59
+ returns unless an abstraction is part of the API. Use pointer receivers for
60
+ mutation or values that must not be copied, and avoid public embedding that
61
+ promotes dependency methods. Mark retired API with a `Deprecated:` paragraph
62
+ naming the replacement.
49
63
  - Use named domain values instead of interchangeable primitives. Defined types
50
64
  do not inherit codec methods; explicitly forward text/JSON codecs and test wire
51
65
  formats. See `cratis-fundamentals-concept-go` when using Fundamentals.Go.
66
+ - Specify `Close` ownership, repeated-close behavior and use after close, and
67
+ document concurrent-use guarantees for clients, handles and callbacks.
68
+ - Use generics for meaningful type-safe reuse, not speculative abstraction; do not
69
+ replace domain types with `any` for plumbing convenience.
52
70
  - Document whether slices, maps, config and results are owned, copied or borrowed.
53
71
  Copying a slice/map header is not a detached copy. Do not copy a used mutex,
54
72
  wait group or atomic value, or expose pooled bytes after returning their buffer.
@@ -62,7 +80,9 @@ exact commands.
62
80
  return an `error` interface containing a typed nil.
63
81
  - Add useful operation context. Handle/log an error at one boundary rather than
64
82
  repeatedly logging and returning it. A partial or ambiguous write is not success
65
- and must not trigger a blind retry.
83
+ and must not trigger a blind retry. Use sentinels for stable categories and typed
84
+ errors for structured details; translate dependency errors whose identity must
85
+ stay internal, and explain intentional suppression.
66
86
  - Put `context.Context` first for blocking/cancelable work and propagate it through
67
87
  RPCs, HTTP, storage and callbacks. Never escape caller cancellation by replacing
68
88
  it with `Background`. Do not retain request contexts in long-lived clients.
@@ -73,6 +93,9 @@ exact commands.
73
93
 
74
94
  ## Goroutines, streams and iterators
75
95
 
96
+ - Prefer synchronous APIs and let callers place concurrency. A saturated
97
+ `errgroup.SetLimit` can block `Go`; use `errgroup` only when its cancellation
98
+ and error behavior fits.
76
99
  - Each goroutine needs an owner, termination condition and observable join path.
77
100
  Cancellation requests termination; it does not join workers.
78
101
  - Bound workers, queues, subscriptions, buffers and retries; define backpressure.
@@ -85,6 +108,8 @@ exact commands.
85
108
  - Preserve protocol metadata, tenancy, checkpoints and duplicate-delivery semantics.
86
109
  No generic retry or stream helper may promise exactly-once effects.
87
110
  - Validate remote TLS identities. Development exceptions must be explicit.
111
+ Bound untrusted payload sizes, fan-out and retained subscription state, and keep
112
+ long-lived subscription lifetimes apart from unary-call deadlines.
88
113
  Use cryptographic randomness for secrets, never `math/rand`.
89
114
 
90
115
  ## Specifications and checks
@@ -98,10 +123,23 @@ exact commands.
98
123
  - Cover error identity, nil/zero input, cancellation/deadlines, partial outcomes,
99
124
  early stream exit and resource ownership. Bound fuzz duration and preserve
100
125
  failing seeds. Use race checks where supported; race-free does not mean leak-free.
126
+ - Add a regression test for each corrected behavior, including error and boundary
127
+ cases. Use `httptest` or `bufconn` plus real-transport tests where needed, and
128
+ protobuf-aware comparison for generated types.
129
+ - Profile or benchmark a demonstrated problem before optimizing; add no pools,
130
+ caches or `unsafe` without measured justification.
101
131
  - Run the repository's build, tests, formatting, vet and pinned golangci-lint gates,
102
132
  with race and vulnerability scanning as separate checks. Respect the declared
103
133
  minimum without silent toolchain upgrades. Report absent tools honestly.
104
134
 
135
+ ## Compatibility and releases
136
+
137
+ - Prefer additive change: exported signature changes break callers, and adding a
138
+ method to an exported interface breaks its external implementations. Check struct comparability, embedding, defaults, errors and wire shape;
139
+ API-diff tools aid but cannot prove behavioral compatibility.
140
+ - Introduce `/v2` only with a migration and parallel-support plan. Never move a
141
+ published tag; issue a corrective release and retract when needed.
142
+
105
143
  ## Adaptation and attribution
106
144
 
107
145
  This condensed rule adapts the repository-owned Go baseline at
@@ -18,8 +18,9 @@ paths:
18
18
  jobs on the Free plan, 5 for macOS). Every queued job competes with every repository.
19
19
  - **Public repositories** run free on hosted runners; **private repositories bill every
20
20
  minute** — Linux 1×, Windows 2×, macOS 10× — against a small monthly allowance.
21
- - The organization runs its own scale set (`cratis-arc`). Jobs there cost nothing and
22
- do not touch the hosted pool.
21
+ - The organization runs self-hosted scale sets on UpCloud (`cratis-arc` and
22
+ `cratis-arc-small`). They do not touch the hosted pool, but share infrastructure
23
+ capacity and cost across Cratis repositories.
23
24
 
24
25
  ## Scheduling
25
26
 
@@ -53,6 +54,9 @@ paths:
53
54
  `cancel-in-progress` is false or unset. Keep a single pending slot only for a documented
54
55
  design where each run releases everything since the last release, so a replaced run
55
56
  loses nothing; say so in a comment beside the `concurrency` block.
57
+ - Avoid duplicate `push` and `pull_request` verification for the same feature-branch
58
+ commit: use pull requests for branch verification and restrict push verification
59
+ to the default branch. Filter unrelated heavy jobs by changed paths.
56
60
  - **A required check must not be skippable by `paths:` alone.** A workflow that never
57
61
  starts never reports, and the pull request hangs. Gate jobs on a change-detection job
58
62
  and require one always-run aggregator job (`if: always()`, checks every `needs.*.result`).
@@ -73,9 +77,26 @@ paths:
73
77
 
74
78
  ## Runners
75
79
 
76
- - Private-repository jobs belong on the organization scale set: `runs-on: cratis-arc`,
77
- or a repository variable such as `${{ vars.<REPO>_RUNNER || 'ubuntu-latest' }}` so
78
- routing changes without a code change.
80
+ These are judgment-shaped routing and cache choices. Read the
81
+ [Infrastructure runner guide](https://github.com/Cratis/Infrastructure/blob/main/Documentation/deployment/github-runners.md)
82
+ for UpCloud runner resources, ceilings and storage; link it rather than copying
83
+ capacity numbers into workflows or guidance.
84
+
85
+ - Use GitHub-hosted runners for public repositories (free hosted execution) and jobs
86
+ needing Windows or macOS. Private Linux jobs use `cratis-arc` for builds, specs,
87
+ audits, deployments or Docker; `cratis-arc-small` is only for lightweight script/API
88
+ guards. The small set has no Docker: no container actions, services or Testcontainers.
89
+ A repository runner variable can make routing configurable.
90
+ - Compare job memory and in-job parallelism with the documented limits before
91
+ increasing fan-out. Both sets share nodes; pod ceilings and node cold starts can
92
+ make extra jobs wait. Use the `cratis-ci-pipeline-performance` skill for sizing.
93
+ - Runner pods are ephemeral: workspaces, Docker data and local package downloads do
94
+ not persist between jobs. Use the shared Cratis cache actions, `actions/cache` or
95
+ `setup-*` caching keyed on lockfiles; never assume a warm local package cache.
96
+ - When testing locally packed `0.0.0-ci` packages, a restored global NuGet cache can
97
+ supply stale bytes for the same package ID/version. Use a unique version per run,
98
+ a per-run packages folder, or remove that package from the restored cache before
99
+ restoring consumers. See the skill's `references/caching.md`.
79
100
  - On self-hosted runners, `actions/setup-dotnet` cannot write `/usr/share/dotnet`.
80
101
  Set `DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet` on the setup step.
81
102
  - **Never** route `pull_request`-triggered jobs of a *public* repository to self-hosted
@@ -23,7 +23,9 @@ exact commands.
23
23
 
24
24
  - Read the `go` directive, build constraints and CI before choosing a feature.
25
25
  The minimum is not the newest locally installed compiler; a `toolchain`
26
- preference does not replace minimum-version testing. Do not raise it incidentally.
26
+ preference does not replace minimum-version testing. Do not raise it incidentally
27
+ or rewrite unrelated code to modernize syntax. Verify unfamiliar APIs against the
28
+ documentation for that minimum, and keep experimental features opt-in.
27
29
  - Iterators require Go 1.23; `T.Context` and `B.Loop` require 1.24;
28
30
  stable `testing/synctest` and `WaitGroup.Go` require 1.25.
29
31
  - Prefer the standard library when it meets the contract. Review dependency
@@ -32,6 +34,10 @@ exact commands.
32
34
  including indirect minimum-version changes; keep sums consistent. A consumer
33
35
  must not need a developer's workspace or local replacement.
34
36
  - Never edit generated code; change its authoritative inputs and pinned generator.
37
+ - Start with a small public package surface aligned with caller workflows; put
38
+ implementation-only packages in `internal/` and keep optional adapters apart from
39
+ core behavior. Do not mandate `pkg/`. Avoid package initialization that performs
40
+ I/O, starts goroutines or mutates globals.
35
41
 
36
42
  ## Names, public APIs and values
37
43
 
@@ -43,11 +49,23 @@ exact commands.
43
49
  - Document exported declarations with comments beginning with their names.
44
50
  Explain nil/zero semantics, concurrency, ownership, cancellation, error identities
45
51
  and cleanup. Add executable examples for important public workflows.
46
- - Make zero values useful where possible; reject invalid construction early.
47
- Document option defaults, validation, ordering and duplicates.
52
+ - Design exported APIs from realistic caller examples first and export only what
53
+ the library will support. Make zero values useful where possible; reject invalid
54
+ construction early. Use plain parameters or config structs for simple
55
+ configuration and functional options when extensibility outweighs the added
56
+ namespace; document option defaults, validation, ordering and duplicates.
57
+ - Reuse standard interfaces such as `io.Reader` and `http.Handler`; prefer concrete
58
+ returns unless an abstraction is part of the API. Use pointer receivers for
59
+ mutation or values that must not be copied, and avoid public embedding that
60
+ promotes dependency methods. Mark retired API with a `Deprecated:` paragraph
61
+ naming the replacement.
48
62
  - Use named domain values instead of interchangeable primitives. Defined types
49
63
  do not inherit codec methods; explicitly forward text/JSON codecs and test wire
50
64
  formats. See `cratis-fundamentals-concept-go` when using Fundamentals.Go.
65
+ - Specify `Close` ownership, repeated-close behavior and use after close, and
66
+ document concurrent-use guarantees for clients, handles and callbacks.
67
+ - Use generics for meaningful type-safe reuse, not speculative abstraction; do not
68
+ replace domain types with `any` for plumbing convenience.
51
69
  - Document whether slices, maps, config and results are owned, copied or borrowed.
52
70
  Copying a slice/map header is not a detached copy. Do not copy a used mutex,
53
71
  wait group or atomic value, or expose pooled bytes after returning their buffer.
@@ -61,7 +79,9 @@ exact commands.
61
79
  return an `error` interface containing a typed nil.
62
80
  - Add useful operation context. Handle/log an error at one boundary rather than
63
81
  repeatedly logging and returning it. A partial or ambiguous write is not success
64
- and must not trigger a blind retry.
82
+ and must not trigger a blind retry. Use sentinels for stable categories and typed
83
+ errors for structured details; translate dependency errors whose identity must
84
+ stay internal, and explain intentional suppression.
65
85
  - Put `context.Context` first for blocking/cancelable work and propagate it through
66
86
  RPCs, HTTP, storage and callbacks. Never escape caller cancellation by replacing
67
87
  it with `Background`. Do not retain request contexts in long-lived clients.
@@ -72,6 +92,9 @@ exact commands.
72
92
 
73
93
  ## Goroutines, streams and iterators
74
94
 
95
+ - Prefer synchronous APIs and let callers place concurrency. A saturated
96
+ `errgroup.SetLimit` can block `Go`; use `errgroup` only when its cancellation
97
+ and error behavior fits.
75
98
  - Each goroutine needs an owner, termination condition and observable join path.
76
99
  Cancellation requests termination; it does not join workers.
77
100
  - Bound workers, queues, subscriptions, buffers and retries; define backpressure.
@@ -84,6 +107,8 @@ exact commands.
84
107
  - Preserve protocol metadata, tenancy, checkpoints and duplicate-delivery semantics.
85
108
  No generic retry or stream helper may promise exactly-once effects.
86
109
  - Validate remote TLS identities. Development exceptions must be explicit.
110
+ Bound untrusted payload sizes, fan-out and retained subscription state, and keep
111
+ long-lived subscription lifetimes apart from unary-call deadlines.
87
112
  Use cryptographic randomness for secrets, never `math/rand`.
88
113
 
89
114
  ## Specifications and checks
@@ -97,10 +122,23 @@ exact commands.
97
122
  - Cover error identity, nil/zero input, cancellation/deadlines, partial outcomes,
98
123
  early stream exit and resource ownership. Bound fuzz duration and preserve
99
124
  failing seeds. Use race checks where supported; race-free does not mean leak-free.
125
+ - Add a regression test for each corrected behavior, including error and boundary
126
+ cases. Use `httptest` or `bufconn` plus real-transport tests where needed, and
127
+ protobuf-aware comparison for generated types.
128
+ - Profile or benchmark a demonstrated problem before optimizing; add no pools,
129
+ caches or `unsafe` without measured justification.
100
130
  - Run the repository's build, tests, formatting, vet and pinned golangci-lint gates,
101
131
  with race and vulnerability scanning as separate checks. Respect the declared
102
132
  minimum without silent toolchain upgrades. Report absent tools honestly.
103
133
 
134
+ ## Compatibility and releases
135
+
136
+ - Prefer additive change: exported signature changes break callers, and adding a
137
+ method to an exported interface breaks its external implementations. Check struct comparability, embedding, defaults, errors and wire shape;
138
+ API-diff tools aid but cannot prove behavioral compatibility.
139
+ - Introduce `/v2` only with a migration and parallel-support plan. Never move a
140
+ published tag; issue a corrective release and retract when needed.
141
+
104
142
  ## Adaptation and attribution
105
143
 
106
144
  This condensed rule adapts the repository-owned Go baseline at
@@ -111,11 +111,12 @@ when keyed on `github.ref` for merged `pull_request` `closed` events). A workflo
111
111
  whose release tooling aggregates since the last release may keep one pending slot,
112
112
  documented. Read `references/publish-pipelines.md`.
113
113
 
114
- **Runner routing.** Fits queue-bound workflows. Default: private-repository jobs
115
- on the organization scale set `cratis-arc` or a repository variable that falls
116
- back to a hosted label, hosted runners for public repositories. Pitfall: never
117
- route `pull_request` jobs of a public repository to self-hosted runners; that is
118
- fork code on our infrastructure. The rule owns the details.
114
+ **Runner routing.** Fits queue-bound workflows. Read the
115
+ [Infrastructure runner guide](https://github.com/Cratis/Infrastructure/blob/main/Documentation/deployment/github-runners.md)
116
+ for the UpCloud scale sets `cratis-arc` and `cratis-arc-small`; do not copy its
117
+ capacity numbers. The rule owns routing and public-fork safety. Read
118
+ `references/runner-sizing-and-images.md` when choosing hosted versus self-hosted
119
+ runners, sizing in-job parallelism or weighing fan-out against cold starts.
119
120
 
120
121
  **Publish from tested artifacts.** Fits release workflows that rebuild what the
121
122
  pull-request run already built and tested. Default: build and pack once, then
@@ -195,7 +196,7 @@ touches `permissions:`, uses `pull_request_target`, or adds a cache.
195
196
  - Read `references/publish-pipelines.md` when a release or deploy workflow queues, drops runs or rebuilds.
196
197
  - Read `references/flaky-and-hung-jobs.md` when runs fail, hang or are rerun.
197
198
  - Read `references/test-setup-cost.md` when test steps outlast reported test time, tests duplicate a dependency's suite, or compiles repeat per push.
198
- - Read `references/runner-sizing-and-images.md` when jobs are uniformly slow, pull images, install tools or reuse workspaces.
199
+ - Read `references/runner-sizing-and-images.md` when choosing runners, sizing parallelism, or when jobs are uniformly slow, pull images, install tools or reuse workspaces.
199
200
  - Read `references/security-and-pinning.md` when a change touches actions, permissions, triggers or caches.
200
201
  - Read `references/sources.md` when attributing or extending the method.
201
202
  - Execute `scripts/ci-timings.sh` for every measurement; read it only to change it. Its self-test reads the synthetic inputs `scripts/fixtures/runs.json` and `scripts/fixtures/jobs/1003.json`, `scripts/fixtures/jobs/1007.json`, `scripts/fixtures/jobs/1010.json`; edit them only together with the expected lines in the script.
@@ -1,16 +1,18 @@
1
1
  # Caching
2
2
 
3
- Use this when a job spends measurable time restoring packages, installing a
4
- frontend toolchain or rebuilding Docker layers. Check the slowest-steps table
5
- first: a step under a minute is not worth a cache.
6
-
7
3
  ## Contents
8
4
 
9
5
  - Shared Cratis cache actions
6
+ - Ephemeral runner pods
10
7
  - NuGet and Yarn determinism
8
+ - Locally packed CI packages
11
9
  - Docker layer cache
12
10
  - Scope, eviction and safety
13
11
 
12
+ Use this when a job spends measurable time restoring packages, installing a
13
+ frontend toolchain or rebuilding Docker layers. Check the slowest-steps table
14
+ first: a step under a minute is not worth a cache.
15
+
14
16
  ## Shared Cratis cache actions
15
17
 
16
18
  The `Cratis/Workflows` repository ships two composite actions. Use them instead
@@ -35,6 +37,17 @@ key and one path list.
35
37
  workflow on that branch never warms one. Add a scheduled warm-up workflow that
36
38
  runs the same restore and build.
37
39
 
40
+ ## Ephemeral runner pods
41
+
42
+ Cratis self-hosted runners use per-job pods: workspace files, package downloads
43
+ and Docker data disappear with the job. A warm runner or node image does not make
44
+ `~/.nuget/packages`, the Yarn/npm download cache or BuildKit layers persistent.
45
+ Use the shared actions above, or explicit `actions/cache` / `setup-*` dependency
46
+ caching when the shared actions do not fit. Key downloads on the OS, toolchain and
47
+ committed lockfiles (`packages.lock.json`, `yarn.lock` or `package-lock.json`),
48
+ and use remote Docker layer caching below. Do not cache `node_modules` as a
49
+ substitute for an immutable install.
50
+
38
51
  ## NuGet and Yarn determinism
39
52
 
40
53
  A cache is only as stable as its key. Make the key inputs deterministic.
@@ -48,6 +61,34 @@ A cache is only as stable as its key. Make the key inputs deterministic.
48
61
  caches across commits are rarely correct; share build output within a run as an
49
62
  artifact instead (see `build-once-and-shard.md`).
50
63
 
64
+ ## Locally packed CI packages
65
+
66
+ A repository that packs `0.0.0-ci` repeatedly and tests a consumer can restore an
67
+ older package with the same ID/version from cached `~/.nuget/packages`. NuGet can
68
+ reuse the extracted package without reading the newly packed bytes, so a green
69
+ consumer test may exercise the wrong code. A lockfile cache key alone does not
70
+ fix an unchanged local package version.
71
+
72
+ Default: pack a unique prerelease version per run and attempt, and make the
73
+ consumer reference that exact version. If the version must stay fixed, set a
74
+ per-run packages folder for **every restore/build/test that can restore** in both
75
+ the producer and consumer, and do not restore the global package cache into it:
76
+
77
+ ```yaml
78
+ env:
79
+ NUGET_PACKAGES: ${{ runner.temp }}/nuget-${{ github.run_id }}-${{ github.run_attempt }}
80
+ ```
81
+
82
+ Alternatively, after cache restoration and before consumer restore, remove only
83
+ the locally packed package's ID/version directory from the global packages
84
+ folder. Keep unrelated dependency downloads cached. A fresh runner is not enough
85
+ if the workflow restores stale package bytes from a remote cache.
86
+
87
+ Verify with `dotnet restore <consumer.csproj> --verbosity normal` followed by the
88
+ consumer's focused test command. Pass means restore resolves the newly packed
89
+ ID/version from the intended feed/folder and the test exercises behavior added
90
+ in that package, not merely that a cached restore succeeds.
91
+
51
92
  ## Docker layer cache
52
93
 
53
94
  ```yaml
@@ -4,6 +4,7 @@
4
4
 
5
5
  - The hang
6
6
  - Change detection and the aggregator
7
+ - Avoiding redundant runs
7
8
  - Keeping required checks stable
8
9
  - Merge queue
9
10
 
@@ -82,6 +83,35 @@ jobs:
82
83
  build). For one event, `paths` and `paths-ignore` cannot be combined; use
83
84
  `paths` with `!` negation patterns.
84
85
 
86
+ ## Avoiding redundant runs
87
+
88
+ On the shared Cratis runner pools, two events for the same feature-branch commit
89
+ consume capacity that another repository needs. Verify open pull requests through
90
+ `pull_request` and restrict push verification to the default branch:
91
+
92
+ ```yaml
93
+ on:
94
+ pull_request:
95
+ push:
96
+ branches: [main]
97
+ concurrency:
98
+ group: ${{ github.workflow }}-${{ github.ref }}
99
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
100
+ ```
101
+
102
+ Use the repository's actual default branch. Keep publish/release/deploy workflows
103
+ separate and follow their non-cancelling queue rules in
104
+ [github-actions.md](../../../rules/github-actions.md). Cancellation stops obsolete
105
+ PR runs; it does not deduplicate `push` and `pull_request` events, whose refs differ.
106
+ Use change detection above to avoid unrelated heavy jobs without suppressing the
107
+ required aggregator. Retain any required `merge_group` trigger as described below.
108
+
109
+ Verify event deduplication with
110
+ `gh run list --workflow <workflow.yml> --commit <sha> --json event,headSha,status`:
111
+ a PR update should have one verification run for that workflow, not both a push
112
+ and a pull-request run. Check that a superseded PR run cancels, unrelated paths
113
+ skip heavy jobs, and the required aggregator still reports.
114
+
85
115
  ## Keeping required checks stable
86
116
 
87
117
  Branch protection matches check names. Renaming a job, changing a matrix key or
@@ -1,16 +1,55 @@
1
1
  # Runner sizing, images and cache layers
2
2
 
3
- Use this when every job is uniformly slow, when jobs spend time pulling or
4
- installing tools, or when deciding how to warm workspaces.
5
-
6
3
  ## Contents
7
4
 
5
+ - Choosing Cratis runners
8
6
  - Cores per job before job count
9
7
  - Container images
10
8
  - Tools baked into the runner image
11
9
  - Cache layers and warm workspaces
12
10
  - Publish stages
13
11
 
12
+ Use this when every job is uniformly slow, when jobs spend time pulling or
13
+ installing tools, or when choosing runners and deciding how to warm workspaces.
14
+
15
+ ## Choosing Cratis runners
16
+
17
+ Read the [Infrastructure runner guide](https://github.com/Cratis/Infrastructure/blob/main/Documentation/deployment/github-runners.md)
18
+ for the UpCloud scale sets, resource requests and limits, pod ceilings, scratch
19
+ storage and autoscaling. Keep those numbers there, not in this skill. The routing
20
+ and public-fork safety invariants live in
21
+ [github-actions.md](../../../rules/github-actions.md).
22
+
23
+ Choose by the work, not just the queue length:
24
+
25
+ | Choice | When it fits | Check before routing |
26
+ |---|---|---|
27
+ | GitHub-hosted | Public repositories, Windows/macOS jobs | Public hosted execution is free; compare queue and startup time, not just billed minutes |
28
+ | `cratis-arc-small` | Lightweight script/API guards such as release-note or work-record checks | No Docker daemon, container actions, service containers or Testcontainers; not for builds, specs, audits or deployments |
29
+ | `cratis-arc` | Private Linux builds, specs, dependency audits, deployments and Docker work | Fit peak memory, worker count and scratch use inside the documented limits |
30
+
31
+ For example, a private-repository release-note script can use
32
+ `runs-on: cratis-arc-small`; a private-repository Testcontainers suite needs
33
+ `runs-on: cratis-arc`.
34
+
35
+ - Measure peak memory for the whole job, including Docker services and test
36
+ workers. Leave headroom below the documented limit; an OOM is not evidence that
37
+ more shards will help when those shards share nodes.
38
+ - Set build/test worker counts from the pod's CPU and memory limits, not the
39
+ node's advertised cores. A larger in-job worker count can cause contention.
40
+ - Compare fan-out with each set's pod ceiling and the shared node pool. More
41
+ shards can wait for slots or node autoscaling instead of shortening the run.
42
+ - Include runner startup, image pulls and fresh-node provisioning in wall time.
43
+ For short jobs, merge compatible checks with one setup before adding shards;
44
+ compare the hosted alternative where routing policy permits it.
45
+ - Reduce redundant work before requesting capacity: apply PR cancellation,
46
+ change-detected heavy jobs and event deduplication as described in
47
+ `required-checks-and-path-filters.md` and the owning rule.
48
+
49
+ Do not infer live node counts, runner cost/quotas, network/egress policy or shared
50
+ package-cache availability from the YAML. Record missing operational facts in
51
+ Infrastructure, not as assumptions in the corpus.
52
+
14
53
  ## Cores per job before job count
15
54
 
16
55
  Undersized runner pods (a small CPU request or limit) make every job slow, and more
@@ -45,6 +84,12 @@ a version check. Keep the image's contents versioned so a change is reviewable.
45
84
  Prefer content-addressed caches, which are correct by construction, before
46
85
  warm volumes:
47
86
 
87
+ Cratis runner pods start with per-job workspaces and Docker storage; neither is
88
+ a persistent shared NuGet, npm or BuildKit cache. Restore remote caches explicitly
89
+ (see `caching.md`) rather than treating a previous job's downloads as available.
90
+ The layers below are options to configure, not claims that Infrastructure provides
91
+ them:
92
+
48
93
  1. A registry mirror or pull-through cache for images.
49
94
  2. A remote or registry-backed BuildKit cache (see `caching.md`).
50
95
  3. Package caches keyed by lock files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "7.103.2",
3
+ "version": "7.103.4",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [