Skip to content

Commit 74f7815

Browse files
fix(dogfood): the wall CONTROL cases' packages declare the CRM dependency and their publish lines read the receipt (#24777)
Fixes #24725 Clause-②: no The two CONTROL cases of `packages/qa/dogfood/test/wall-environment-metadata-refusal.dogfood.test.ts` that PR #24724 held off now publish for real and assert it. They are the walled `CONTROL: the platform operator writes at every one of these doors` case (both postures, `isolated` and `group`) and the `single (CONTROL)` case. Before this change, each one's `publish-drafts` was refused with a `200`, and the status-only `ok()` read that refusal as a write. The ruled closure narrowing (PR #24630, `9f12d06770`) is unchanged, as are every save-door and publish rule, every other case in this file (the `/packages` case PR #24724 changed included), and every other dogfood file. This PR changes one test file and nothing else. ## What changes (two cases, one file, +19 / -4) - **Each case's package declares the CRM dependency at create.** `POST /packages` now sends `dependencies: { [CRM_PKG]: '*' }`, where `CRM_PKG` is the file's existing constant for `com.example.crm` (`examples/app-crm`'s `manifest.id`). This covers `com.em.PREFIX.opctl` (walled) and `com.em.sin.a` (`single`). - **Each publish line asserts the receipt in `data`.** The receipt must read `{ success: true, outcome: 'published', publishedCount: 1 }`, with `failed[]` as the assertion message. The form is copied byte for byte from the `/packages` case. It stays inline rather than moving into a helper, so the `/packages` case's lines stay untouched and a failure points at each case's own line. - The view names stay on `crm_lead` (`crm_lead.em_PREFIX_pkgv`, `crm_lead.em_sin_pv`), so no later line changes. `ok()` stays status-only for every other line. ## Evidence Every run used the same command, `pnpm exec vitest run --maxWorkers=2 test/wall-environment-metadata-refusal.dogfood.test.ts` in `packages/qa/dogfood`, under the shared verify lock. - The closure was built by `pnpm turbo run build --filter=@objectstack/dogfood --concurrency=2`: 66/66 tasks, the dogfood gate's own build target. - The file runs both walled postures (`isolated`, `group`) and the `single` control in one run. That is how the gate's `turbo run test --filter=@objectstack/dogfood` executes it, since `OS_TEST_SHARD` only slices whole files. So one run covers every variant the gate runs. - Each instrumented or mutated run went through `scripts/ablation-replace.mjs` in WRAP mode. Each was restored and proven hash-equal to `HEAD`: blob `16c8fd8a2` for R0, `3bfd0b150` for R2 to R4, with `git diff HEAD` empty. | Run | Tree | Result | |---|---|---| | R0 (before, instrumented) | `580fd4ed3` (`origin/main`), with a log of each CONTROL `publish-drafts` receipt | `23 passed`: the false green. All three receipts are `200 {"success":false,"outcome":"refused","publishedCount":0,"failedCount":1,...}`, each with `list-view-object-outside-closure`: `crm_lead.em_iso_pkgv` into `com.em.iso.opctl`, `crm_lead.em_gro_pkgv` into `com.em.gro.opctl`, and `crm_lead.em_sin_pv` into `com.em.sin.a` | | R1 (green) | `cff4ff61e`, this change | `23 passed` | | R2 (after, instrumented) | `cff4ff61e` plus the receipt log | `23 passed`. All three receipts are `200 {"success":true,"outcome":"published","publishedCount":1,"failedCount":0,...}`, for the same three views | | R3 (ablation) | `cff4ff61e` with `dependencies` removed from the two CONTROL packages only (the `/packages` case keeps its own) | `3 failed / 20 passed`. The failures are at the receipt assertion, `:333:10` (isolated and group) and `:378:8` (single). Received `{ success: false, outcome: 'refused', publishedCount: 0 }`. The message is `failed[]`: `view/crm_lead.em_sin_pv failed author-time validation: 1 issue — views[0].object [list-view-object-outside-closure]` (the same for `em_iso_pkgv` and `em_gro_pkgv`) | | R4 (doors after the publish, instrumented) | `cff4ff61e` plus logs of the next doors | `23 passed`. In both walled postures, `duplicate` answers `200` with `copiedCount: 1`, and the copy holds `crm_lead.em_PREFIX_pkgv` as `active`. `DELETE /packages/com.em.PREFIX.opctl` answers `200` with `deletedCount: 1` (the published view) and `registryRemoved: true`, and the operator's `GET` then answers `404`. Under `single`, A's `DELETE /packages/com.em.sin.a` answers `200` with `deletedCount: 1`, and no `sys_metadata` row of the package remains | What the runs establish: - On `main` both CONTROLs measured nothing at the `publish-drafts` door (R0). - With the dependency declared, the operator (walled) and A, a `manage_metadata` holder below the operator, under `single` really publish. A's create with `dependencies` answers `201` (R1, R2). - Each CONTROL can now fail at that door, and names the cause when it does (R3). - Each door after the publish now acts on a package holding a published view, and answers as before (R4). ## Gates (local subset, on `cff4ff61e`) - `tsc --noEmit` in `packages/qa/dogfood`: `TSC_EXIT=0`, and `--listFiles` includes this file. - `node scripts/pm/dispatch-gates.mjs --commands` derived 55 families. 54 exit 0. `pnpm check:dual-build-cjs-loads` exits 3 with PREREQUISITE NOT MET: it reads every package's `dist`, and eight packages outside the dogfood closure are unbuilt locally. That family is NOT MEASURED locally, and CI's `Lint & Repo Gates` reads it. `--ran` reconciliation: `55 derived famil(ies) accounted for — 54 run, 1 NOT-MEASURED`, `0 UNRUN`. - `pnpm lint` (`eslint . --no-inline-config`): `LINT_EXIT=0`, no finding. - Dogfood is not in the PR or queue fast set. It runs on `main`'s hourly full battery, and the runs above are its pre-merge reading. ## Changeset None. `skip-changeset` applies: the diff is one file under `packages/qa/dogfood/test/`, and `@objectstack/dogfood` is `"private": true`, so it publishes nothing. ## Acceptance notes - `ok()` still reads only the status for every other line of these two cases. R4 read the bodies of `duplicate` and `DELETE` in addition, and both carry a success receipt. No other door's body was read here. - Under `single`, A's `GET /packages/com.em.sin.a` after the delete answers `403`, not `404`. That is the read gate as written: package reads need `studio.access` or `setup.access` (`packages/runtime/src/domains/packages.ts`), which A does not hold. It is not a line of this file. --- _Generated by [Claude Code](https://claude.ai/code/session_01LmjU2x1faLZ39DVTFyVca6)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 795ff70 commit 74f7815

