Skip to content

docs(qa): checklist item for public-form withdrawal layering (17.7 security follow-up) - #21964

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21932-checklist-public-form-layering
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21932-checklist-public-form-layering

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21932

Clause-②: no

What

This PR adds one checklist item, access-security.public-form-withdrawal-layers, to docs/qa/platform-checklist/areas/access-security.json. It sits right after access-security.public-form-intake. Its fields are rev 1, since: v17.7, P1, surface api. No other file changes.

This delivers the last two rows of #21932. The first five rows and both re-checks landed in PR #21943.

The #21835 / PR #21864 row: public-form withdrawal layering

The item is written against what PR #21864 landed on main. Its merge, 3c7785d4ab, is an ancestor of this branch's base 01e0f71a. Each rule on the card maps to a clause:

Card rule Where in the item Oracle Code anchor
An env-wide withdrawal is not re-opened by an org overlay acceptance[0]: both doors answer 404 FORM_NOT_FOUND and no row lands. acceptance[1]: an org-scoped save that would leave the form open answers 403 NOT_OVERRIDABLE api rest-server.ts#registerFormEndpoints, anonymous-form-intake.ts#anonymousFormIntakeWithdrawnIn, protocol.ts#anonymousFormIntakeReopenRefusal
Only an explicit false withdraws acceptance[2]: with an absent allowAnonymous, or no publicLink, env-wide, the org save that opens the form is accepted and both doors serve it api anonymous-form-intake.ts#anonymousFormExplicitWithdrawals. Premise: protocol.ts#projectStorableViewBody
A package's shipped false withdraws acceptance[4] test anonymousFormExplicitWithdrawals. Pins: protocol.org-scoped-write-refused.test.ts ('single: a package-shipped form') and anonymous-form-intake.test.ts
The env-wide definition may open a package-closed form acceptance[5] test protocol.ts#envWideRawViewRows, with the same protocol pin
The ruled known limit is recorded as a known gap fixtures.knownGaps[0], plus a negative saying it is not a FAIL none The doors match by served item name. The save check runs only from saveMetaItem and the draft promotion, never from rollbackMetaItem or revertCommit

acceptance[3] is the control pair, which the dogfood also pins:

  • An organization can always withdraw the form for itself.
  • A form open at both layers is served, and its row lands in the organization.

automated.ref leads with packages/qa/dogfood/test/showcase-public-form-withdrawal-layers.dogfood.test.ts. That dogfood covers acceptance[0], [1] and [3] end to end. The ref also names the rest, metadata-protocol and metadata-core unit pins.

The steps drive the stock showcase form, showcase_inquiry.contact at /forms/contact-us. The admin saves it at two scopes: env-wide, and in the Default Organization the doors read. Both doors are probed anonymously.

Where the code is narrower than the card's wording

Where they differ, the item follows the code:

  • A package's shipped false. This holds only for an artifact the stack schema parsed (strict defineStack, the default). There the schema default enabled: false counts as an explicit false. An artifact loaded unparsed (strict: false, or a hand-built manifest) is judged as written, so a switch it omits is absent and withdraws nothing. acceptance[4] says so.
  • Only an explicit false. The false must sit on a sharing that keeps a non-empty publicLink. A sharing with no link withdraws nothing, even with both switches false. acceptance[2] says so.
  • A second documented limit. main carries "Known limit: packages and names" besides the ruled one. Cases where two packages ship the same view name are outside this item's fixture. The item points at that docs section as it reads at the run's commit, rather than restating it.

The #21867 / PR #21928 row

Confirmed on main with no change. The item is automation.paused-run-trigger-record-masked in docs/qa/platform-checklist/areas/automation.json, at rev 1, status: active. Its automated.ref is packages/qa/dogfood/test/flow-trigger-record-credential-mask.dogfood.test.ts, which is on disk. PR #21928 merged as 1f0469655f, an ancestor of this branch's base.

Overlap with #21934

#21934 is not addressed here. Its PR #21962 was an open, unmerged draft when this PR was opened, so the item is written against main as it stands.

Tests

All results are at head 2d51effa.

Acceptance notes

  • coverage.json is untouched. The claim's file surface is areas/*.json, and the view kind is already mapped. Mapping the new item to view is optional, and is left to whoever next owns coverage.json.
  • A stale clause in the sibling item. access-security.public-form-intake clause 7 says "republishing restores service" but does not name the scope of the republish. With layering, republishing in an organization over an env-wide withdrawal is refused with a 403. The new item covers that case. The old item is unchanged, with no revision bump, to keep this PR to the card's rows.
  • No changeset. The diff touches only docs/qa/platform-checklist/areas/access-security.json, which no published package ships: the root package is private, and no package files entry names docs/qa. skip-changeset applies.

