@cratis/pi 7.103.7 → 7.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,16 @@ Use real locally built packages to prove a producer fix at its consumer's packag
16
16
  boundary. A **producer** builds the changed library or image; a **consumer** is the
17
17
  downstream repository that reproduces the bug. Start before opening the producer
18
18
  PR whose purpose is that consumer bug, rather than publishing just to find out.
19
- Also use this loop to compare the consumer against lower-layer main.
19
+ Also use this loop for:
20
+
21
+ - Consumer-blocking fixes, dependency features, and breaking-change trials.
22
+ - Release candidates in their main consumer (Specifications in Fundamentals).
23
+ - Cross-repository bisects and build/test timing comparisons against lower-layer main.
24
+ - Chained upgrades (Specifications → Fundamentals → Arc/Chronicle) without releasing each hop.
25
+ - Release blockers: trying Specifications in Fundamentals can expose a generator bug before publishing.
26
+
27
+ Prefer a release/preview for trivial changes, external consumers unable to use
28
+ the local feed, or CI-only behavior this loop cannot reproduce.
20
29
 
21
30
  **Local verification replaces waiting, not shipping.** After local proof, release
22
31
  the producer through the normal authorized workflow and bump the consumer to the
@@ -31,7 +40,7 @@ pin updates use ordinary dependency maintenance; CI changes use the
31
40
 
32
41
  Execute [scripts/cratis-local-packages.sh](scripts/cratis-local-packages.sh), not
33
42
  an improvised replacement. Run `bash "$local" <command> --help` for inputs and
34
- errors. Requires bash and git; add dotnet for NuGet, node and Yarn for npm, Docker
43
+ errors. Requires bash, git and node for package registry operations; add dotnet for NuGet, Yarn and npm for npm packages, Docker
35
44
  for images, or the selected language toolchain. Check availability first:
36
45
 
37
46
  ```bash
@@ -58,13 +67,14 @@ commands before choosing the build, regression spec and full gate commands.
58
67
  - [ ] 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
68
  - [ ] If needed, publish the kernel and assets, then build a matching local image.
60
69
  - [ ] Run `closure` for the consumer and producers, follow the printed commands, and run its one `verify-pack` after the last pack.
61
- - [ ] Activate the consumer overlay and select the local image.
70
+ - [ ] Set a task ID (`--task ID` or `CRATIS_LOCAL_TASK`, convention `owner/repo#number`), activate the consumer overlay and select the local image.
71
+ - [ ] Run `list` (or `list --json`) to inspect recorded producers, tasks and live consumers.
62
72
  - [ ] Build the consumer, run its regression specs/app action and required gates.
63
73
  - [ ] On failure, diagnose, fix the producer, create a distinct stamp and repeat.
64
74
  - [ ] Run `check` before every consumer commit and push, and on the producer; it must report 0 leaks.
65
75
  - [ ] Upstream first: a downstream ships only after the upstream has released and its pin is bumped to the released version.
66
76
  - [ ] Restore overlays, unset environment overrides, and run `check` again.
67
- - [ ] Review stamp-scoped `clean` dry-run targets, then apply cleanup.
77
+ - [ ] Review `finish --task ID`, mark completed work with `--apply`, then review `clean --expired` (or explicit `--stamp`) targets before applying cleanup.
68
78
 
69
79
  ### 1. Stamp
70
80
 
@@ -78,6 +88,8 @@ Format: `<next-patch>-local.<12-digit-UTC-minute>.g<sha7>`, based on the highest
78
88
  downgrades. Confirm tags cover the consumer's released constraints. With no tag,
79
89
  the helper warns and falls back to a base that must be checked before proceeding.
80
90
  The `g` prefix keeps even an all-digit SHA nonnumeric in SemVer.
91
+ On `release/5.0`, use `stamp --base-version 5.0.0 "$producer"` (also with `--train`);
92
+ the strict SemVer core overrides tag discovery verbatim. Ensure it meets consumer minima.
81
93
  Use the identical stamp for packages and images. A changed build needs a distinct
82
94
  stamp; do not overwrite a cached version (the minute and commit determine it).
83
95
 
@@ -99,7 +111,11 @@ separately. The package IDs a producer should ship are listed in
99
111
  the stamp, every overlay, one `pack-*` command per map row (with its `--property` arguments) and
