@cratis/pi 7.103.3 → 7.103.5
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/go.instructions.md +42 -4
- package/package/corpus/hooks/README.md +16 -0
- package/package/corpus/hooks/scripts/cratis-cleanup-worktrees.sh +140 -24
- package/package/corpus/rules/go.md +42 -4
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +8 -4
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dependency-map.md +59 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +162 -28
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/dependency-map.tsv +68 -0
- package/package.json +1 -1
|
@@ -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
|
-
-
|
|
48
|
-
|
|
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
|
|
@@ -128,6 +128,22 @@ ignored records. Other ignored files (`node_modules`, build output) go with a de
|
|
|
128
128
|
`--include-work-records` only after preserving any records you still need: it permits deleting
|
|
129
129
|
`.ai-work/` content, but never overrides locks or other safety checks. Hook mode ignores this flag;
|
|
130
130
|
automatic hooks never opt in.
|
|
131
|
+
- **GitHub lookups.** The complete worktree and branch sweep is planned before any removal. Git
|
|
132
|
+
ancestry is checked first; only a plan needing more evidence triggers the single REST batch. It
|
|
133
|
+
reads the latest 100 closed pull requests, so older merged branches absent from that page are simply
|
|
134
|
+
kept. Only merged entries with the exact branch and head commit qualify. Hook runs share an atomic
|
|
135
|
+
stamp and cache under the Git common directory, with at most one request per repository every
|
|
136
|
+
15 minutes, including failed attempts. A fresh successful cache is reused. A failed request, invalid
|
|
137
|
+
response, or unavailable lock without a fresh successful cache aborts **all** removals in that sweep,
|
|
138
|
+
including ancestry-proven worktrees and branches, with a one-line notice. Failed attempts are marked
|
|
139
|
+
for the full window: later plans needing GitHub also abort all removals without retrying, while plans
|
|
140
|
+
needing only ancestry evidence proceed without GitHub. Locks older than 15 minutes can be reclaimed.
|
|
141
|
+
Manual runs are not throttled, but also use at most one request per run and abort all removals if
|
|
142
|
+
that request fails.
|
|
143
|
+
- **Disable GitHub only.** Set `CRATIS_CLEANUP_NO_GH=1` to avoid all GitHub lookups while keeping
|
|
144
|
+
cleanup based on Git ancestry enabled. Missing `gh` or `jq`, a non-GitHub origin, or an invalid
|
|
145
|
+
repository slug also select ancestry-only cleanup before planning: unproven items are kept, with
|
|
146
|
+
no GitHub call, abort, or failed-attempt marker.
|
|
131
147
|
- **Pin a long-lived worktree.** Use `git worktree lock <path>` to keep it out of cleanup; release the
|
|
132
148
|
pin with `git worktree unlock <path>` when ready.
|
|
133
149
|
- **Self-test.** `--self-test` plants delivered, dirty, unmerged, brand-new, work-record and locked cases
|
|
@@ -139,34 +139,121 @@ REMOVED=(); KEPT=(); FREED_KB=0
|
|
|
139
139
|
note_removed() { REMOVED+=("$1"); }
|
|
140
140
|
note_kept() { KEPT+=("$1"); }
|
|
141
141
|
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
# The cache is available only after a successful, valid batch. Failed attempts also stamp the window.
|
|
143
|
+
GH_PULLS=''
|
|
144
|
+
valid_pulls() {
|
|
145
|
+
jq -e -s 'length == 1 and (.[0] | type == "array" and all(.[];
|
|
146
|
+
type == "object" and has("merged_at") and (.merged_at == null or (.merged_at | type == "string")) and
|
|
147
|
+
(.head | type == "object" and (.ref | type == "string") and (.sha | type == "string"))))' "$1" >/dev/null 2>&1
|
|
148
|
+
}
|
|
149
|
+
lock_stat() { stat -c '%i %Y' "$1" 2>/dev/null || stat -f '%i %m' "$1" 2>/dev/null; }
|
|
150
|
+
fresh_stamp() {
|
|
151
|
+
local stamp
|
|
152
|
+
stamp=$(cat "$state/stamp" 2>/dev/null || true)
|
|
153
|
+
[[ "$stamp" =~ ^[0-9]+$ ]] && [ "$now" -lt "$((stamp + 900))" ]
|
|
154
|
+
}
|
|
155
|
+
cached_pulls() {
|
|
156
|
+
fresh_stamp && [ ! -e "$state/failed" ] && valid_pulls "$state/pulls.json" || return 1
|
|
157
|
+
cat "$state/pulls.json"
|
|
158
|
+
}
|
|
159
|
+
take_lock() {
|
|
160
|
+
mkdir "$state/lock" 2>/dev/null && return 0
|
|
161
|
+
local modified retired acquired
|
|
162
|
+
read -r _ modified < <(lock_stat "$state/lock")
|
|
163
|
+
[[ "$modified" =~ ^[0-9]+$ ]] && [ "$now" -ge "$((modified + 900))" ] || return 1
|
|
164
|
+
# Rename atomically: a killed reclaimer can leave only a retired directory, never a blocking marker.
|
|
165
|
+
retired="$state/lock.stale.$$.$RANDOM"
|
|
166
|
+
mv "$state/lock" "$retired" 2>/dev/null || return 1
|
|
167
|
+
acquired=1
|
|
168
|
+
mkdir "$state/lock" 2>/dev/null && acquired=0
|
|
169
|
+
rm -rf "$retired"
|
|
170
|
+
return "$acquired"
|
|
171
|
+
}
|
|
172
|
+
hook_pulls() (
|
|
173
|
+
local repo="$1" common state now temporary lock_inode current
|
|
174
|
+
common=$(git rev-parse --git-common-dir) || exit 1
|
|
175
|
+
state="$common/cratis-cleanup-gh/$repo"
|
|
176
|
+
now=$(date +%s)
|
|
177
|
+
mkdir -p "$state" 2>/dev/null || exit 1
|
|
178
|
+
if ! take_lock; then cached_pulls; exit $?; fi
|
|
179
|
+
read -r lock_inode _ < <(lock_stat "$state/lock")
|
|
180
|
+
temporary=''
|
|
181
|
+
# A retired owner must never remove its replacement's lock.
|
|
182
|
+
trap '[[ -z "$temporary" ]] || rm -f "$temporary"; read -r current _ < <(lock_stat "$state/lock"); [ "$current" != "$lock_inode" ] || rmdir "$state/lock" 2>/dev/null' EXIT
|
|
183
|
+
if fresh_stamp; then cached_pulls; exit $?; fi
|
|
184
|
+
temporary=$(mktemp "$state/pulls.XXXXXX") || exit 1
|
|
185
|
+
printf '%s\n' "$now" > "$temporary" && mv "$temporary" "$state/failed" || exit 1
|
|
186
|
+
printf '%s\n' "$now" > "$temporary" && mv "$temporary" "$state/stamp" || exit 1
|
|
187
|
+
read -r current _ < <(lock_stat "$state/lock")
|
|
188
|
+
[ "$current" = "$lock_inode" ] || exit 1
|
|
189
|
+
# Only the latest 100 closed PRs are searched; older merged branches are simply kept.
|
|
190
|
+
if gh api "repos/$repo/pulls?state=closed&per_page=100" > "$temporary" 2>/dev/null && valid_pulls "$temporary"; then
|
|
191
|
+
read -r current _ < <(lock_stat "$state/lock")
|
|
192
|
+
[ "$current" = "$lock_inode" ] || exit 1
|
|
193
|
+
mv "$temporary" "$state/pulls.json" && rm -f "$state/failed" || exit 1
|
|
194
|
+
cat "$state/pulls.json"
|
|
195
|
+
else exit 1; fi
|
|
196
|
+
)
|
|
197
|
+
GH_POSSIBLE=0; GH_REPO=''
|
|
198
|
+
configure_github() {
|
|
199
|
+
[ "${CRATIS_CLEANUP_NO_GH:-}" != 1 ] || return 0
|
|
200
|
+
command -v gh >/dev/null 2>&1 && command -v jq >/dev/null 2>&1 || return 0
|
|
201
|
+
local remote repo
|
|
202
|
+
remote=$(git remote get-url origin 2>/dev/null) || return 0
|
|
203
|
+
case "$remote" in
|
|
204
|
+
https://github.com/*) repo=${remote#https://github.com/} ;;
|
|
205
|
+
git@github.com:*) repo=${remote#git@github.com:} ;;
|
|
206
|
+
ssh://git@github.com/*) repo=${remote#ssh://git@github.com/} ;;
|
|
207
|
+
*) return 0 ;;
|
|
208
|
+
esac
|
|
209
|
+
repo=${repo%.git}
|
|
210
|
+
[[ "$repo" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || return 0
|
|
211
|
+
GH_REPO=$repo; GH_POSSIBLE=1
|
|
212
|
+
}
|
|
213
|
+
configure_github
|
|
214
|
+
load_pulls() {
|
|
215
|
+
if [ "$HOOK" = 1 ]; then GH_PULLS=$(hook_pulls "$GH_REPO") || return 1
|
|
216
|
+
else GH_PULLS=$(gh api "repos/$GH_REPO/pulls?state=closed&per_page=100" 2>/dev/null) || return 1; fi
|
|
217
|
+
printf '%s' "$GH_PULLS" | valid_pulls /dev/stdin
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Ancestry evidence is resolved during planning; GitHub evidence only after the complete plan exists.
|
|
221
|
+
ancestry_delivered() {
|
|
222
|
+
local sha="$1" track="$3"
|
|
145
223
|
if git merge-base --is-ancestor "$sha" "$default" 2>/dev/null; then
|
|
146
|
-
if ! grep -qx "$sha" "$firstparent"; then
|
|
147
|
-
if [ "$track" = "[gone]" ]; then
|
|
148
|
-
fi
|
|
149
|
-
if [ -n "$br" ] && [ "${CRATIS_CLEANUP_NO_GH:-}" != 1 ] && command -v gh >/dev/null 2>&1; then
|
|
150
|
-
if gh pr list --head "$br" --state merged --json headRefOid -q '.[].headRefOid' 2>/dev/null | grep -qx "$sha"; then
|
|
151
|
-
echo "pull request merged at this commit"; return 0
|
|
152
|
-
fi
|
|
224
|
+
if ! grep -qx "$sha" "$firstparent"; then EVIDENCE="merged into $default"; return 0; fi
|
|
225
|
+
if [ "$track" = "[gone]" ]; then EVIDENCE="merged into $default, upstream gone"; return 0; fi
|
|
153
226
|
fi
|
|
154
227
|
return 1
|
|
155
228
|
}
|
|
229
|
+
github_delivered() {
|
|
230
|
+
printf '%s' "$GH_PULLS" | jq -e --arg br "$1" --arg sha "$2" \
|
|
231
|
+
'any(.[]; .merged_at != null and .head.ref == $br and .head.sha == $sha)' >/dev/null 2>&1
|
|
232
|
+
}
|
|
233
|
+
PLAN_KIND=(); PLAN_PATH=(); PLAN_SHA=(); PLAN_BRANCH=(); PLAN_EVIDENCE=(); NEEDS_GH=0
|
|
234
|
+
plan_item() {
|
|
235
|
+
local kind="$1" path="$2" tip="$3" branch="$4" evidence=''
|
|
236
|
+
if ancestry_delivered "$tip" "$branch" "$([ -n "$branch" ] && track_of "$branch")"; then evidence=$EVIDENCE
|
|
237
|
+
elif [ -n "$branch" ] && [ "$GH_POSSIBLE" = 1 ]; then NEEDS_GH=1; fi
|
|
238
|
+
PLAN_KIND+=("$kind"); PLAN_PATH+=("$path"); PLAN_SHA+=("$tip")
|
|
239
|
+
PLAN_BRANCH+=("$branch"); PLAN_EVIDENCE+=("$evidence")
|
|
240
|
+
}
|
|
156
241
|
track_of() { git for-each-ref --format='%(upstream:track)' "refs/heads/$1" 2>/dev/null; }
|
|
157
242
|
|
|
158
|
-
# ──
|
|
159
|
-
git worktree prune >/dev/null 2>&1
|
|
243
|
+
# ── Plan the entire sweep without removing worktrees or branches ──────────────
|
|
160
244
|
wt=""; sha=""; br=""; locked=0; first=1
|
|
161
245
|
process_worktree() {
|
|
162
246
|
[ -n "$wt" ] || return 0
|
|
163
247
|
if [ "$first" = 1 ]; then first=0; return 0; fi # the main worktree is never removed
|
|
164
|
-
local name="${br:-detached}"
|
|
248
|
+
local name="${br:-detached}"
|
|
165
249
|
if [ "$locked" = 1 ]; then note_kept "worktree $wt ($name): locked"; return 0; fi
|
|
166
250
|
case "$here/" in "$wt"/*) note_kept "worktree $wt ($name): it is the current directory - run the sweep from the main checkout"; return 0 ;; esac
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
251
|
+
plan_item worktree "$wt" "$sha" "$br"
|
|
252
|
+
}
|
|
253
|
+
execute_worktree() {
|
|
254
|
+
local name="${br:-detached}" ev="$1" current
|
|
255
|
+
current=$(git -C "$wt" rev-parse HEAD 2>/dev/null) || return 0
|
|
256
|
+
[ "$current" = "$sha" ] || { note_kept "worktree $wt ($name): tip changed since planning"; return 0; }
|
|
170
257
|
if [ "$INCLUDE_WORK_RECORDS" = 0 ] && [ -d "$wt/.ai-work" ]; then
|
|
171
258
|
local records
|
|
172
259
|
if ! records=$(find "$wt/.ai-work/." -mindepth 1 -maxdepth 1 -print -quit); then
|
|
@@ -191,20 +278,49 @@ while IFS= read -r line; do
|
|
|
191
278
|
done < <(git worktree list --porcelain; echo)
|
|
192
279
|
process_worktree
|
|
193
280
|
|
|
194
|
-
# ── Local branches ────────────────────────────────────────────────────────────
|
|
195
281
|
in_use=$(git worktree list --porcelain | sed -n 's#^branch refs/heads/##p')
|
|
282
|
+
planned_branches=$(printf '%s\n' "${PLAN_BRANCH[@]+"${PLAN_BRANCH[@]}"}")
|
|
196
283
|
while IFS='|' read -r b bsha; do
|
|
197
284
|
if [ -z "$b" ] || [ "$b" = "$default_local" ]; then continue; fi
|
|
198
|
-
printf '%s\n' "$in_use" | grep -qx "$b"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
elif git branch -D "$b" >/dev/null 2>&1; then note_removed "branch $b: $ev"
|
|
202
|
-
else note_kept "branch $b: git refused to delete it"; fi
|
|
203
|
-
else
|
|
204
|
-
note_kept "branch $b: work not delivered to $default"
|
|
285
|
+
if printf '%s\n' "$in_use" | grep -qx "$b"; then
|
|
286
|
+
[ "$DRY" = 0 ] || continue
|
|
287
|
+
printf '%s\n' "$planned_branches" | grep -qx "$b" || continue
|
|
205
288
|
fi
|
|
289
|
+
plan_item branch "$b" "$bsha" "$b"
|
|
206
290
|
done < <(git for-each-ref --format='%(refname:short)|%(objectname)' refs/heads)
|
|
207
291
|
|
|
292
|
+
abort_sweep() {
|
|
293
|
+
echo "cratis-cleanup-worktrees: GitHub evidence unavailable; 0 removed, keeping the entire sweep" >&2
|
|
294
|
+
exit 0
|
|
295
|
+
}
|
|
296
|
+
# Resolve every item's evidence before any deletion, including ancestry-proven items.
|
|
297
|
+
if [ "$NEEDS_GH" = 1 ] && ! load_pulls; then abort_sweep; fi
|
|
298
|
+
for ((i=0; i<${#PLAN_KIND[@]}; i++)); do
|
|
299
|
+
if [ -z "${PLAN_EVIDENCE[i]}" ] && [ -n "${PLAN_BRANCH[i]}" ] && [ -n "$GH_PULLS" ]; then
|
|
300
|
+
if github_delivered "${PLAN_BRANCH[i]}" "${PLAN_SHA[i]}"; then PLAN_EVIDENCE[i]="pull request merged at this commit"
|
|
301
|
+
else result=$?; [ "$result" = 1 ] || abort_sweep; fi
|
|
302
|
+
fi
|
|
303
|
+
done
|
|
304
|
+
|
|
305
|
+
# ── Execute the resolved plan, rechecking tips and worktree safety ─────────────
|
|
306
|
+
git worktree prune >/dev/null 2>&1
|
|
307
|
+
for ((i=0; i<${#PLAN_KIND[@]}; i++)); do
|
|
308
|
+
kind=${PLAN_KIND[i]}; path=${PLAN_PATH[i]}; sha=${PLAN_SHA[i]}; br=${PLAN_BRANCH[i]}; ev=${PLAN_EVIDENCE[i]}
|
|
309
|
+
if [ "$kind" = branch ]; then
|
|
310
|
+
git worktree list --porcelain | sed -n 's#^branch refs/heads/##p' | grep -qx "$br" && continue
|
|
311
|
+
fi
|
|
312
|
+
if [ -z "$ev" ]; then
|
|
313
|
+
if [ "$kind" = worktree ]; then note_kept "worktree $path (${br:-detached}): work not delivered to $default"
|
|
314
|
+
else note_kept "branch $br: work not delivered to $default"; fi
|
|
315
|
+
continue
|
|
316
|
+
fi
|
|
317
|
+
if [ "$kind" = worktree ]; then wt=$path; execute_worktree "$ev"; continue; fi
|
|
318
|
+
[ "$(git rev-parse -q --verify "refs/heads/$br")" = "$sha" ] || { note_kept "branch $br: tip changed since planning"; continue; }
|
|
319
|
+
if [ "$DRY" = 1 ]; then note_removed "branch $br: would delete, $ev"
|
|
320
|
+
elif git branch -D "$br" >/dev/null 2>&1; then note_removed "branch $br: $ev"
|
|
321
|
+
else note_kept "branch $br: git refused to delete it"; fi
|
|
322
|
+
done
|
|
323
|
+
|
|
208
324
|
# ── Report ────────────────────────────────────────────────────────────────────
|
|
209
325
|
summary="cratis-cleanup-worktrees: ${#REMOVED[@]} removed, ${#KEPT[@]} kept"
|
|
210
326
|
[ "$FREED_KB" -gt 0 ] && summary="$summary, $((FREED_KB / 1024)) MB freed"
|
|
@@ -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
|
-
-
|
|
47
|
-
|
|
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
|
|
@@ -55,12 +55,13 @@ commands before choosing the build, regression spec and full gate commands.
|
|
|
55
55
|
## Copy this checklist
|
|
56
56
|
|
|
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
60
|
- [ ] Activate the consumer overlay and select the local image.
|
|
61
61
|
- [ ] Build the consumer, run its regression specs/app action and required gates.
|
|
62
62
|
- [ ] On failure, diagnose, fix the producer, create a distinct stamp and repeat.
|
|
63
|
-
- [ ] Run `check` before every consumer commit and push, and on the producer.
|
|
63
|
+
- [ ] Run `check` before every consumer commit and push, and on the producer; it must report 0 leaks.
|
|
64
|
+
- [ ] Upstream first: a downstream ships only after the upstream has released and its pin is bumped to the released version.
|
|
64
65
|
- [ ] Restore overlays, unset environment overrides, and run `check` again.
|
|
65
66
|
- [ ] Review stamp-scoped `clean` dry-run targets, then apply cleanup.
|
|
66
67
|
|
|
@@ -92,7 +93,10 @@ For npm, complete the repository build before
|
|
|
92
93
|
exit `0`. Inspect package dependencies: every locally changed dependency ID must
|
|
93
94
|
resolve to the stamp, not a published fallback. Chronicle's client solution filter
|
|
94
95
|
can omit `Cratis.Chronicle.CodeAnalysis`; pack its code-analysis package project
|
|
95
|
-
separately.
|
|
96
|
+
separately. The package IDs a producer should ship are listed in
|
|
97
|
+
[dependency-map.tsv](scripts/dependency-map.tsv) (a reference, see
|
|
98
|
+
[references/dependency-map.md](references/dependency-map.md)); compare them with the feed by hand.
|
|
99
|
+
A successful pack alone does not prove dependency closure.
|
|
96
100
|
|
|
97
101
|
### 3. Build an image when needed
|
|
98
102
|
|
|
@@ -237,7 +241,7 @@ minutes, instead of after a release and a pin bump.
|
|
|
237
241
|
- Read [references/npm-yarn.md](references/npm-yarn.md) when packing frontend libraries or restoring Yarn overlays.
|
|
238
242
|
- Read [references/docker-images.md](references/docker-images.md) when publishing a kernel or selecting images in a consumer.
|
|
239
243
|
- Read [references/go-jvm-python-elixir.md](references/go-jvm-python-elixir.md) when the boundary uses another language toolchain.
|
|
240
|
-
- Read [references/worked-examples.md](references/worked-examples.md) when following Chronicle → Studio, Fundamentals → Arc or
|
|
244
|
+
- 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.
|
|
241
245
|
- Read [references/other-environments.md](references/other-environments.md) when running on another developer machine or an agent host.
|
|
242
246
|
|
|
243
247
|
Read `scripts/npm-restore-check.cjs` when inspecting the read-only npm restoration
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
# Dependency map
|
|
5
|
+
|
|
6
|
+
`scripts/dependency-map.tsv` is a reference table of the producer-to-consumer edges between
|
|
7
|
+
Cratis repositories, the pack targets of each producer and the package IDs each ships.
|
|
8
|
+
`cratis-local-packages.sh` does not read it: use it to decide which repositories to pack,
|
|
9
|
+
in which order, and which IDs to expect in the feed. It holds no versions; read pins live
|
|
10
|
+
from the consumer manifests (`Directory.Packages.props`, `package.json`, `Dockerfile`).
|
|
11
|
+
|
|
12
|
+
## Rows
|
|
13
|
+
|
|
14
|
+
Tab-separated, `#` starts a comment.
|
|
15
|
+
|
|
16
|
+
| Row | Fields | Meaning |
|
|
17
|
+
| --- | --- | --- |
|
|
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
|
+
| `ids` | repo, eco, comma-separated IDs | The packages the producer ships in that ecosystem |
|
|
20
|
+
| `uses` | consumer, producer, eco, manifest | An edge; the manifest is the pin file, or the Yarn project directory for npm |
|
|
21
|
+
| `image` | repo, dockerfile, tag suffix | A producer image a consumer builds on |
|
|
22
|
+
|
|
23
|
+
`eco` is `nuget`, `npm` or `image`. For npm, `.` means the repository root.
|
|
24
|
+
|
|
25
|
+
## Using it
|
|
26
|
+
|
|
27
|
+
- Find every `uses` row whose producer is the repository you changed; those consumers
|
|
28
|
+
pin its packages. A consumer may also reach the producer through another repository
|
|
29
|
+
(Fundamentals reaches Chronicle through Arc as well as directly).
|
|
30
|
+
- Pack upstream first. Overlay the earlier producer into the next repository with
|
|
31
|
+
`use-nuget`, pack that, then `restore` it. Use one stamp for the whole chain: `stamp`
|
|
32
|
+
takes one producer, so generate it once and reuse the string.
|
|
33
|
+
- Pack properties from a `pack` row are not accepted by `pack-nuget`. When a row has them,
|
|
34
|
+
run `dotnet pack TARGET -c Release -p:PackageVersion=$stamp -p:Version=$stamp -p:PROP=VAL
|
|
35
|
+
-o "$CRATIS_LOCAL_FEED/nuget"` yourself, with the properties as `-p:` arguments and never
|
|
36
|
+
as exported environment variables (an exported `IsPackaging=true` reaches Chronicle's
|
|
37
|
+
nested `dotnet run` code generator and fails with a multi-target error).
|
|
38
|
+
- After packing, list the feed for the stamp and compare it with the `ids` row; a missing ID
|
|
39
|
+
is a silent partial overlay. Open the nuspec of each packed ID and check that every locally
|
|
40
|
+
changed dependency is at the stamp.
|
|
41
|
+
|
|
42
|
+
## Facts about the chains
|
|
43
|
+
|
|
44
|
+
- Arc and Chronicle depend on each other. Arc's Chronicle-facing packages are built against
|
|
45
|
+
a released Chronicle, and Chronicle's clients against a released Arc. The same holds for
|
|
46
|
+
Chronicle and Orleans. Record the nuspec dependencies and do not break the cycle.
|
|
47
|
+
- Image and package pins are coupled. A consumer that builds `FROM` a producer image may
|
|
48
|
+
derive the tag from the producer's NuGet pin; check its publish workflow before tagging a
|
|
49
|
+
local image.
|
|
50
|
+
- A repository you did not name keeps its released version: Stage reaches Arc, Chronicle
|
|
51
|
+
reaches Orleans. A binary break through that hop is a finding about the consumer's pins.
|
|
52
|
+
- The current Screenplay rows list only npm IDs although Stage and Studio consume its NuGet
|
|
53
|
+
packages; read the IDs from the producer's publish workflow.
|
|
54
|
+
|
|
55
|
+
## Extend it
|
|
56
|
+
|
|
57
|
+
Add `pack` rows only from the producer's publish workflow on the default branch, not a
|
|
58
|
+
stale clone. Add `ids` for every package a consumer must resolve. Add one `uses` row per
|
|
59
|
+
consumer manifest that pins the producer.
|
|
@@ -3,16 +3,28 @@
|
|
|
3
3
|
|
|
4
4
|
# Worked examples
|
|
5
5
|
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Chronicle kernel → Studio integration specs
|
|
9
|
+
- Fundamentals → Arc (NuGet)
|
|
10
|
+
- Components → Studio frontend (npm)
|
|
11
|
+
- Chronicle clients → Direct (NuGet)
|
|
12
|
+
- Screenplay → Stage and Studio (NuGet and npm)
|
|
13
|
+
- Arc → Chronicle (NuGet)
|
|
14
|
+
- Fundamentals → Arc → Chronicle under one stamp
|
|
15
|
+
|
|
16
|
+
Run times below are rounded from one measured run on a shared machine; queue wait is not included.
|
|
6
17
|
Use absolute producer/consumer paths for your checkouts and the entry point's
|
|
7
18
|
`local` helper variable. These recipes define work to execute, not pre-verified
|
|
8
19
|
results. Substitute actual regression filters and CI commands; stop on a failed
|
|
9
20
|
step. Apply the entry point's check/restore/cleanup sequence to both repositories.
|
|
10
21
|
|
|
22
|
+
|
|
11
23
|
## Chronicle kernel → Studio integration specs
|
|
12
24
|
|
|
13
25
|
1. Reproduce Studio's integration bug against its released client/kernel. Record
|
|
14
26
|
the regression filter and inspect Studio's CI build/test commands.
|
|
15
|
-
2. Generate the stamp and pack client packages plus their
|
|
27
|
+
2. Generate the stamp and pack client packages plus their dependencies.
|
|
16
28
|
A client solution filter can omit CodeAnalysis. Include its package explicitly:
|
|
17
29
|
|
|
18
30
|
```bash
|
|
@@ -60,38 +72,160 @@ requirement the consumer's container setup does not satisfy yet), that is a
|
|
|
60
72
|
real finding about the consumer, not about the overlay: read the container log
|
|
61
73
|
before changing the producer.
|
|
62
74
|
|
|
63
|
-
## Fundamentals → Arc
|
|
75
|
+
## Fundamentals → Arc (NuGet)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
stamp=$(bash "$local" stamp "$fundamentals")
|
|
79
|
+
(cd "$fundamentals" && bash "$local" pack-nuget . "$stamp" Fundamentals.slnx)
|
|
80
|
+
bash "$local" use-nuget "$arc" "$stamp"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Gates (Arc `dotnet-build.yml`), run in this order after sourcing the printed env:
|
|
84
|
+
`yarn install --immutable`; in `Source/DotNET/Screenplay.Embedded`: `npm ci --workspaces=false`,
|
|
85
|
+
`npm run typecheck`, `npm test`, `npm run build`; `dotnet build --configuration Release
|
|
86
|
+
-p:CratisSpecsTargetFramework=net10.0`; then `dotnet test <project> --no-build --configuration
|
|
87
|
+
Release --framework net10.0` for each project that `python3 scripts/ci-test-shards.py projects
|
|
88
|
+
--shard specs-N` prints. Finish by listing Arc's `obj/project.assets.json` files for the stamp.
|
|
89
|
+
|
|
90
|
+
- Pass: the Fundamentals IDs in `project.assets.json` are at the stamp, build has 0 warnings and 0 errors, 18 spec projects run and none fail.
|
|
91
|
+
- Time: pack about 4 min, build about 6 min, the spec projects about 18 min.
|
|
92
|
+
- Pitfall: skipping the embedded viewer build makes four `Screenplay.Embedded.Specs` fail
|
|
93
|
+
with a 404 for the viewer. It is the missing step, not the overlay.
|
|
94
|
+
- Pitfall: Arc's own `@cratis/fundamentals` npm line is out of scope for the NuGet stamp.
|
|
95
|
+
- Skip producer specs when the producer is unchanged `origin/main`; its CI covered that SHA.
|
|
96
|
+
|
|
97
|
+
For a JavaScript Fundamentals fix, run the producer's build and `pack-npm`, then `use-npm` in
|
|
98
|
+
Arc and install. Include every affected workspace tarball so no transitive copy stays released.
|
|
99
|
+
|
|
100
|
+
## Components → Studio frontend (npm)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
(cd "$components" && yarn install --immutable && yarn workspace @cratis/components run ci && yarn build)
|
|
104
|
+
stamp=$(bash "$local" stamp "$components")
|
|
105
|
+
(cd "$components" && bash "$local" pack-npm . "$stamp")
|
|
106
|
+
bash "$local" use-npm "$studio" "$stamp"
|
|
107
|
+
(cd "$studio" && yarn install) # not --immutable: the overlay edits the lock
|
|
108
|
+
yarn why @cratis/components
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Gates: from `Source/<App>` run `yarn ci` for each Studio application that uses
|
|
112
|
+
`@cratis/components` (Core, Admin, Editor, EventModels, Viewer, ScreenMigration), `node
|
|
113
|
+
scripts/generate-scene-package-catalog.mjs --check`, then in Core `yarn build` and the
|
|
114
|
+
`initial-bundle-guard.mjs` budget check, and `yarn build-storybook` where the script exists.
|
|
64
115
|
|
|
65
|
-
|
|
66
|
-
`
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
- Pass: every installed `@cratis/components*` copy is at the stamp (`yarn why` and the installed `package.json` files),
|
|
117
|
+
`yarn why` shows one resolution, and every gate above exits 0.
|
|
118
|
+
- Time: Components `ci` about 7 min, all-workspace build about 1 min, pack about 2 min,
|
|
119
|
+
Studio install under 30 s, Studio gates about 8 min.
|
|
120
|
+
- Pitfall: `yarn workspace @cratis/components run ci` builds only the core package. Run
|
|
121
|
+
`yarn build` at the Components root before `pack-npm`, or the adapter tarballs have no
|
|
122
|
+
`dist` and Studio fails with `Failed to resolve entry for package
|
|
123
|
+
"@cratis/components.primereact"`.
|
|
124
|
+
- Pitfall: the root `package.json` version is a placeholder, so the stamp base comes from tags.
|
|
125
|
+
Resolutions bypass the prerelease ranges that npm would otherwise ignore.
|
|
70
126
|
|
|
71
|
-
|
|
72
|
-
`pack-npm`, then `use-npm` in Arc and install. Run Arc frontend build/specs and the
|
|
73
|
-
reproducer. Include all affected workspace tarballs so a transitive dependency
|
|
74
|
-
cannot silently remain released. A green producer-only spec is not consumer proof.
|
|
75
|
-
Finish with the entry point's commit checks, restoration and cleanup.
|
|
127
|
+
## Chronicle clients → Direct (NuGet)
|
|
76
128
|
|
|
77
|
-
|
|
129
|
+
```bash
|
|
130
|
+
stamp=$(bash "$local" stamp "$chronicle")
|
|
131
|
+
(cd "$chronicle" && dotnet pack Source/Clients/Packages.slnf -c Release -p:PackageVersion=$stamp -p:Version=$stamp \
|
|
132
|
+
-p:IsPackaging=true -p:DisableProxyGenerator=true -p:DisableDockerBuild=true -o "$CRATIS_LOCAL_FEED/nuget")
|
|
133
|
+
bash "$local" use-nuget "$direct" "$stamp"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Gates (Direct `build.yml`): `dotnet build Source/Direct/Direct.csproj -c Release
|
|
137
|
+
-p:DisableProxyGenerator=true`, the same in Debug with `-warnaserror`, `dotnet test` of that
|
|
138
|
+
project with `--no-build`, and `dotnet test Source/Assurance/Assurance.csproj`. Then
|
|
139
|
+
check `project.assets.json`. Image-based specs also need the producer image tag.
|
|
140
|
+
|
|
141
|
+
- Pass for a consumer whose pins lag the producer: every Chronicle ID is in the feed at the
|
|
142
|
+
stamp, the Release build with the owner's floors passes, and `project.assets.json` shows every
|
|
143
|
+
stamped ID at the stamp. The Debug `-warnaserror` build and the specs are gated on the consumer owner's pin
|
|
144
|
+
bump (Arc, Orleans, Screenplay, MongoDB.Driver) and on the analyzer fixes the newer pins
|
|
145
|
+
bring. Record that dependency; do not edit the consumer's source or tracked pins.
|
|
146
|
+
- Time: pack about 5 min; the Release build about 2 min.
|
|
147
|
+
- Pitfall: pass pack properties as `-p:` arguments to `dotnet pack`, never
|
|
148
|
+
as environment variables; `pack-nuget` takes none. An exported `IsPackaging=true` reaches Chronicle's nested
|
|
149
|
+
`dotnet run` code generator and fails with a multi-target error.
|
|
150
|
+
- Pitfall: a consumer whose pins lag the producer's own pins fails with NU1109 or NU1605.
|
|
151
|
+
That is the upgrade the consumer would need anyway; record the bump list.
|
|
152
|
+
- Pitfall: a released umbrella package can pull in a released package that is also stamped
|
|
153
|
+
(here an AspNetCore package). Pin it with a direct reference or treat it as a finding.
|
|
154
|
+
|
|
155
|
+
## Screenplay → Stage and Studio (NuGet and npm)
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
stamp=$(bash "$local" stamp "$screenplay")
|
|
159
|
+
(cd "$screenplay" && yarn install && yarn workspaces foreach -Rt --from @cratis/screenplay-mcp-app run build \
|
|
160
|
+
&& bash "$local" pack-nuget . "$stamp" Screenplay.slnx && yarn build && bash "$local" pack-npm . "$stamp")
|
|
161
|
+
bash "$local" use-nuget "$stage" "$stamp"; bash "$local" use-nuget "$studio" "$stamp"
|
|
162
|
+
bash "$local" use-npm "$studio" "$stamp" # at the workspace root, not the nested app
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Gates: Stage's frontend `npm ci`, test and build, then `dotnet build` in Debug and the repository's
|
|
166
|
+
own spec-shard helper; skip probes that need a live kernel or database and say so. Studio:
|
|
167
|
+
`dotnet build` of its solution in Debug with warnings as errors, then the frontend `yarn ci` and
|
|
168
|
+
`yarn build` of every workspace that consumes the language package.
|
|
78
169
|
|
|
79
|
-
|
|
80
|
-
|
|
170
|
+
- Pass: `project.assets.json` shows the stamp in both NuGet consumers and `yarn why` at the Studio workspace root shows one resolution; specs executed > 0.
|
|
171
|
+
- Pitfall: a repository without a committed lockfile fails `yarn install --immutable`; use plain
|
|
172
|
+
`yarn install` there.
|
|
173
|
+
- 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 `yarn pack`;
|
|
175
|
+
a package whose `files` lists a gitignored folder can ship only part of it. Compare the
|
|
176
|
+
tarball file list with the package's `dist` folder.
|
|
177
|
+
- Pitfall: multi-hop. Studio keeps its released Stage; a binary break is a finding.
|
|
178
|
+
|
|
179
|
+
## Arc → Chronicle (NuGet)
|
|
81
180
|
|
|
82
181
|
```bash
|
|
83
|
-
stamp=$(bash "$local" stamp "$
|
|
84
|
-
bash "$local" pack-
|
|
85
|
-
bash "$local" use-
|
|
86
|
-
cd "$consumer"
|
|
87
|
-
yarn install
|
|
182
|
+
stamp=$(bash "$local" stamp "$arc")
|
|
183
|
+
(cd "$arc" && bash "$local" pack-nuget . "$stamp" Arc.slnx)
|
|
184
|
+
bash "$local" use-nuget "$chronicle" "$stamp"
|
|
88
185
|
```
|
|
89
186
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
187
|
+
Gates: Chronicle `dotnet build` in Release with `DisableProxyGenerator=true` and
|
|
188
|
+
`DisableDockerBuild=true`, the non-integration specs; Workbench `yarn install` then `yarn ci`
|
|
189
|
+
with `NODE_OPTIONS=--max-old-space-size=4096`.
|
|
190
|
+
|
|
191
|
+
- Pass: Release build clean; every spec assembly passes; `project.assets.json` shows the stamp for
|
|
192
|
+
every Arc ID.
|
|
193
|
+
- Finding when the consumer's other pins lag: the build passes, but the specs fail with
|
|
194
|
+
`MissingMethodException` on a Fundamentals method the newer Arc calls, because Chronicle pins an
|
|
195
|
+
older Fundamentals than Arc was built against. Record it; do not edit the consumer's pins.
|
|
196
|
+
- Pitfall: build the Arc embedded viewer before the .NET build, or the Arc build fails.
|
|
197
|
+
- Pitfall: Chronicle resolves Arc packages to the stamp only where its manifests pin them; a
|
|
198
|
+
released package that depends on a released Arc package keeps that version. Record it.
|
|
199
|
+
- Pitfall: `use-npm` overlays only the consumer repository root `package.json` and has no option to
|
|
200
|
+
target `Source/Workbench`, so this recipe does not put the Arc npm packages under the Workbench.
|
|
201
|
+
To verify them there, run `pack-npm` in Arc, then add `file:` resolutions to the Workbench
|
|
202
|
+
manifest by hand and run `yarn install`; do not claim the Workbench result without that step.
|
|
203
|
+
- Pitfall: the Workbench is its own Yarn project with its own lockfile. `restore` refuses to
|
|
204
|
+
put back a changed lockfile; in a throwaway worktree check out the two manifests and remove the
|
|
205
|
+
helper's backup folder.
|
|
206
|
+
|
|
207
|
+
## Fundamentals → Arc → Chronicle under one stamp
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
stamp=$(bash "$local" stamp "$arc") # one stamp, reused for every layer
|
|
211
|
+
(cd "$fundamentals" && bash "$local" pack-nuget . "$stamp" Fundamentals.slnx)
|
|
212
|
+
bash "$local" use-nuget "$arc" "$stamp" # Arc builds against the new Fundamentals
|
|
213
|
+
(cd "$arc" && bash "$local" pack-nuget . "$stamp" Arc.slnx)
|
|
214
|
+
bash "$local" restore "$arc"
|
|
215
|
+
bash "$local" use-nuget "$chronicle" "$stamp" # no IDs: every stamped Fundamentals and Arc ID
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Gates: Chronicle Release build and the non-integration specs, as above.
|
|
219
|
+
|
|
220
|
+
Without package IDs `use-nuget` overrides every package in the feed carrying the stamp, so Chronicle
|
|
221
|
+
resolves Fundamentals at the stamp both directly and through Arc; naming only the Arc IDs would leave
|
|
222
|
+
its direct Fundamentals pin at the release and bring back the `MissingMethodException`.
|
|
223
|
+
|
|
224
|
+
- Pass: every spec assembly passes (no `MissingMethodException`), which the two-repository run does
|
|
225
|
+
not achieve. A binary mismatch between layers disappears only when every layer carries the same stamp.
|
|
226
|
+
- Order: pack the upstream first, overlay it into the next repository, pack that, and so on;
|
|
227
|
+
inspect the nuspec of each pack before overlaying it.
|
|
228
|
+
- A stamp is tied to the commit of the repository it was generated from. After changing any
|
|
229
|
+
layer, generate a new stamp and repeat the chain.
|
|
230
|
+
- Time: each pack 15–30 minutes with queue waits; the Chronicle build and specs about 5 minutes
|
|
231
|
+
of run time each.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Reference table of producer/consumer dependencies between Cratis repositories. cratis-local-packages.sh does not read it.
|
|
2
|
+
# Tab-separated; # starts a comment; no versions (pins are read live from consumer manifests).
|
|
3
|
+
# pack <repo> <eco> <target> [PROP=VAL,...] pack targets; PROP is a -p:PROP=VAL argument (never an environment variable)
|
|
4
|
+
# ids <repo> <eco> <comma-separated expected package IDs>
|
|
5
|
+
# uses <consumer> <producer> <eco> <manifest path relative to consumer>
|
|
6
|
+
# image <repo> <dockerfile> <tag-suffix>
|
|
7
|
+
# A target of "." means the repository root (npm workspaces).
|
|
8
|
+
|
|
9
|
+
# Fundamentals
|
|
10
|
+
pack Fundamentals nuget Fundamentals.slnx
|
|
11
|
+
ids Fundamentals nuget Cratis.Fundamentals,Cratis.Metrics.Roslyn,Cratis.OpenTelemetry
|
|
12
|
+
pack Fundamentals npm .
|
|
13
|
+
|
|
14
|
+
# Orleans (edges only)
|
|
15
|
+
pack Orleans nuget Orleans.slnx
|
|
16
|
+
|
|
17
|
+
# Arc
|
|
18
|
+
pack Arc nuget Arc.slnx CratisSpecsTargetFramework=net10.0
|
|
19
|
+
ids Arc nuget Cratis.Arc,Cratis.Arc.Core,Cratis.Arc.MongoDB,Cratis.Arc.EntityFrameworkCore,Cratis.Arc.Swagger,Cratis.Arc.Connections.Azure,Cratis.Arc.ProxyGenerator.Build,Cratis.Arc.Testing
|
|
20
|
+
pack Arc npm .
|
|
21
|
+
ids Arc npm @cratis/arc,@cratis/arc.react,@cratis/arc.react.mvvm,@cratis/arc.vite
|
|
22
|
+
|
|
23
|
+
# Chronicle (clients only; the kernel ships as an image)
|
|
24
|
+
pack Chronicle nuget Source/Clients/Packages.slnf IsPackaging=true,DisableProxyGenerator=true,DisableDockerBuild=true
|
|
25
|
+
ids Chronicle nuget Cratis.Chronicle,Cratis.Chronicle.AspNetCore,Cratis.Chronicle.Aspire,Cratis.Chronicle.CodeAnalysis,Cratis.Chronicle.Connections,Cratis.Chronicle.Contracts,Cratis.Chronicle.Testing,Cratis.Chronicle.XUnit.Integration
|
|
26
|
+
image Chronicle Docker/Production/Dockerfile (none)
|
|
27
|
+
|
|
28
|
+
# Components
|
|
29
|
+
pack Components npm .
|
|
30
|
+
ids Components npm @cratis/components,@cratis/components.primereact,@cratis/components.mui
|
|
31
|
+
|
|
32
|
+
# Screenplay
|
|
33
|
+
pack Screenplay nuget Screenplay.slnx
|
|
34
|
+
pack Screenplay npm .
|
|
35
|
+
ids Screenplay npm @cratis/screenplay-language
|
|
36
|
+
|
|
37
|
+
# Stage
|
|
38
|
+
pack Stage nuget Stage.slnx
|
|
39
|
+
|
|
40
|
+
# Edges (consumer, producer, ecosystem, manifest)
|
|
41
|
+
uses Arc Fundamentals nuget Directory.Packages.props
|
|
42
|
+
uses Arc Chronicle nuget Directory.Packages.props
|
|
43
|
+
uses Arc Screenplay nuget Directory.Packages.props
|
|
44
|
+
uses Chronicle Fundamentals nuget Directory.Packages.props
|
|
45
|
+
uses Chronicle Arc nuget Directory.Packages.props
|
|
46
|
+
uses Chronicle Orleans nuget Directory.Packages.props
|
|
47
|
+
uses Chronicle Arc npm Source/Workbench
|
|
48
|
+
uses Orleans Arc nuget Directory.Packages.props
|
|
49
|
+
uses Orleans Chronicle nuget Directory.Packages.props
|
|
50
|
+
uses Orleans Fundamentals nuget Directory.Packages.props
|
|
51
|
+
uses Stage Fundamentals nuget Directory.Packages.props
|
|
52
|
+
uses Stage Arc nuget Directory.Packages.props
|
|
53
|
+
uses Stage Chronicle nuget Directory.Packages.props
|
|
54
|
+
uses Stage Screenplay nuget Directory.Packages.props
|
|
55
|
+
uses Studio Fundamentals nuget Directory.Packages.props
|
|
56
|
+
uses Studio Arc nuget Directory.Packages.props
|
|
57
|
+
uses Studio Chronicle nuget Directory.Packages.props
|
|
58
|
+
uses Studio Orleans nuget Directory.Packages.props
|
|
59
|
+
uses Studio Stage nuget Directory.Packages.props
|
|
60
|
+
uses Studio Screenplay nuget Directory.Packages.props
|
|
61
|
+
uses Studio Components npm .
|
|
62
|
+
uses Studio Screenplay npm .
|
|
63
|
+
uses Direct Fundamentals nuget Directory.Packages.props
|
|
64
|
+
uses Direct Arc nuget Directory.Packages.props
|
|
65
|
+
uses Direct Chronicle nuget Directory.Packages.props
|
|
66
|
+
uses Direct Orleans nuget Directory.Packages.props
|
|
67
|
+
uses Direct Screenplay nuget Directory.Packages.props
|
|
68
|
+
uses Direct Chronicle image Source/Watchdog/Dockerfile.full
|