Generated by Claude Code

claude added 2 commits October 6, 2026 06:45
access-security.public-form-withdrawal-layers asserts how a public form's
withdrawal composes across metadata layers: an organization overlay cannot
re-open a form the env-wide definition withdrew (both anonymous doors and
the org-scoped write door), only an explicit false withdraws, a package's
schema-parsed false withdraws, and the env-wide definition may open a
package-closed form. The ruled known limit (name-anchored doors; overlays
stored before the withdrawal or restored by rollback or revert) is
recorded as a knownGap, not a clause.

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/m label Oct 6, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 6, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 07:15
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 07:15
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 2a22177 Oct 6, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21932-checklist-public-form-layering branch October 6, 2026 07:40
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
… of objectui#10202 (objectstack-ai#21969)

Fixes objectstack-ai#21957

Clause-②: no

## What changes

`docs/qa/platform-checklist/areas/studio-authoring.json` gains one item,
`studio-authoring.picklist-bound-select`: rev 1, `since: v17.7`, P2,
surface `mixed`. It is appended after
`studio-authoring.doc-on-app-menu`. It is part (d) of the director
ruling 6006062645 on objectui#10202 (letter A′). No other item changes,
no item index moves, and no other file is touched.

## Changes from the card's draft, and why

1. **`since` is `v17.7`, not `v17.6`.**
- 17.6.0 has already shipped: the `@objectstack/*@17.6.0` tags point at
`617f25f8` (2026-10-02).
- objectui PR 11669 merged later, on 2026-10-06, as objectui `c3623eb1`.
- No 17.7 tag exists yet. Of the 315 pending changesets, 146 declare a
minor bump and none a major, so the next release is 17.7.
- This week's sibling item
`access-security.public-form-withdrawal-layers` (PR objectstack-ai#21964) also sets
`v17.7`.
- One condition: the feature ships in 17.7 only if `.objectui-sha` moves
past `c3623eb1` before the 17.7 cut. See "The console pin" below.
2. **The draft's third clause is split in two.** Its `"oracle": "mixed"`
is not in the gate's oracle set (`api`, `network`, `screenshot`, `dom`,
`log`, `test`, `build`). The draft's words are kept in both halves:
- the picker half is `dom`: picker options against `GET /meta/picklist`;
   - the PUT half is `api`: the captured PUT body and status.
3. **The picklist fixture line now spells the recipe**, so a runner can
write it from the item's text alone:
- the file to edit, `examples/app-showcase/objectstack.config.ts`, and
the `defineStack` keys `picklists` and `picklistExtensions`;
   - `definePicklist`, exported from `@objectstack/spec`;
- the extension's one value must be new: a repeated value is refused at
load with `INVALID_METADATA`;
- the extension may sit in the list's own package.
`packages/objectql/src/engine-picklist.test.ts` pins this: its
nested-plugin case merges an extension registered under the list's own
package;
- boot with `--compile`. Otherwise `os dev` serves an existing
`dist/objectstack.json`; it warns that the artifact is stale, but it
boots.

   The fixture stays uncommitted, and nothing is added to `examples/`.
4. **The console fixture line names the merge it must carry**: objectui
PR 11669, merged as `c3623eb1`. A runner can then check whether the
console under test carries it.
5. **The spec citations are now anchors the gate resolves** (README,
"Write the symbol as an ANCHOR"):
- `PicklistSchema`, `PicklistExtensionSchema`,
`PicklistServedFieldSchema` and `definePicklist`;
   - `FieldSchema.picklist`;
   - the stack keys `picklists` and `picklistExtensions`.

This file's anchor census goes from 29 to 36, and all of them resolve.
The producer-obligation citation now also names objectui#10202, where
comment 5925784898 lives.
6. The history entry carries `"ref": "objectstack-ai#21957"` and lists these changes.

## The console pin

On `main` (`80f9f7e6`), `.objectui-sha` is `0abd4f9f`. That pin does
**not** contain `c3623eb1`, the merge of objectui PR 11669. The reading
was taken in a shallow objectui checkout:

- `git merge-base --is-ancestor c3623eb1 0abd4f9f` exits 1;
- the control leg, `0abd4f9f~30` (`b65aa5e`) against `0abd4f9f`, exits
0;
- the reverse also exits 0: `0abd4f9f` is an ancestor of `c3623eb1`, 21
commits behind it.