100
112
  ONE `verify-pack` for the whole plan (see [references/dependency-map.md](references/dependency-map.md)).
101
113
  Run the printed commands in order. A successful pack alone does not prove dependency closure;
102
- `verify-pack` does.
114
+ `verify-pack` does. After successful packing, the helper atomically records package IDs,
115
+ producer worktree, branch and HEAD in `FEED/stamps/STAMP/record.json` (mode `0600`).
116
+ Pass `--task ID` to `pack-*` and `use-*`, or set `CRATIS_LOCAL_TASK`; without either,
117
+ the recorded task is null. IDs match `^[A-Za-z0-9][A-Za-z0-9._#:/-]{0,127}$` and
118
+ are JSON values, never paths. Prefer `owner/repo#number`.
103
119
 
104
120
  ### 3. Build an image when needed
105
121
 
@@ -126,9 +142,18 @@ export CHRONICLE_KERNEL_VERSION="$stamp" # Studio integration specs, if used
126
142
  ```
127
143
 
128
144
  Pass: the helper reports overridden IDs and restore resolves them to local
129
- artifacts. Follow any printed `--configfile` instruction. Run Yarn install after
145
+ artifacts. To isolate NuGet downloads per worktree, set the optional
146
+ `NUGET_PACKAGES` export printed by `use-nuget` before restore and keep it set through
147
+ `clean`; see [versions and cache](references/dotnet-nuget.md#versions-and-cache).
148
+ Follow any printed `--configfile` instruction. Run Yarn install after
130
149
  `use-npm`; it adds file-tarball resolutions, not links to source folders.
131
150
 
151
+ For independent NuGet producer versions, explicitly opt in with
152
+ `verify-pack --composition FILE`, `use-nuget CONSUMER --composition FILE
153
+ [--pin-transitive]`, then `verify-assets CONSUMER --composition FILE`. See the
154
+ [manifest and native-validation workflow](references/dotnet-nuget.md#mixed-version-compositions-opt-in).
155
+ No producer is rebuilt; restore before changing a composition or an active legacy overlay.
156
+
132
157
  ### 5. Build and exercise the consumer
133
158
 
134
159
  From the consumer root, with the printed NuGet environment sourced:
@@ -181,18 +206,65 @@ build output, not pre-existing work.
181
206
 
182
207
  ### 8. Clean
183
208
 
209
+ `list [--json] [--stamp STAMP]` is a lock-free, read-only inventory, including legacy
210
+ artifacts without records. It shows producer ownership, task state, live/total consumers
211
+ and age, plus recorded state. Invalid records appear as `invalid` rows (JSON includes
212
+ `error`); listing continues and exits `1`. `use-*` records each consumer, including every
213
+ composition member stamp. `restore` restores overlays first, then updates only those
214
+ stamps' consumer entries; an invalid selected record warns with its path and exits `1`.
215
+ A registry-write failure after activation warns and exits `1`, keeping the overlay for restoration.
216
+
217
+ ```bash
218
+ bash "$local" list
219
+ bash "$local" finish --task owner/repo#number
220
+ bash "$local" finish --task owner/repo#number --apply
221
+ ```
222
+
223
+ `finish` is dry-run by default, marks task completion only, and succeeds with a message
224
+ if no record names the task and all records are valid. It warns and skips invalid records
225
+ without rewriting them, continues with valid matches, and exits `1` if any were skipped.
226
+ It never restores consumers or cleans packages automatically.
227
+
184
228
  ```bash
185
229
  bash "$local" clean "$consumer" --stamp "$stamp"
186
230
  # Review listed subjects and stop task-owned containers first.
187
231
  bash "$local" clean "$consumer" --stamp "$stamp" --apply