1 file changed

Lines changed: 19 additions & 4 deletions

File tree

‎packages/qa/dogfood/test/wall-environment-metadata-refusal.dogfood.test.ts‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,18 @@ for (const posture of ['isolated', 'group'] as const) {
319319
await ok(await h.stack.apiAs(h.operator, 'DELETE', `/meta/view/${name}`));
320320

321321
const pkg = `com.em.${p}.opctl`;
322-
await ok(await h.stack.apiAs(h.operator, 'POST', '/packages', { id: pkg, name: 'opctl', version: '1.0.0', type: 'app' }), 201);
322+
// As in the /packages case: the package declares the CRM package, whose
323+
// object the view binds, and the publish line reads the receipt, because
324+
// a refused publish also answers 200.
325+
await ok(await h.stack.apiAs(h.operator, 'POST', '/packages',
326+
{ id: pkg, name: 'opctl', version: '1.0.0', type: 'app', dependencies: { [CRM_PKG]: '*' } }), 201);
323327
await ok(await h.stack.apiAs(h.operator, 'PATCH', `/packages/${pkg}`, { description: 'by the operator' }));
324328
await ok(await h.stack.apiAs(h.operator, 'PUT', `/meta/view/crm_lead.em_${p}_pkgv?package=${pkg}&mode=draft`, view(`crm_lead.em_${p}_pkgv`, 'v1')));
325-
await ok(await h.stack.apiAs(h.operator, 'POST', `/packages/${pkg}/publish-drafts`, {}));
329+
const published = await h.stack.apiAs(h.operator, 'POST', `/packages/${pkg}/publish-drafts`, {});
330+
await ok(published);
331+
const receipt = ((await published.json()) as { data?: Record<string, unknown> }).data;
332+
expect({ success: receipt?.success, outcome: receipt?.outcome, publishedCount: receipt?.publishedCount }, JSON.stringify(receipt?.failed))
333+
.toEqual({ success: true, outcome: 'published', publishedCount: 1 });
326334
await ok(await h.stack.apiAs(h.operator, 'POST', `/packages/${pkg}/duplicate`, { targetPackageId: `com.em.${p}.opctl.copy` }));
327335
await ok(await h.stack.apiAs(h.operator, 'DELETE', `/packages/${pkg}`));
328336

@@ -358,9 +366,16 @@ describe('[ADR-0131 D12 items 7–8 · D11] single (CONTROL): the refusal is ine
358366
await ok(await h.stack.apiAs(h.tokA, 'PUT', '/meta/view/crm_lead.em_sin_view', view('crm_lead.em_sin_view', 'View by A')));
359367
await ok(await h.stack.apiAs(h.tokA, 'PUT', '/meta/flow/em_sin_flow', flow('em_sin_flow', 'Flow by A')));
360368
expect(await readAsB(h, 'view', 'crm_lead.em_sin_view')).toEqual({ status: 200, label: 'View by A' });
361-
await ok(await h.stack.apiAs(h.tokA, 'POST', '/packages', { id: 'com.em.sin.a', name: 'a', version: '1.0.0', type: 'app' }), 201);
369+
// As in the walled /packages case: the package declares the CRM package, and
370+
// the publish line reads the receipt.
371+
await ok(await h.stack.apiAs(h.tokA, 'POST', '/packages',
372+
{ id: 'com.em.sin.a', name: 'a', version: '1.0.0', type: 'app', dependencies: { [CRM_PKG]: '*' } }), 201);
362373
await ok(await h.stack.apiAs(h.tokA, 'PUT', '/meta/view/crm_lead.em_sin_pv?package=com.em.sin.a&mode=draft', view('crm_lead.em_sin_pv', 'v1')));
363-
await ok(await h.stack.apiAs(h.tokA, 'POST', '/packages/com.em.sin.a/publish-drafts', {}));
374+
const published = await h.stack.apiAs(h.tokA, 'POST', '/packages/com.em.sin.a/publish-drafts', {});
375+
await ok(published);
376+
const receipt = ((await published.json()) as { data?: Record<string, unknown> }).data;
377+
expect({ success: receipt?.success, outcome: receipt?.outcome, publishedCount: receipt?.publishedCount }, JSON.stringify(receipt?.failed))
378+
.toEqual({ success: true, outcome: 'published', publishedCount: 1 });
364379
await ok(await h.stack.apiAs(h.tokA, 'DELETE', '/packages/com.em.sin.a'));
365380
await ok(await h.stack.apiAs(h.tokA, 'POST', INSTALL_LOCAL, packageBody('com.em.sin.il', '1.0.0', [object('em_sin_ilobj', 'v1')])));
366381
await ok(await h.stack.apiAs(h.tokA, 'DELETE', `${INSTALL_LOCAL}/com.em.sin.il`));

0 commit comments

Comments
 (0)