So the console fixture line is load-bearing until the pin moves. That
line asks for `pnpm objectui:refresh` from an objectui at or after the
merge.

## Verification (at `eed17a88b`)

`dispatch-gates.mjs --commands` derives 13 gates for this diff, and all
13 were run. Exit codes were captured before any pipe. `--ran`
reconciles the list as 13 derived, 13 run, 0 NOT-MEASURED (a derived
zero).

| gate | exit | verdict |
|---|---|---|
| `pnpm check:platform-checklist` | 0 | OK: 15 areas, 275 items (271
active, 2 planned); symbol anchors 683/693 resolved (676/686 on the
base) |
| `node scripts/check-ci-filter-parity.mjs` | 0 | OK |
| `node scripts/check-closing-keyword-parity.mjs` | 0 | OK: 3 parsers
agree on all 9 keywords |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | 40
assertions |
| `node scripts/check-comment-mask-corpus.mjs` | 0 | 8297 files, 0
disagree |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 | clean (see below) |
| `pnpm check:cross-package-test-inputs` | 0 | OK |
| `pnpm check:doc-authoring` | 0 | clean |
| `pnpm check:driver-memory-census` | 0 | OK |
| `pnpm check:gitlink-declared` | 0 | OK |
| `pnpm check:nul-bytes` | 0 | OK: no raw ASCII control bytes |
| `pnpm check:refd-timer-probe` | 0 | OK |
| `pnpm check:watch-hint-literal` | 0 | OK |

- `check:doc-formula-expressions` first exited 3: its prerequisite was
not met, because `@objectstack/formula` and `@objectstack/lint` were not
built. After `turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint`, it was re-run and exited 0.
- `checklist-select.mjs` resolves the item as runnable under both
`studio-authoring.picklist-bound-select` and `since:v17.7`.

The diff is checklist JSON only and changes nothing a published package
ships, so the PR takes `skip-changeset`.

## Acceptance notes