188
232
  ```
189
233
 
234
+ `clean --stamp STAMP` validates only that stamp's record; default clean and `--all`
235
+ validate all selected records. An invalid selected record refuses cleanup (`2`).
236
+
237
+ Use `clean --expired` for retention-based cleanup, then add `--apply` after review.
238
+ It keeps unrecorded stamps, active compositions and live manifest/overlay references
239
+ (in registered sessions or recorded consumer worktrees). Otherwise, a stamp expires
240
+ when every non-null producing task is finished (at least one required), all recorded
241
+ consumers are gone (at least one required), or a real release at or above its base exists,
242
+ in that order. Offline evidence is NuGet `.nupkg.metadata` in the shared or registered
243
+ private caches; npm has no offline release check. `--online` opts into NuGet/npm
244
+ registry checks with a 10-second timeout; failures provide no evidence. Each stamp
245
+ prints `expired STAMP: REASON` or `kept STAMP: REASON`; kept stamps succeed (`0`).
246
+ `list --json` reports the same offline decision in `expiry:{expired,reasons,kept}`.
247
+ `--expired` cannot combine with `--stamp` or `--all`. It retains the same lock, fuse
248
+ and dry-run default. Applying also prunes gone consumers and missing session registrations.
249
+
190
250
  Pass: expected subjects listed, then removed without force. Cleanup covers local
191
251
  feed packages, NuGet version folders, discoverable Yarn cache entries, local image
192
- tags and registered sessions; unresolved npm backups block it. Missing Docker/Yarn
252
+ tags, stamp record directories and registered sessions; unresolved npm backups block it. Missing Docker/Yarn
193
253
  probes are reported as skipped cleanup, not proof those stores are empty. Default
194
254
  fuse: 500 subjects; investigate an unexpectedly large list instead of raising it.
195
- Prefer `--stamp` to `--all` on shared machines.
255
+ NuGet cleanup scans the active shared cache and every registered private cache.
256
+ It removes only recognized local/train version folders. Missing or mismatched
257
+ private-cache markers, symlinks and paths outside the consumer's Git directory
258
+ refuse the entire request. `restore` keeps private caches. After restoring overlays
259
+ and removing live stamps, use `clean "$consumer" --private-cache` to review whole-cache
260
+ removal, then add `--apply` to remove the marked cache and its session registration.
261
+ Prefer `--stamp` to `--all`. Legacy cleanup refuses the entire request (exit `2`,
262
+ also in dry-run) for registered live overlay/manifest references; it reports kept
263
+ stamps and paths. Restore/remove references, then retry. This is not an ownership
264
+ registry; unregistered consumers need manual review. To retire a hand-rolled
265
+ `-local.N` feed, restore released pins/sources and switch to helper stamps; review
266
+ old artifacts separately because `clean` does not recognize them. See
267
+ [migration](references/dotnet-nuget.md#retire-a-hand-rolled-feed).
196
268
 
197
269
  ## Configuration and results
198
270
 
@@ -203,7 +275,9 @@ is sourced before defaults. Do not put credentials in it. Environment settings:
203
275
  | --- | --- |
204
276
  | `CRATIS_LOCAL_FEED` | `${XDG_CACHE_HOME:-$HOME/.cache}/cratis/local-feed`; outside tracked files |
205
277
  | `CRATIS_LOCAL_SESSION_DIR` | `<absolute-git-dir>/cratis-local`; per-worktree overlays/backups |
206
- | `NUGET_PACKAGES` | `$HOME/.nuget/packages`; shared cache, unique version folders |
278
+ | `NUGET_PACKAGES` | `$HOME/.nuget/packages`; active cache scanned by `clean` |
279
+ | `CRATIS_LOCAL_TASK` | Optional task ID, convention `owner/repo#number`; `--task ID` on pack/use/finish overrides it |
280
+ | `CRATIS_LOCAL_PRIVATE_CACHE` | `0`; `1` opts `use-nuget` into a marked worktree cache, also available with `--private-cache` |
207
281
  | `CRATIS_LOCAL_CLEAN_MAX` | `500`; cleanup subject fuse |
208
282
  | `CRATIS_LOCAL_RUN` | Optional executable wrapper receiving `build <label> -- <command...>` |
209
283
 
@@ -216,7 +290,7 @@ doing read-only discovery.
216
290
  Helper exit codes: `0` success, `1` findings/failed operation, `2` cannot run or
217
291
  invalid input. Neither `1` nor `2` is a pass. After script changes, execute
218
292
  `bash "$local" --self-test`; pass requires printed passed/skipped counts and exit
219
- `0`. npm overlay/restore tests run when node is on the inherited PATH; real packing
293
+ `0`. Registry tests require node; npm overlay/restore tests use the inherited PATH. Real packing
220
294
  is not exercised by this hermetic self-test. Report any unrun ecosystem checks.
221
295
 
