Repository navigation
Commit ba4afcc
fix(release): judge a .objectstack-sha console bundle against objectui's own build of that commit (#24532)
Fixes #24510
Clause-②: no
## What this changes
On a console pin whose objectui commit carries `.objectstack-sha`,
`scripts/build-console.sh` stands THIS tree in for objectui's pinned
objectstack commit (`OBJECTSTACK_ROOT`). So the spec in objectui's
install is this tree's own. The script handed exactly that spec to
`assert-console-spec-injection.mjs` as `--vendored`. It equalled
`--injected`, so the assertion printed "no observable skew", stamped
`skew: false` with null witnesses, and exited 0 before it looked at the
bundle at all. `check:console-injection --require-stamp` then replayed
"Nothing assertable". Both release paths passed any bundle.
- `scripts/build-console.sh`: on the `.objectstack-sha` branch, before
the stand-in, it now builds objectui's OWN `@objectstack/*` at its
`.objectstack-sha`, by objectui's own recipe (`node
scripts/objectstack-pin.mjs build` with `OBJECTSTACK_ROOT` and
`OS_SKIP_DTS` unset). It keeps a copy of that cache, the pin script's
`.stamp.json` included, at
`node_modules/.cache/objectstack-pin-reference` in the objectui build
tree, outside the cache the stand-in replaces. That copy's `spec` is
passed as `--vendored`. A rerun in the same build tree reuses it while
its stamp says `source: pin` at this pin. A pin without
`.objectstack-sha` is unchanged: `--vendored` is still objectui's
install.
- `scripts/assert-console-spec-injection.mjs`: when the `--objectui`
tree carries `.objectstack-sha`, `--vendored` must sit beside a
pin-script stamp with `source: pin` and `sha` equal to that pin.
Anything else is refused as cannot-judge (exit 2), never read as "no
skew": the stand-in (`source: external`), the install's own copy (no
stamp), or a build of another commit. The stamp entry now records
`vendoredFrom` and `vendoredPin`. The verdict text names the pinned
reference (`pin-only`, not `published-only`). The probe derivation in
`console-spec-probes.mjs` is unchanged.
- `scripts/check-console-injection.mjs`: the replay names the pinned
reference in its no-skew line and in its red verdict. Self-test battery
15 (17 cases, floor 17; roster floor 12 → 13) covers four things. It
refuses the three wrong references. It stamps a real witness against the
right one. It goes red on a bundle carrying the pinned spec, at build
time and on replay. It passes the honest no-skew case and names the
reference in it.
- `packages/spec/scripts/build-console-objectstack-root.test.ts`: the
existing branch test (it pins this exact branch of `build-console.sh`)
now expects three things. The native pin build runs before the stand-in,
without `OBJECTSTACK_ROOT` and without `OS_SKIP_DTS`. The reference
cache and its pin stamp survive the stand-in. A rerun reuses the
reference only while its stamp names the pin.
The producer of the vacuity was the wiring in `build-console.sh`, so the
fix is there. The assertion's new refusal keeps that wiring from coming
back silently. `.github/workflows/release.yml` is untouched: its
`check:console-injection --require-stamp` call did not need to change,
so this PR touches no governed surface.
## The card's "Wanted", item by item
1. **"the assertion takes its witness from the spec at objectui's
`.objectstack-sha`"**: done. `--vendored` is objectui's own build of
`2c9bf9430a11`, and the assertion refuses any other reference on such a
pin.
2. **"so that the stamp carries a non-null witness"**: done. The stamp
is shown before and after below.
3. **"A bundle that does not carry this tree's spec fails both release
paths again"**: done. The build path exits 1 and the replay exits 1 on a
bundle carrying objectui's own pinned spec. The old code exits 0 on
both. The matrix is below.
## Measurements
All readings are from objectstack `a5a781bc7` (BASE) and this branch, at
`.objectui-sha` `4016370af3c9`, where objectui's `.objectstack-sha` is
`2c9bf9430a11` (spec 18.0.0-next.3). This tree's spec is 18.0.0-next.5.
The objectui source was the sibling checkout, `build-console.sh` mode 2.
**Stamp, real build (`bash scripts/build-console.sh` under
`os-verify-lock`):**
| | skew | freshWitness | staleDetector | vendoredPin | replay
`--require-stamp` |
|---|---|---|---|---|---|
| BASE `a5a781bc7` (VERDICT 0, 862 s) | `false` | `null` | `null` |
absent | exit 0, "Nothing assertable in this stamp" |
| this branch `91dc1bab6` (VERDICT 0, 672 s) | `true` | "Error code the
write was refused or failed with, as the single-record write door
answers it" | "Binds the element to one same-origin API endpoint instead
of an object" | `2c9bf9430a11…` | exit 0, "the dist carries this tree's
copy" |
Build-time verdict on this branch: "Console bundle carries THIS tree's
@objectstack/spec, and only it (vendored: @objectstack/spec of
objectui's own build of objectstack 2c9bf94)". 16 of 16
injected-only descriptions are present and all 2 pin-only ones are
absent. This is the same count os-dev-report 6263864885 measured by
hand.
**Reference build cost**, from the pin script's own step timings on this
shared box: fetch 14.8 s, install 5.0 s, build of the 7 packages 144.7
s, pack 16.5 s, so about 181 s. The rerun in the same tree skipped it:
the log has 0 "Building objectui's own" lines, against 1 on the first
run.
**The negative proof (hypothesis 5).** The red bundle is a scratch
`build-console.sh` run with both halves deliberately broken:
`OBJECTUI_DEPS_BUILD_CMD` puts objectui's own pin build back into its
install after the stand-in, and `OBJECTUI_BUILD_CMD` drops
`OBJECTSTACK_SPEC_DIST` and `OBJECTSTACK_CLIENT_DIST`. That is what
ships if pack and inject both regress. The run ended with build-time
exit 1: "✗ Built console still carries the @objectstack/spec of
objectui's own build of objectstack 2c9bf94 … (1 of 2 pin-only
descriptions) … Binds the element to one same-origin API endpoint
instead of an object". Lock VERDICT command-exit 1. The `import/jobs`
canary passed on that same bundle.
A first sabotage aimed only `OBJECTSTACK_SPEC_DIST` at the pinned spec
while the install stayed this tree. It never produced a bundle:
rolldown's MISSING_EXPORT error said this tree's `@objectstack/lint`
needs `virtualFieldAggregateRemedy`, which the pinned spec lacks. So the
coherent leak is the whole pin, and that is the bundle used here.
Then every leg was re-run at the final head, against the saved bundles.
These are direct node runs, assertion blob `26acfe988c3b`, BASE blob
`4fd59042a9eb`:
| leg | bundle | `--vendored` | exit | stamp |
|---|---|---|---|---|
| new assertion | this tree's | pin reference | **0** | skew true,
witnesses above |
| new assertion | objectui's own pin | pin reference | **1** | none
written |
| BASE assertion | this tree's | stand-in (BASE's wiring) | 0 | skew
false, null, null |
| BASE assertion | objectui's own pin | stand-in (BASE's wiring) |
**0**, the vacuity | skew false, null, null |
| new assertion | objectui's own pin | stand-in | **2**, refused | none
written |
| replay, new stamp | objectui's own pin | n/a | **1**, "carries the
@objectstack/spec of objectui's own build of objectstack 2c9bf94" |
|
| replay, BASE stamp | objectui's own pin | n/a | **0**, the vacuity | |
| replay, new stamp | this tree's | n/a | **0** | |
## PM mechanism hypotheses
1. **Confirmed**, by the BASE build above. `--vendored` resolved to this
tree's packed spec, and the stamp and replay matched the card exactly.
2. **Route: objectui's own pin build, kept before the stand-in.**
objectui's cache is replaced by the stand-in, and on a CI runner it is
empty before the build anyway. objectui's machine store is not read
under `OBJECTSTACK_ROOT`, and the pin script at this pin has no
machine-store code at all: its 2 "machine" hits are about npm network
settings, against 7 hits in objectui#12972's `f9401e6`. `git show` of
this repo's history gives source, while the probes read built
`.describe()` text. An npm version is exact only when the pin is a
version commit, and objectui's `bump-objectstack.sh` pins any main HEAD.
The reference stays a built spec, needs no secret, and fetches
objectstack from GitHub like the pin script does by default
(`OBJECTSTACK_REPO_URL` overrides it). The cache-HIT replay still reads
only the stamp, which now carries real probes. On a HIT nothing is
built, and the replay's expiry check still reads only this tree's built
spec.
3. **Rule: "no skew" is a pass only against a reference that is not this
tree.** On a `.objectstack-sha` pin, that reference is objectui's own
build of the pinned commit, proven by its pin stamp. When that build
declares the same descriptions as this tree, the bundle carries the same
text either way, so it stays an honest exit 0. The stamp then records
`vendoredPin`, and the replay prints "no observable skew between the
injected and objectui's own build of objectstack SHA". The vacuous case
can no longer be produced: a reference that is the stand-in or the
install is refused with exit 2 before the bundle is read. Battery 15
pins both.
4. **Out of scope, noted.** The `import/jobs` client canary passed on
the pin-native bundle too, so it does not tell an injected client from
objectui's own. That is measured above and agrees with 6263864885. No
client witness was added: per 6263864885, the two trees' client differs
only in a type signature and comments, so a witness would have nothing
to tell apart today. See the Acceptance notes.
5. **Done**: red 1 and green 0 on the build path, red 1 and green 0 on
replay, against BASE's 0 and 0 on the same bundles. See the matrix.
## Route choice on the four axes
- **Real business need.** The spec to judge against is the one a broken
pack-or-inject would actually ship. Measured above, that is objectui's
own build of its pin. An npm version or a source checkout would be a
different spec.
- **Long-term soundness.** It reuses objectui's own build recipe and its
stamp, so no copy of that build logic lives here. It follows the pin
automatically as `.objectstack-sha` moves. The cost is about 3 minutes
per console build that misses its cache.
- **Keeping AI from writing it wrong.** The assertion refuses the
vacuous wiring loudly (exit 2, naming the pin). No version-mapping
heuristic is involved that could quietly pick the wrong reference.
- **No requirement sprawl.** No new gate, no required-check change, no
workflow change, no release act. The existing assertion stops being
vacuous.
## Tests and gates
The gate union was run after the final commit, at `14d4124e8`.
`dispatch-gates.mjs --commands` derived 86 families. 84 exited 0 on the
first pass. Two answered exit 3, PREREQUISITE NOT MET, which is not a
measurement:
- `check-plugin-teardown-shape --self-test` could not reach its fixture
commit in a shallow clone. After `git fetch --unshallow origin main` it
exited 0.
- `check:dual-build-cjs-loads` needs every package's dist. After `turbo
run build --filter=!@objectstack/docs --concurrency=2` (76 of 76, 75
cached, VERDICT 0) it exited 0.
`dispatch-gates.mjs --ran` then reported "86 derived, 86 run, 0
NOT-MEASURED, 0 UNRUN" (exit 0). This PR's check-runs belong to CI and
were not re-run here.
- `node scripts/check-console-injection.mjs --self-test`: exit 0, "84
assertions over real fixture trees".
- Ablation, in a scratch tree, so the worktree was never mutated.
Battery 15 with BASE's assertion swapped in, blob verified equal to the
BASE blob: exit 1, 10 failures. The stand-in leg reads "expected 2, got
0". Restored, blob equal to HEAD: exit 0.
- `pnpm --filter @objectstack/spec exec vitest run --project repo
--maxWorkers=2 scripts/build-console-objectstack-root.test.ts`: 6 of 6
pass. The same file at `24e7c5cab`, before its update, failed 2 of 5.
Ablation through `scripts/ablation-replace.mjs`: the reference guard was
replaced by `if false`, the anchor went 1 → 0 and the blob changed, and
3 of 6 failed. The restore was proven: blob equal to HEAD, `git diff
HEAD` empty. Re-run after the restore: 6 of 6.
- `pnpm --filter @objectstack/spec typecheck`: VERDICT 0. `tsc -p
tsconfig.scripts.json --listFiles` includes the edited test.
- `check:console-injection` exit 0 and `check:console-sha` exit 0 on the
real dist.
## Acceptance notes
- Every console build that misses its dist cache on a `.objectstack-sha`
pin now pays the reference build once, about 181 s on this box. Both
workflows hash `scripts/build-console.sh` into their dist-cache key, so
every entry stamped by the old script stops being restored. The first
run of each workflow after this lands rebuilds cold.
- The reference build fetches objectstack at the pinned commit from
GitHub, the host the build already clones objectui from. It refuses
loudly on any pin-script refusal, since `build-console.sh` runs under
`set -e`.
- Client canary (hypothesis 4): it is non-discriminating on this branch,
as measured above. The reference cache now also holds the pin's own
`@objectstack/client`, which is where a derived client witness would
come from if one is ever wanted. Owner: none. Not filed: this is an
observation with nothing to witness today.
- The replay's pass line still says "not the published one" on a pinned
stamp. That is wording only, and it was left as is.
- Changed lines: 227 (204 added, 23 removed). The three planned scripts
account for 191. The other 36 are the existing branch test, which this
diff could not leave red.
- Local scope: the derived gate families and the files above. The spec
package's `local` vitest project, whole `test:repo`, `pnpm lint` and the
rest of the farm belong to CI.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 04669a4 commit ba4afcc
4 files changed
Lines changed: 204 additions & 23 deletions
File tree
- packages/spec/scripts
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
26 | 31 | | |
27 | 32 | | |
28 | 33 | | |
| |||
60 | 65 | | |
61 | 66 | | |
62 | 67 | | |
63 | | - | |
| 68 | + | |
| 69 | + | |
64 | 70 | | |
65 | | - | |
66 | | - | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
67 | 79 | | |
68 | 80 | | |
69 | 81 | | |
| |||
158 | 170 | | |
159 | 171 | | |
160 | 172 | | |
| 173 | + | |
| 174 | + | |
161 | 175 | | |
162 | 176 | | |
163 | 177 | | |
| |||
181 | 195 | | |
182 | 196 | | |
183 | 197 | | |
184 | | - | |
| 198 | + | |
| 199 | + | |
185 | 200 | | |
186 | 201 | | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
187 | 211 | | |
188 | 212 | | |
189 | 213 | | |
| 214 | + | |
190 | 215 | | |
| 216 | + | |
191 | 217 | | |
192 | 218 | | |
193 | 219 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
20 | 22 | | |
21 | 23 | | |
22 | 24 | | |
| |||
73 | 75 | | |
74 | 76 | | |
75 | 77 | | |
76 | | - | |
| 78 | + | |
77 | 79 | | |
| 80 | + | |
78 | 81 | | |
79 | 82 | | |
80 | 83 | | |
| |||
107 | 110 | | |
108 | 111 | | |
109 | 112 | | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
110 | 157 | | |
111 | 158 | | |
112 | 159 | | |
| |||
162 | 209 | | |
163 | 210 | | |
164 | 211 | | |
| 212 | + | |
| 213 | + | |
165 | 214 | | |
166 | 215 | | |
167 | 216 | | |
| |||
175 | 224 | | |
176 | 225 | | |
177 | 226 | | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
178 | 230 | | |
179 | | - | |
| 231 | + | |
180 | 232 | | |
181 | 233 | | |
182 | 234 | | |
| |||
193 | 245 | | |
194 | 246 | | |
195 | 247 | | |
196 | | - | |
| 248 | + | |
197 | 249 | | |
198 | 250 | | |
199 | 251 | | |
| |||
204 | 256 | | |
205 | 257 | | |
206 | 258 | | |
207 | | - | |
| 259 | + | |
208 | 260 | | |
209 | 261 | | |
210 | 262 | | |
211 | 263 | | |
212 | 264 | | |
213 | | - | |
214 | | - | |
215 | | - | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
216 | 268 | | |
217 | 269 | | |
218 | 270 | | |
219 | | - | |
| 271 | + | |
220 | 272 | | |
221 | 273 | | |
222 | 274 | | |
| |||
241 | 293 | | |
242 | 294 | | |
243 | 295 | | |
244 | | - | |
| 296 | + | |
245 | 297 | | |
246 | 298 | | |
247 | 299 | | |
248 | 300 | | |
249 | 301 | | |
250 | 302 | | |
251 | | - | |
| 303 | + | |
252 | 304 | | |
253 | | - | |
| 305 | + | |
254 | 306 | | |
255 | 307 | | |
256 | 308 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
338 | 338 | | |
339 | 339 | | |
340 | 340 | | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
341 | 345 | | |
342 | 346 | | |
343 | 347 | | |
| |||
383 | 387 | | |
384 | 388 | | |
385 | 389 | | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
386 | 413 | | |
387 | 414 | | |
388 | 415 | | |
| |||
484 | 511 | | |
485 | 512 | | |
486 | 513 | | |
487 | | - | |
488 | | - | |
| 514 | + | |
| 515 | + | |
489 | 516 | | |
490 | 517 | | |
491 | | - | |
| 518 | + | |
492 | 519 | | |
493 | 520 | | |
494 | 521 | | |
| |||
0 commit comments