Commit bc2ec80
feat(devx): extend AMPLIFIERS to the packages a sibling checkout links (#18749)
Part of #16529 — this lands the objectstack half of the maintainer's
ruling (batch #151 item 1, letter **C**). It is deliberately NOT a
closing reference: the ruling's scope is "cloud's 184-specifier closure
(`plugin-auth`, `core`, `organizations` **and the rest**)", and "the
rest" is not derivable inside this container (see *Declared gap* below).
The card should be closed by hand once that gap is judged, and the cloud
half (the sibling preflight reading the stamp) is a separate cloud card
`Blocked-by:` this one, per the ruling's Execution section.
## What this changes
`scripts/check-dev-prereqs.mjs`'s `AMPLIFIERS` list gains three
packages, and each of their build scripts gains `--stamp` as the last
`&&`-joined step:
| package | path | build script now ends |
|---|---|---|
| `@objectstack/core` | `packages/core` | `tsup && node
../../scripts/check-dts-emitted.mjs && node
../../scripts/check-dev-prereqs.mjs --stamp` |
| `@objectstack/plugin-auth` | `packages/plugins/plugin-auth` | `tsup &&
node ../../../scripts/check-dts-emitted.mjs && node
../../../scripts/check-dev-prereqs.mjs --stamp` |
| `@objectstack/organizations` | `packages/plugins/organizations` |
`tsup --config ../../../tsup.config.ts && node
../../../scripts/check-dts-emitted.mjs && node
../../../scripts/check-dev-prereqs.mjs --stamp` |
Each package was located by the `name` field in its manifest, not by
assuming the ruling's word was a path — `organizations` lives one
directory deeper than the other two, under `packages/plugins/`.
The header gains the second admission criterion (a sibling checkout
links it), the ruling's explicit refusal to make the stamp a cross-repo
contract, and the declared shortfall. No version number, no
stable-location promise, no docs page — the format stays internal dev
tooling shared by sibling checkouts, and when it changes the sibling's
preflight reds once and is fixed in the same breath.
## Why the stamp and not a source SHA
Carried forward from triage at the triage seat's explicit request, so
the next reader does not re-propose it: "stamp the source SHA into the
build output" sounds more precise and answers **where HEAD is** — which
is exactly the half the consumer already does, and exactly the half its
own docblock records as insufficient. A sibling sitting *exactly on the
pin* whose `dist/` was built from an older commit produces the identical
type error, and a HEAD comparison is silent all the way through it. A
more precise answer to the wrong question.
## Where `--stamp` goes, and the proof it is right
`--stamp` goes **after** `check-dts-emitted.mjs`, matching the existing
`packages/spec` entry. The authority is the file's own
`isStampReachableOnlyFromCompleteBuild` work — `stampStepOrderProblem` —
which rejects three lying spellings. This is machine-checked rather than
argued: the coverage gate was driven to **fire on five broken placements
and pass on the shipped one**, on the real manifests, with the mutation
proven on disk each time and restored from `HEAD` under a trap.
Firing leg, mutating `packages/core/package.json` in place (gate exit
**1** on every row, each naming `packages/core` — a package this gate
could not judge at all before this diff):
| injected spelling | gate's diagnosis |
|---|---|
| `tsup ; node …--stamp` | the step before it is joined by `;`, not `&&`
— so the stamp is written even when that step failed |
| `tsup \|\| node …--stamp` | the step before it is joined by `\|\|`,
not `&&` — so the stamp is written even when that step failed |
| `node …--stamp && tsup` | `check-dev-prereqs.mjs --stamp` is not its
LAST step |
| `tsup && node …--stamp && node …check-dts-emitted.mjs` |
`check-dev-prereqs.mjs --stamp` is not its LAST step |
| `tsup && node …check-dts-emitted.mjs` (stamp dropped) |
`check-dev-prereqs.mjs --stamp` does not appear in it at all |
Green leg, on the shipped spelling over a fully built tree (`pnpm
build`, 73/73 tasks successful):
```
✓ 68 package build artifacts present (existence, not freshness).
✓ @objectstack/spec, @objectstack/core, @objectstack/plugin-auth, @objectstack/organizations built from
the sources on disk — the only freshness claim this line makes; everything else above is existence only.
```
The three stamps were written by the real builds, not by hand — from the
build log:
```
@objectstack/core:build: ✓ packages/core/dist/.build-input-hash ← 134aa01ce7f7368e…
@objectstack/plugin-auth:build: ✓ packages/plugins/plugin-auth/dist/.build-input-hash ← 90bd5c9bb3ac8a7d…
@objectstack/organizations:build: ✓ packages/plugins/organizations/dist/.build-input-hash ← e8c1627e65820b37…
```
Every mutation leg restored from `HEAD` under a trap with absolute
paths, verified by blob hash equality and an empty `git diff HEAD`; the
working tree read 0 changed files afterwards.
## The gate now catches the defect the card is about
A source edit in a newly listed package that no build has consumed — the
shape that surfaces in the sibling as a type error naming an import
nobody touched — now reads **stale** instead of passing unexamined:
```
✗ A built package's dist no longer matches its sources — 1 unmet precondition, not a list of problems.
All 68 declared build artifacts are present. They are not all current:
@objectstack/core (packages/core/dist/.build-input-hash)
built from sources hashing 134aa01ce7f7368e…
the sources on disk hash f274704eb5548273…
```
Before this diff that verdict was unreachable for `packages/core`:
freshness is only computed for listed packages.
## Declared gap — "and the rest" is not derivable here
The ruling starts from cloud's 184-specifier link closure. That closure
cannot be read in this container; attaching `objectstack-ai/cloud` to
the session was attempted and refused (no access for this session's
credential). So this PR delivers **exactly the three packages the ruling
names**, and the remainder is reported rather than invented.
⛔ "Every workspace package that emits a `dist/`" was explicitly **not**
substituted for that closure — it is a different set, and listing
packages nobody links buys a build step for no reader.
The shortfall is monotone-safe, and that is the reason it is acceptable
to land short: the coverage error fires only on **listed** packages, so
a shorter list checks less. It cannot false-red and it cannot turn
today's green red.
## Acceptance notes
- **Negative controls, both directions, unchanged.** `--stamp` from an
unlisted package still exits 1 with its own wording (`packages/metadata
is not a declared freshness amplifier…`); a listed package whose build
stops stamping still throws a coverage error (row 5 of the firing
table).
- **No mtime criterion is introduced anywhere** — the family's shared
criterion (content, not mtime) holds. The added entries are judged by
the same `buildInputHash` content digest the one existing entry is.
- **The first segment's protection extends to the new entries for
free.** With `packages/plugins/plugin-auth/dist/index.mjs` moved aside,
`--stamp` from that package exits 1 (`…is not on disk, so this run
emitted nothing for a stamp to vouch for`) instead of stamping over a
dist with no build in it. Restored byte-identical (sha256 equal before
and after).
- **The "two lines" promise re-measured, with one honest correction.**
The mechanism is still two lines per package — path in `AMPLIFIERS`,
`--stamp` at the end of the build — and nothing else is wired anywhere
(no CI step, no turbo entry, no ignore rule needed; `dist/**` is already
a turbo output so the stamp caches and cleans with the artifact it
describes). What the promise does not cover is the header's *stated
admission criterion*: it named exactly one dist, so admitting three more
needed the criterion itself written down. That is a documentation edit,
not a third wiring step.
- **Changeset: measured, not assumed.** `npm pack --dry-run --json` on
`@objectstack/core`: **18** tarball entries with the stamps present,
**16** with them moved out of the packed tree — the delta is exactly
`dist/.build-input-hash` and `dist/.build-input-hash-dts`, which the new
last build step writes, and `dist` is in this package's `files[]`.
Positive control: `dist/index.js` present in both listings. So something
published does move, and a `patch` changeset is owed for the three
packages rather than `skip-changeset`. Nothing is imported, executed or
resolved from the two files; no export moves.
## Not addressed here
`#16529` remains open for the closure gap above. The cloud-side half —
the sibling preflight reading this stamp — is not in this repository and
is not touched by this PR.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent aa910a6 commit bc2ec80
5 files changed
Lines changed: 61 additions & 7 deletions
File tree
- .changeset
- packages
- core
- plugins
- organizations
- plugin-auth
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
205 | 230 | | |
206 | 231 | | |
207 | 232 | | |
| |||
355 | 380 | | |
356 | 381 | | |
357 | 382 | | |
358 | | - | |
359 | | - | |
360 | | - | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
361 | 387 | | |
362 | 388 | | |
363 | 389 | | |
364 | 390 | | |
365 | | - | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
366 | 400 | | |
367 | 401 | | |
368 | 402 | | |
| |||
0 commit comments