222
296
  ## Why the local loop
@@ -3,6 +3,17 @@
3
3
 
4
4
  # .NET and NuGet
5
5
 
6
+ ## Contents
7
+
8
+ - CPM overlay
9
+ - Registry and list
10
+ - Source mapping
11
+ - Versions and cache
12
+ - Retire a hand-rolled feed
13
+ - Mixed-version compositions (opt-in)
14
+ - Non-CPM escape
15
+ - Restoration and drift
16
+
6
17
  ## CPM overlay
7
18
 
8
19
  Execute the helper from the entry point. `pack-nuget PRODUCER STAMP TARGET...`
@@ -14,10 +25,11 @@ them from an accidental published fallback.
14
25
  `use-nuget CONSUMER STAMP [ID...]` requires a root `Directory.Packages.props`.
15
26
  Without IDs it selects every package with `<id>.<stamp>.nupkg` in the feed,
16
27
  including transitive dependencies absent from the consumer props. The generated
17
- overlay imports the consumer's real props, uses `PackageVersion Update` for IDs
18
- already pinned there and `PackageVersion Include` for the others, and enables
19
- `CentralPackageTransitivePinningEnabled` so the added IDs pin transitive packages.
20
- An `Update` alone cannot add an absent ID. It does not copy or edit the props in git.
28
+ overlay imports each project's nearest `Directory.Packages.props` and replaces
29
+ owned `PackageVersion` items only when CPM is enabled, honoring nested props files.
30
+ CPM opt-out projects get owned versions through late `PackageReference Update`
31
+ items in `CustomAfterMicrosoftCommonTargets`, chaining any existing targets import;
32
+ consumer files are never edited.
21
33
 
22
34
  ```bash
23
35
  bash "$local" use-nuget "$consumer" "$stamp" Cratis.Chronicle Cratis.Chronicle.CodeAnalysis
@@ -31,8 +43,8 @@ dotnet build path/to/consumer.slnx -c Release --no-restore -warnaserror
31
43
  Transitive pinning stays as the consumer sets it; `--pin-transitive` forces it on so stamped IDs the
32
44
  consumer does not pin still take effect (it can raise NU1109 floors).
33
45
 
34
- The printed properties are `DirectoryPackagesPropsPath` and
35
- `RestoreAdditionalProjectSources`. For an IDE, launch it from that shell, or pass
46
+ The printed properties include `DirectoryPackagesPropsPath`,
47
+ `RestoreAdditionalProjectSources` and `CustomAfterMicrosoftCommonTargets`. For an IDE, launch it from that shell, or pass
36
48
  the printed `-p:` properties explicitly to its build. Verify restore uses the
37
49
  stamp in `obj/project.assets.json`; shell environment changes do not reconfigure
38
50
  an already-running IDE. A config overlay printed below must also reach IDE restore.
@@ -40,6 +52,55 @@ an already-running IDE. A config overlay printed below must also reach IDE resto
40
52
  The `CRATIS_LOCAL_RUN` adapter applies to pack steps, not these restore/build
41
53
  commands; route each heavy command separately through the host's build queue.
42
54
 
55
+ ## Registry and list
56
+
57
+ Package operations require node for the stamp registry. Successful `pack-nuget` and
58
+ `pack-npm` calls record package IDs, producer repo/worktree, branch, HEAD and UTC time
59
+ in `FEED/stamps/STAMP/record.json` (schema `1`, mode `0600`). `--task ID` on pack/use
60
+ commands overrides `CRATIS_LOCAL_TASK`; otherwise task is null. Prefer `owner/repo#number`.
61
+ IDs match `^[A-Za-z0-9][A-Za-z0-9._#:/-]{0,127}$`; they are JSON values, never paths.
62
+
63
+ `list [--json] [--stamp STAMP]` joins feed artifacts with records without locking or
64
+ writing. Legacy stamps appear with `recorded:false`. Text shows stamp, ecosystem,
65
+ package count, producer `repo@branch`, task state, live/total consumers, age and recorded state. JSON
66
+ has top-level `schema:1`, `feed` and `stamps`; each stamp includes packages, producers,
67
+ tasks, consumer states (`live`, `restored`, `gone`), creation time and age in seconds.
68
+ Its `expiry:{expired,reasons,kept}` uses the same offline decision as `clean --expired`:
69
+ keep unrecorded stamps, active compositions and live manifest/overlay references first;
70
+ otherwise expire finished producing tasks, all consumers gone, or a release at or above
71
+ the stamp's base, in that order. A taskless pack alone never expires by task completion;
72
+ a stamp without consumers never expires by consumers gone. Live references include
73
+ registered sessions and recorded consumer worktrees, even after task completion.
74
+ A live consumer must still be a Git root and retain an env, overlay, backup or composition.
75
+ `use-*` records consumers after activation (every composition member stamp); `restore`
76
+ restores overlays first, then marks only the session's stamps restored without erasing
77
+ history. An invalid selected record prints a `WARNING:` naming its path and exits `1`;
78
+ unrelated records are not read. A registry error after activation warns, exits `1` and
79
+ leaves the overlay intact. Pack/use still refuse invalid target records (`2`); dry runs
80
+ write no records. `list` keeps listing invalid records as `recorded:"invalid"` with an
81
+ `error` string in JSON (text: `RECORDED` is `invalid`) and exits `1` if any are invalid.
82
+
83
+ ```bash
84
+ bash "$local" list --json
85
+ bash "$local" finish --task owner/repo#number
86
+ bash "$local" finish --task owner/repo#number --apply
87
+ ```
88
+
89
+ `finish` previews by default; `--apply` marks that task finished in every matching
90
+ record. Invalid records are skipped with a `WARNING:` naming each path, never rewritten;
91
+ valid matches are still marked, and any skipped record makes the exit code `1`. No match
92
+ with all records valid is a successful no-op with a message. It does not restore overlays
93
+ or delete packages. Stamp-scoped `clean --apply` reads only the selected record and removes
94
+ its directory with its artifacts; invalid selected records refuse cleanup (`2`). Default
95
+ clean and `--all` validate all selected records and fail closed (`2`) on any invalid one.
96
+ Review the dry run first. For retention-based cleanup, run `clean --expired`, then
97
+ `clean --expired --apply` after reviewing `expired STAMP: REASON` and `kept STAMP: REASON`.
98
+ Kept stamps are success (`0`); invalid records or scan errors refuse cleanup (`2`).
99
+ `--expired` is exclusive with `--stamp` and `--all`, uses the same lock and fuse, and
100
+ never removes an unrecorded stamp. Apply also prunes gone consumers from remaining
101
+ records and removes missing session directories' registrations. A consumer is gone
102
+ when its worktree is missing/not a Git root or its session directory is missing.
103
+
43
104
  ## Source mapping