- **Not run yet.** This PR writes the item; no run record exists. The
first run needs a console that carries objectui PR 11669 (see "The
console pin").
- **`coverage.json` is unchanged; the `picklist` kind stays waived.**
  - The checklist's rules do not require a mapping for a new item.
- The waiver also names its own retirement edit: the promotion of the
planned `records-forms.picklist-shared-across-objects`.
- The waiver's premise, "No runtime reads a picklist", no longer holds.
The runtime half (objectstack-ai#19519) has landed: see objectui#10202 comment
5925784898 and `packages/objectql/src/picklist-resolution.ts`.
- So the next sweep's waiver re-audit should rewrite or retire it.
Carrier: none.
- **The showcase has the same stale premise.** Its own coverage waivers
for `picklist` and `picklistExtensions` in
`examples/app-showcase/src/coverage.ts` say "declared ahead of its
runtime reader". Carrier: none.
- **Scope held.** The item's steps go only through the two designers
objectui#10202 repaired: Setup → Metadata → Object, and the Studio data
page. They never go through Studio → Access → OWD overview, a writer
known to fail that is filed on the objectui side.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ce row no longer displaces a code-defined datasource at boot, and the metadata door refuses edits to the host default (objectstack-ai#21965)

Part of objectstack-ai#21922
Fixes objectstack-ai#21944
Clause-②: no (narrowing)

## What changes

A code-defined datasource (a `*.datasource.ts` the installed artifact
declares, or the host's own `default`) is read-only by published
contract: `DatasourceSchema.origin` says "code — authored as
`*.datasource.ts`, GitOps-owned, read-only in the UI", the datasource
registry entry in `metadata-plugin.zod.ts` says code-defined datasources
"win on name collision", and `datasource-admin-service.ts` says "A
runtime datasource never shadows a code one (code wins on collision)".
The datasource-admin plugin's boot restore broke all three, and the
metadata door could not see `default` at all.

The fix is the one host-owned set of code datasources the two cards'
triage asked for ("One set serves both, so do not build two"):

- **The set** (`packages/runtime/src/code-datasource-names.ts`, new).
One in-memory `Set` of the datasource names the host registers from
code, on the kernel service `code-datasource-names`.
`contributeCodeDatasourceNames` registers it on first use and adds to it
after that, the shape `seed-summary` uses.
- **Its producers, both in `init()`.** `AppPlugin.init()` adds every
datasource the artifact declares: the same list its `start()` registers
in the MetadataService, now memoized so the two phases read one answer.
`DefaultDatasourcePlugin.init()` adds `default`. Phase 1 completes
before any `start()`, so the set is whole before the restore runs,
whatever order the plugins were composed in.
- **The restore** (`restoreRuntimeDatasources`,
`packages/services/service-datasource/src/datasource-admin-plugin.ts`).
A stored row under a name in the set is not registered over the code
definition. It is kept, and one boot warning names it with the repair.
The warning goes to the host's `options.logger`, or to the kernel logger
when the host passes none (`os serve` passes none).
- **The resolver** (`isDeclaredCodeDatasource`,
`packages/metadata-protocol/src/protocol.ts`, nothing else in that
file). It reads the same set beside the installed packages, so the
metadata door answers `default` the way it answers every code-defined
datasource since PR objectstack-ai#21942.

⛔ "Code" is never read from a stored row's `origin`, the MetadataService
slot's `origin`, the connection service's `ConnectResult`, or a request
body's `origin`.

## Measured on a booted showcase

The harness is the `@objectstack/verify` `bootStack` with the
datasource-admin routes mounted the way `serve.ts` mounts them, in a
temp cwd. The stored rows assert `origin: 'runtime'` and their own
`config.filename` (the cards' case (b)). They were written through the
metadata door's repository on the runtime-only intent, then the stack
restarted. BEFORE is `76fec88b16`; AFTER is this branch at `1d840709ae`.
The readings come from a throwaway probe that was never committed; the
committed pins below assert the AFTER column.

| Reading after the restart | BEFORE | AFTER |
|---|---|---|
| admin list, `showcase_external` | `origin: runtime`, label "Shadow
21922" | `origin: code`, "External Analytics (SQLite)" |
| `PATCH /api/v1/datasources/showcase_external` | 200 | 400
`DATASOURCE_ADMIN_ERROR` "… is code-defined and cannot be edited at
runtime." |
| live pool named `default` | a second pool opened on the stored row's
file; verdict `already-registered` became `connected` | none; verdict
stays `already-registered` |
| `showcase_ext_customer` read | 3 rows (code fixture) | 3 rows (code
fixture) |
| boot warning naming each stored row | none | one per row |
| `PUT /api/v1/meta/datasource/default` | 200 "Saved datasource
'default'" | 403 `NOT_OVERRIDABLE` |
| `DELETE /api/v1/meta/datasource/default`, no stored row | 200 | 403
`NOT_OVERRIDABLE` |
| `DELETE /api/v1/meta/datasource/showcase_external` (repair), then meta
`GET` in the same boot | 200, but the meta `GET` kept serving the stored
edit until the next restart | 200, and the meta `GET` serves the code
definition |

## The dispatch's mechanism hypotheses

- **H1, start order.** In all three compositions that load
`service-datasource` (`serve.ts`, `standalone-stack.ts`, the verify
harness), `DefaultDatasourcePlugin` and `AppPlugin` are `use()`d before
`DatasourceAdminServicePlugin`. None of the three declares an ordering
edge to another, so their `start()`s run in insertion order: the code
registrations did land before the restore, but by list position alone,
which ADR-0116 says proves nothing. The answer is the first branch: the
set is filled by a phase that precedes the restore (`init()`). It is
pinned by a boot whose reader plugin is composed first, ahead of every
producer. The restore pin also covers a code registration that lands
after it.
- **H2, the seam.** It is a kernel service read through the services
registry the protocol already resolves (`getServicesRegistry()`), with
no `packages/spec` change. The ObjectQL registry was rejected: the
engine's datasource definitions mix both origins, and a host package
record would be a fabricated provenance.
- **H3, the admin refusal.** Measured, not assumed. The pins' stored
rows carry `origin: 'runtime'`, and the slot the refusal reads holds
AppPlugin's explicit `origin: 'code'`. Under ablation A the admin door
served the stored row as `origin: runtime`, so the refusal cannot come
from the admin read's `origin ?? 'code'` default.
- **H4, the live pool.** For `default`: yes. The restored row reached
`rehydratePools`, which opened a second pool named `default` on the
row's file. Routing did not move, because the engine never routes to a
driver named `default`; the default driver keeps its natural name. It is
the same defect and the same decision fixes it, pinned by
`getDriverByName('default')` and the connect verdict. For
`showcase_external`, nothing was re-pointed at boot in this composition:
AppPlugin's connect ran first, so the rehydrate answered
`already-registered`. The admin `PATCH` 200 was the open door to a
re-point (an update that changes connectivity rebuilds the pool), and it
is now refused.

## Seam and the Clause-② limb (for the seat)

- **Published exports added: none.** `code-datasource-names.ts` is not
re-exported from `packages/runtime/src/index.ts`. `service-datasource`
and `metadata-protocol` spell the service name privately and read the
value structurally as `has(name)`, the way `'datasource-connection'` is
read today.
- **What the seam does add is one kernel service entry**,
`code-datasource-names`, which two packages read by name. Whether that
is the claim's "service contract" limb is the seat's call. The line
above stays as the claim wrote it, and the changeset grades all three
packages `minor`, which holds under either reading.

## Named gap: the metadata door's read while a stored row exists

`GET /api/v1/meta/datasource/:name` still serves a stored row under a
code-defined name for as long as the row exists. The door reads its
stored overlay first (ADR-0005's read order), whatever the
MetadataService holds. The AFTER boot measured it: the admin door served
the code definition while the meta `GET` served the stored row, for
`showcase_external` and for `default`. So triage's pins "both doors
serve the code definition" and "removes it with no change to what is
served" hold for the admin door. For the metadata door they hold once
the repair `DELETE` has run, in the same boot. That read lives in
`getMetaItem`'s overlay step, outside `isDeclaredCodeDatasource`, and
`protocol.ts` is held by objectstack-ai#21934 in other regions, so it is left to the
seat. `meta-door-code-datasource.dogfood.test.ts` already pins that read
as it is.

objectstack-ai#21922 stays open for that read: this PR is `Part of` it, and the seat
routes the remaining half through triage when it merges.

## Landing beyond the claim's named files

`packages/runtime/src/app-plugin.ts` is the producer of the packages'
half of the set, in the declared `runtime` package. The memo also makes
its residual-owner warning print once instead of once per phase.
`packages/runtime/src/code-datasource-names.ts` is new in the same
package.

## Patch round 1 (head `d77e150701`)

Review `6011282321` on objectstack-ai#21922. The Tests, Ablations and Gates sections
below are round 0's, at `80fbcfdea6`; this section carries the readings
on the current head.

- **The plugin-dev pin (CI red on round 0).** `AppPlugin.init()` now
contributes its datasource names as a function that the host's
code-datasource set resolves at its first read. The set is still
contributed to only in Phase 1, before any `start()`. The resolution is
deferred because the names come from the artifact's `collections`, which
walk `packages[]`. `AppPlugin.init()`'s manifest registration is the one
thing in `init()` allowed to touch `packages[]`, pinned by
`plugin-dev`'s malformed-stack falsifier, which this PR's first head
turned red. The kernel service now holds a `CodeDatasourceNames`, a set
with pending contributions; readers still use `has(name)` only. A
contribution that throws stays pending and rethrows to every reader.
- **The `default` refusal names what defines it**: the host's database
configuration (the database URL the server starts with). It names no
`*.datasource.ts`, because none declares `default`. Every
package-declared datasource's sentence is byte-identical, and `code`,
`status` and the refused set are unchanged (`packaged-base-regime.ts`,
the datasource row's `hostOwned`).
- `const listOf = (` spacing restored in `app-plugin.ts`. Merged
`origin/main` `80f9f7e6ba` as `49421a8fe6`.
- **Ablation D:** the old sentence put back for `default` turned 6 unit
cases and 1 dogfood case red, and was restored by blob.
- **Tests at `d77e150701`** (each `VERDICT command-exit 0`):
  - `service-datasource`: 748 / 748;
  - `metadata-protocol`: 27978 passed, 19 skipped;
  - `runtime` local: 4668 passed, 19 skipped;
  - `plugin-dev`: 86 / 86;
  - the two dogfood files: 11 / 11;
- downstream consumers of `runtime` (cli, client, verify,
http-conformance, cloud-connection): all passed;
  - typechecks for the five packages: green.
- **Gates at `d77e150701`:** `dispatch-gates --commands` derived 72,
reconciled with `--ran` as 72 run and 0 not measured, plus
`check:init-service-contract` and `check:startup-registry-verdict`. All
exit 0.
- **Docs:** the 18 hand-written pages the Docs Drift Check lists were
read page by page. None states anything this PR makes false, so no docs
are edited.

## Tests (head `80fbcfdea6`)

- `pnpm --filter @objectstack/service-datasource exec vitest run
--maxWorkers=2`: 41 files, 748 passed.
- `pnpm --filter @objectstack/metadata-protocol exec vitest run
--maxWorkers=2`: 218 files passed, 3 skipped; 27976 tests passed, 19
skipped.
- `pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2
--project local`: 331 files, 4658 passed, 19 skipped.
- Dogfood, `--project isolated`:
`datasource-restore-code-wins.dogfood.test.ts` (new) and
`meta-door-code-datasource.dogfood.test.ts`, 2 files, 11 passed.
- `typecheck` green for `service-datasource`, `metadata-protocol`,
`runtime` (including its `check:test-typecheck` ledger, held) and
`dogfood`. `tsc --listFiles` counts each touched test file once in its
program.
- Each package's run includes its new pins: 5 in
`datasource-admin-plugin.test.ts` (the restore), 4 in
`code-datasource-names.test.ts` (the set and its phase), and 2 resolver
plus 8 door cases in `protocol.code-defined-datasource-door.test.ts`
(`default`, on both kernel shapes).

## Ablations

Each one was committed first and mutated with
`scripts/ablation-replace.mjs` in wrap mode, under a shell trap. For
subjects resolved through `dist/`, the package was rebuilt and
`ablation-dist-preflight.mjs` proved the marker was present. The restore
leg was rebuilt and proven `--absent`, the blob equalled HEAD, `git diff
HEAD` was empty, and the tree was clean.

- **A, the restore registers over a code name**
(`datasource-admin-plugin.ts`; marker in 2 files of
`service-datasource/dist`). Unit: 3 failed, 16 passed. The slot served
the stored row, the warning was not called, and the order-independent
case registered the row. Dogfood: 2 failed, 3 passed. The admin list
served `showcase_external` as the stored row, and the repair case read
the same.
- **B, the resolver does not know the set** (`protocol.ts`; marker in 2
files of `metadata-protocol/dist`). Unit: 5 failed, 30 passed (the
resolver case, and `PUT` plus no-row `DELETE` of `default` on both
kernels). Dogfood: `PUT /meta/datasource/default` answered 200. The next
case then failed as a cascade, because the row that `PUT` stored made
the seed conflict. The repair `DELETE` and runtime controls stayed
green, as expected.
- **C, AppPlugin's `init()` contribution deleted** (`app-plugin.ts`; the
subject resolves from source). The reader-first boot saw `['default']`,
not `['app_wh', 'default']`. So the set comes from `init()`; the
`start()` registration never fills it.

## Gates (head `80fbcfdea6`)

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 72 commands on the actual change, a
superset of the 52 at dispatch. All 72 ran with exit codes captured
before any pipe. `--ran` printed "72 derived famil(ies) accounted for —
72 run, 0 NOT-MEASURED".

- `check:dual-build-cjs-loads` first answered `PREREQUISITE NOT MET`
(exit 3) because eight packages outside this diff had no `dist/`. After
building them it measured green.
- Two families the derivation does not name were also run, both green:
`check:init-service-contract` ("34 declared / 1 self-provided / 3
without a workspace provider") and `check:startup-registry-verdict`
("none recording a verdict the boot can contradict").
- `check-changeset-no-major`'s level axis needs a PR payload, so CI
reads it.
- Lint is a proven narrowing, not the repo-wide run. `eslint
--no-inline-config --format json` on the 9 touched source and test files
reported 9 files, 0 errors and 0 warnings. `eslint.config.mjs` never
enables type-aware linting (no `parserOptions.project`, no typed rules,
as its own comment states), so this diff cannot move any untouched
file's verdict.

## Acceptance notes

- **The `default` refusal's remedy** names the host's database
configuration (patch round 1).
- **Cluster convergence.** `convergePool` reads a stored row directly
and is unchanged. Its signals come from peer admin writes, and the admin
door now refuses those for code names.
- **The restore's other warnings** (a failed read, a failed register)
still go only to `options.logger`, which `os serve` does not pass. They
are unchanged here.
- **objectstack-ai#21923 remains open.** This diff does not touch
`listDatasourceRecords`, `getDatasourceRecord` or
`persistDatasourceRow`. One interaction: a metadata-door-created
datasource with no `origin` still restores, and is still read as `code`
by the admin door's default.
- **Main drift.** `origin/main` gained objectstack-ai#21956, objectstack-ai#21964 and objectstack-ai#21961 (spec
and docs-qa only) after the round-1 merge. None touches a file here, and
the queue's merged generation is the check.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

qa(checklist): add items for the security fixes landed in the 17.7 pre-release follow-up

2 participants