44
105
 
45
106
  If a tracked root NuGet config has `packageSourceMapping`, the helper prints an
@@ -59,11 +120,141 @@ A stale highest tag or dependency with a newer minimum needs investigation, not
59
120
  warning suppression. The suffix is `-local.<12-digit-UTC-minute>.g<sha7>`; the `g` prefix makes an
60
121
  all-digit SHA unambiguously nonnumeric in SemVer. Dirty edits are not encoded.
61
122
  Wait for a distinct UTC minute or commit before repacking changed contents.
123
+ On a major-version branch, use `bash "$local" stamp --base-version 5.0.0 "$producer"`
124
+ (also with `--train`). It accepts only a SemVer core without leading zeros,
125
+ prerelease or metadata and uses it verbatim; no patch increment is added.
126
+ An explicit base still must meet the consumer's minimum.
127
+
128
+ Opt into a private cache when a shared cached package could mask a same-version
129
+ repack. `use-nuget --private-cache` creates a marked cache at
130
+ `<absolute-git-dir>/cratis-local-nuget` and records it in the session's `nuget-cache`
131
+ file. The printed env file exports `NUGET_PACKAGES`. This works with both stamps
132
+ and `--composition`. Set `CRATIS_LOCAL_PRIVATE_CACHE=1` to opt in through configuration.
133
+ Without either option, activation keeps its default behavior.
134
+
135
+ ```bash
136
+ bash "$local" use-nuget "$consumer" "$stamp" --private-cache
137
+ # Source the env path printed by use-nuget, then restore and verify.
138
+ dotnet restore path/to/consumer.slnx
139
+ bash "$local" restore "$consumer"
140
+ # Remove any remaining live manifest references before cleanup.
141
+ bash "$local" clean "$consumer" --stamp "$stamp"
142
+ bash "$local" clean "$consumer" --stamp "$stamp" --apply
143
+ unset NUGET_PACKAGES
144
+ ```
145
+
146
+ A private cache still reuses its own downloads. Changed bits need a distinct stamp
147
+ or reviewed stamp-scoped cleanup before repacking. `clean` scans the active
148
+ `NUGET_PACKAGES` (or `$HOME/.nuget/packages`) and every registered private cache,
149
+ even after the env export is unset. It removes only recognized `-local`/`-train`
150
+ version folders and keeps released versions. A missing or mismatched marker,
151
+ symlink or cache path outside the consumer's Git directory stops the entire clean
152
+ with exit `2`. Registered live references also refuse cleanup, even in dry-run.
153
+
154
+ `restore` keeps the private cache. To remove it entirely, restore overlays and
155
+ remove all live stamp references first. Review `clean "$consumer" --private-cache`,
156
+ then add `--apply`. This removes the marked cache and its `nuget-cache` registration.
157
+ Never remove an entire shared cache.
158
+
159
+ `clean --expired` also scans `$HOME/.nuget/packages` when `NUGET_PACKAGES` is set.
160
+ Offline release evidence is a non-prerelease `lowercase-id/X.Y.Z/.nupkg.metadata`
161
+ at or above the record's base in the shared or any registered private cache, for
162
+ any recorded NuGet ID. Released folders are evidence only, never cleanup targets.
163
+ Add `--online` to opt into NuGet flat-container and npm registry release checks
164
+ with a 10-second timeout; missing evidence and network failures simply keep that
165
+ expiry reason from applying. `list` never checks online.
166
+
167
+ The helper uses a folder feed, so NuGet's HTTP cache is not involved. When diagnosing
168
+ an HTTP feed instead, `dotnet restore --no-http-cache` bypasses its HTTP cache only;
169
+ it does not bypass packages already in `NUGET_PACKAGES`. Use a fresh worktree cache
170
+ or review stamp-scoped cleanup too, and preserve any required `--configfile` option.
171
+
172
+ ## Retire a hand-rolled feed
173
+
174
+ Stop packing new `-local.N` versions. Restore consumers' released pins and sources,
175
+ then use helper-generated stamps and overlays for the next trial. Confirm restore
176
+ resolves the helper stamp and run `check` before committing. Inventory the old
177
+ feed separately: `clean` recognizes only its local/train grammar, not `-local.N`.
178
+ Remove old artifacts only after checking all their consumers and reviewing exact
179
+ targets; never rename old packages into a helper stamp or clear a whole shared cache.
180
+
181
+ ## Mixed-version compositions (opt-in)
182
+
183
+ Use a composition when products have independent local versions. It never packs
184
+ or rebuilds producers. Write a tab-separated manifest under `.ai-work/`:
185
+
186
+ ```text
187
+ # Separate fields with literal tabs.
188
+ producer Chronicle 22.138.1-local.202610240544.gd1ad215 /absolute/worktree/Chronicle
189
+ producer Screenplay 0.2.2-local.202610240544.g91916a3 /absolute/worktree/Screenplay
190
+ producer Stage 0.1.18-local.202610240544.g524bdce /absolute/worktree/Stage
191
+ package Chronicle Cratis.Chronicle.InProcess
192
+ package Screenplay Cratis.Screenplay
193
+ package Stage Cratis.Stage
194
+ ```
195
+
196
+ Each producer needs a valid local/train stamp, an existing absolute Git worktree
197
+ root and at least one package. Paths declare provenance, not cryptographic artifact
198
+ authenticity. Package IDs have one owner, case-insensitively.
199
+ Declare the complete local dependency closure. Only blank lines and lines starting
200
+ with `#` are ignored. No npm rows or implicit merging with an active overlay.
201
+
202
+ ```bash
203
+ bash "$local" verify-pack --composition .ai-work/composition.tsv --json
204
+ bash "$local" use-nuget "$consumer" --composition .ai-work/composition.tsv --pin-transitive
205
+ # Source the printed env; use the printed --configfile for consumer restore.
206
+ # Run the consumer's own restore/build/specs, then:
207
+ bash "$local" verify-assets "$consumer" --composition .ai-work/composition.tsv --json
208
+ bash "$local" restore "$consumer"
209
+ ```
210
+
211
+ `verify-assets` reads every `obj/project.assets.json` under the consumer and fails
212
+ closed on any project that resolved an owned ID at another version, including stale
213
+ assets from an earlier restore. Restore every project under the consumer with the
214
+ printed config, or remove the `obj` folders of projects outside the build, before
215
+ running it.
216
+
217
+ Transitive pinning is optional. The overlay imports the consumer's CPM file and
218
+ removes/re-includes owned IDs at exact producer versions. Its generated NuGet
219
+ config gives local IDs exclusive exact source mappings, removes conflicting remote
220
+ exact mappings and preserves unrelated sources/mappings/settings. Remote wildcards
221
+ remain less specific. Always pass the printed config during restore. Consumer
222
+ files are never edited. Ambiguous or inherited-only source configuration fails
223
+ closed instead of guessing.
224
+
225
+ Validation requires Node, `unzip` and the selected .NET SDK. A scratch MSBuild task
226
+ loads its `NuGet.Versioning.dll`: `NuGetVersion.TryParse`, `VersionRange.TryParse`
227
+ and `VersionRange.Satisfies` supply native semantics, never a range approximation.
228
+ Its `NuGet.Frameworks.dll` supplies `NuGetFramework.Parse` and
229
+ `FrameworkReducer.GetNearest` for selecting the applicable dependency group.
230
+ Nuspec XML is parsed structurally, across dependency groups. For example,
231
+ `[22.136.4,23.0.0)` admits the selected Chronicle prerelease above. Missing/corrupt
232
+ packages, mismatched metadata, malformed/incompatible ranges and undeclared local
233
+ dependencies fail. Nothing is rebuilt to make a range pass.
234
+
235
+ Assets verification examines every `obj/project.assets.json` and target, checks
236
+ package versions against `libraries`, and validates dependency edges against their
237
+ resolved target. The nearest native framework group must have its owned dependencies
238
+ consumed and represented by target edges. Owned IDs must resolve at their producer's exact version. Unused
239
+ declared IDs need not appear, but at least one owned ID must be consumed. Malformed
240
+ or failed-restore assets, missing dependency nodes and undeclared local packages fail.
241
+ A range violation on an edge where neither end is an owned ID is NuGet's own NU1608
242
+ case and exists without the overlay too, so it is printed as a `WARNING:` on stderr
243
+ (and listed under `warnings` with `--json`) instead of refusing the composition.
244
+ Any violation on an edge to or from an owned ID still fails.
245
+
246
+ Activation stages the normalized manifest, props, config and env together. A shared
247
+ mutation lock and journal protect publication and registration. The next mutation
248
+ rolls back incomplete work or finishes committed work after interruption. Changed
249
+ files, unknown ownership and live locks are preserved and refused: inspect rather
250
+ than deleting a lock by age. Identical activation is a no-op; restore before changing
251
+ a composition. Incompatible legacy NuGet/npm sessions are refused with backups
252
+ untouched. Active compositions retain member artifacts against `clean`; restore
253
+ before cleaning any member. Repeated restore is safe and never edits consumer files.
62
254
 
63
- The existing global cache is shared safely only because local versions have
64
- unique directories. Do not repack changed bits under the same version. Use
65
- `clean --stamp "$stamp"` to list that stamp's version folders, then `--apply`
66
- after review; do not delete the entire NuGet cache.
255
+ Exit codes: `0` success; `1` findings; `2` invalid input, unavailable native validation
256
+ or unsafe state. Composition JSON is `{"packages":[{"id":...,"version":...,"producer":...}]}`,
257
+ with no global `stamp`. Legacy JSON and uniform-stamp commands remain unchanged.
67
258
 
68
259
  ## Non-CPM escape
69
260
 
@@ -75,12 +266,14 @@ graph. Never edit versions intended to ship merely to make local proof work.
75
266
 
76
267
  ## Restoration and drift
77
268
 
78
- `restore` removes session props, generated config and env files, then runs `check`.
269
+ `restore` removes session props, targets, generated config and env files, then runs `check`.
79
270
  It cannot unset variables in the calling shell:
80
271
 
81
272
  ```bash
82
273
  bash "$local" restore "$consumer"
83
274
  unset DirectoryPackagesPropsPath RestoreAdditionalProjectSources
275
+ export CustomAfterMicrosoftCommonTargets="${_CratisLocalPreviousAfterMicrosoftCommonTargets:-}"
276
+ unset _CratisLocalPreviousAfterMicrosoftCommonTargets
84
277
  bash "$local" check "$consumer"
85
278
  ```
86
279
 
@@ -14,6 +14,11 @@ stamp=$(bash "$local" stamp "$producer")
14
14
  bash "$local" pack-npm "$producer" "$stamp"
15
15
  ```
16
16
 
17
+ Pass `--task ID` to pack/use, or set `CRATIS_LOCAL_TASK` (convention `owner/repo#number`),
18
+ to associate the stamp with work. Successful packing records package IDs and producer
19
+ HEAD; activation records the consumer after publication. See
20
+ [Registry and list](dotnet-nuget.md#registry-and-list) for inventory and task completion.
21
+
17
22
  It requires clean tracked workspace manifests, temporarily changes all non-private
18
23
  workspace versions and packs actual tarballs. It restores those manifests on
19
24
  success and failure. Tarballs are named `<scope>-<name>-<version>.tgz` (`@cratis/foo` becomes
@@ -78,3 +83,12 @@ consumer manifests. Reinstall released dependencies after restoration, then run
78
83
  Review `clean "$consumer" --stamp "$stamp"` before `--apply`. Passing the consumer
79
84
  allows Yarn cache discovery; no consumer or unavailable Yarn yields a reported
80
85
  skip. Unresolved backups block cleanup rather than silently losing recovery data.
86
+ `restore` retains consumer history in the registry; stamp-scoped `clean --apply`
87
+ removes the stamp's record directory together with its artifacts.
88
+ Alternatively review `clean "$consumer" --expired`, then add `--apply`. Live manifest
89
+ or overlay references and active compositions always retain the stamp; unrecorded
90
+ stamps require explicit `--stamp`. Finished producing tasks or all consumers gone
91
+ can expire npm stamps. npm has no offline release evidence: `--online` opts into
92
+ registry release checks (10-second timeout); failed checks never remove a stamp.
93
+ Kept stamps succeed (`0`). Applying also prunes gone consumers and missing sessions;
94
+ `--expired` cannot combine with `--stamp` or `--all`.
@@ -32,6 +32,13 @@ sorts above the released pin like a local stamp does.
32
32
 
33
33
  ## Recording consumed versions
34
34
 
35
+ `pack-*` and `use-*` accept `--task ID` (or `CRATIS_LOCAL_TASK`, convention
36
+ `owner/repo#number`). Successful packs record producer HEADs and IDs in the stamp
37
+ registry; use records consumers. `list [--json]` includes train stamps and their
38
+ owners, tasks and consumers. `finish --task ID` previews completion; add `--apply`
39
+ to mark the task finished without removing artifacts. See
40
+ [Registry and list](dotnet-nuget.md#registry-and-list).
41
+
35
42
  `verify-pack STAMP --consumer C --producer P... --json` and
36
43
  `verify-assets CONSUMER STAMP [--npm] [--consumer C --producer P...] --json` print one line:
37
44
  `{"stamp":"...","packages":[{"id":"...","version":"..."}]}`. Findings go to stderr and the exit status
@@ -41,3 +48,12 @@ is unchanged, so a train tool can record the consumed versions without scraping
41
48
 
42
49
  `clean --stamp STAMP` lists and removes the feed files, cache folders and Yarn cache entries of
43
50
  that one train stamp; a local stamp is not matched. Review the dry run before `--apply`.
51
+ Applying also removes `FEED/stamps/STAMP`, including its registry record.
52
+
53
+ For retention-based cleanup of both local and train stamps, review `clean --expired`
54
+ before adding `--apply`. Unrecorded stamps, active compositions and live references
55
+ are kept; otherwise finished producing tasks, all consumers gone, or a real release
56
+ at or above the base can expire a stamp. Offline evidence uses NuGet shared/private
57
+ caches; `--online` opts into NuGet/npm registry checks with a 10-second timeout.
58
+ Network failure is not evidence. Kept stamps succeed (`0`); apply also prunes gone
59
+ consumers and missing sessions. `--expired` cannot combine with `--stamp` or `--all`.