Repository navigation
Commit 670680e
docs(qa): P0 sweep follow-through — re-point query-contract-matrix, correct the run's checklist findings, extend two pins (#21111)
Fixes #21060
Clause-②: no
This finishes QA run #21056 (the 2026-10-01 P0 release sweep). It
re-points the one stale red row and corrects the run's
checklist-accuracy findings, citing source or a local measurement for
each one. It also brings two pins up to date. No product code changes.
The run's product defects stay on their own cards: #21052, #21057,
#21058, objectstack-ai/objectui#11326 and objectstack-ai/objectui#11327.
## 1. The re-point: `api-backend.query-contract-matrix` (rev 2 → 3)
| where | old | new | evidence |
|---|---|---|---|
| acceptance[5] | scalar `$nin` on POST `/query` answers `400
INVALID_FILTER` in both spellings | POST, both spellings: `400
VALIDATION_FAILED` with a located `fields[]` entry
(`query.where.status.$nin` for the object body, `query.where` for the
AST). GET `$filter`: `400 INVALID_FILTER`. Never a 500, never a widened
200. A GET twin probe is added to step 8 |
`FilterConditionSchema.superRefine(checkFilterConditionComparands)` and
`lowerFoldedTransportValues` (spec). The POST door answers a
`FindDataRequestSchema` failure as `VALIDATION_FAILED` + `fields[]`
(`rest-server.ts`). Local parse of both bodies refused at exactly those
two paths. The POST-door posture is pinned by
`rest-server-repeated-filter-param.test.ts` and
`analytics-filter-refusal-envelope.test.ts` |
| acceptance[2], step 5 | dotted `fields: ['account.name']` counted as
narrowing | refused `400 INVALID_FIELD` by design, with the message
naming `expand` | `protocol.ts#assertProjectionFieldsExist` |
| negative[0] | probe the widening with `{"filter": …}` | `filter` is a
declared transport spelling of `where`, so it is served. The probes are
now a mistyped clause (`400 VALIDATION_FAILED`) and an undeclared key
(`400 INVALID_FIELD`) | `QueryTransportParamsSchema`;
`protocol.ts#assertQueryParamsAreFields`;
`request-schema-gate.conformance.test.ts` |
A residual for the maintainer (not a defect): the same condition answers
`VALIDATION_FAILED` on POST and `INVALID_FILTER` on GET (the #8001
family).
## 2. Checklist-accuracy findings: what changed, old → new
- **`access-security.crud-permission-matrix` (3 → 4).**
- Old: "31 rows / 25 showcase rows / 100 cells (50/50)". New: 47 rows
(41 `showcase_*` + 6 delegate `sys_*`), and the pin's 164 cells (87
allow / 77 deny). The same 9 cells are granted only by the baseline. All
of this was recomputed from `access-matrix.json` with the pin's union
rule.
- `showcase_client_liaison` joins the variants and the seeded-set list.
- acceptance[1] names `405 OBJECT_API_METHOD_NOT_ALLOWED` for `sys_user`
create/delete, for every caller. Source: `sys_user` `apiMethods` is
get/list/update/bulk, and `enforceApiAccess` has no admin bypass.
- Clause 5 is recorded as unobservable on stock. The guest anchor ships
unbound, and the only anonymous create lane is the public form.
- `automated.ref` no longer reads the permission-zoo pin as covering the
dev boot (#21057).
- steps[0] now mints members invite-first. A new step names the
ownership-floor trap on the delegate's edit/delete probes
(`owner_only_writes` / `owner_only_deletes`).
- **`access-security.write-path-guards` (2 → 3).**
- acceptance[5], old: "drops the field for the batch". New: per row.
Only the locked row loses the field, reported as
`results[i].droppedFields`. Source: `updateManyData` calls
`engine.update` per row with its own drop listener.
- **`access-security.rls-both-sides` (4 → 5) and
`access-security.owd-sharing-matrix` (2 → 3), plus `FOLLOW-UPS.md`
§7c.**
- The private handle D11 is disambiguated from §7c's public D11 row (an
unrelated two-factor defect). The text stays non-disclosing.
- Clause numbering is stated: "clause N" here means `acceptance[N]`, and
a run record's A(N+1).
- **`access-security.anonymous-deny-surfaces` (1 → 2).** `automated.ref`
now names all six families the pin drives (see §3).
- **`search` recipe `qa-contributor-bound-member`.**
- Step 1, old: "this works on a stock dev boot". New: invite first. The
undeclared audience posture is `invite_only` (migration entry
`audience-posture-default-invite-only`), and an uninvited sign-up
answers `403 SELF_REGISTRATION_CLOSED`.
- Step 5: point the own-row invoice at a seeded account.
- **`ai.mcp-stdio-fail-closed` (3 → 4).**
- Two personas: contributor for RLS, client_liaison for FLS. The only
`readable: false` grants are client_liaison's, on `public_read_write`
`showcase_project`.
- acceptance[5] no longer expects `details.forbiddenFields` on the wire.
REST's `PERMISSION_DENIED` arm answers `{error, code, object}`.
- steps[0] hosts the transport with `os start`. Measured: `os dev`
printed 15 banner lines to stdout, and `os start` printed 0.
- **`api-backend.packaged-action-disabled-dispatch` (2 → 3).** The MCP
door is drivable live: the stock banner prints `MCP: …/api/v1/mcp`.
- **`platform-core.boot-health` (5 → 6) and
`platform-core.seed-integrity` (2 → 3).**
- The seed reads now name the `Seeds:` banner row. At the default warn
level, a healthy boot prints no SeedLoader line (`seed-summary.ts`).
- The "no ⚠" clause now names the four misauthored flow classes. Stock
boots list their policy-disabled scheduled flows with ⚠
(`printAutomationSummary`).
- **`cli.dev-boot-contract` (2 → 3).**
- steps[4], [6] and [7] write into the app directory they run in, so
they need an unshared one.
- Exit codes: SIGINT ends plain `dev` with the child's code (0).
`--fresh` SIGINT exits 130 (`dev.ts`, `ServeRestartCoordinator`).
- The `🔑 Dev admin` banner reprints on later boots and is not seeding
evidence (`maybeReportExistingSeedAdmin`).
- **`integration-system.datasource-credential-refusal-matrix` (1 → 2).**
- The wizard cannot plant a legacy alias row. Measured: `POST
/api/v1/datasources` with `passwd` answered `400
DATASOURCE_ADMIN_ERROR`.
- The admin door updates by PATCH. Measured: PUT answered 405.
- The admin door carries `hasSecret` / `redactedConfigKeys`.
- The metadata path is `GET /api/v1/meta/datasource/NAME`. Measured:
200.
- `source` gains the mongo composed-branch twin and the nested-options
spelling entry.
- **`platform-core.console-login` (3 → 4).**
- The expiry clause verifies after the server-side sign-out its own step
prescribes.
- `/_console/home` renders the header and the launcher, with no sidebar
by design (objectui `HomeLayout`).
- **`platform-core.builtin-apps-nav-render` (3 → 4).**
- Re-measured on a stock dev boot: Setup 45 and Account 8 destinations
for the admin, and Account 8 for the member. The control reads 8 vs 8;
the eighth entry is `nav_connect_agent`.
- The member is the seeded Mei (`demo-personas.ts`).
- The marketplace catalog's cloud egress dependency is recorded as a
known gap.
- **`records-forms.crud-roundtrip` (7 → 8).**
- The readonly-column half of the clone clause is unobservable on the
stock seed. No readonly column on `showcase_account`, no
`approval_status` anywhere, and `lead_score` is null on every seeded
contact.
- An own-row invoice pointing at the run's account turns the later
delete into `409 DELETE_RESTRICTED`. Cause: a required lookup turns
`set_null` into `restrict` (`engine.ts`).
- **`approvals.account-app-entry` (1 → 2).**
- The `zh-CN` option is 中文(中国), and there is no bare `zh`.
- Tab labels are given in both locales.
- The record-link clause needs a row the persona can read. Unreadable
rows render no link (`RecordCell`, href null).
- The sign-off request must be launched after the persona holds
`finance`.
- Grounding also showed the seeded personas have been loginable since
#9308, so the old reason for provisioning one was wrong. The real reason
is their positions.
- **`studio-authoring.first-run-loop` (2 → 3).** Each point below was
grounded in objectui at the pin:
- The object is `repairs_repair_ticket`, because the dialog prefixes the
package namespace (`prefixObjectName`).
- The New object dialog carries a pre-selected OWD choice.
- The "zero nav items" known gap is superseded: the scaffold checkbox
defaults on.
- steps[8] is a forged request, because authoring is hidden on read-only
packages.
- acceptance[0]'s cited objectui F1 targets the testids
`package-id-input` / `package-name-input`, which no source file declares
at the pin.
- **`RUNNER.md` Environment facts.**
- The CRUD tail now reads `15 verified, 0 gaps, 0 FAILED, 1
needs-fixture, 8 skipped`.
- `verify --rls` probes `10 of 10` positions, and the all-personas line
is unchanged.
- Both were re-measured with `objectstack verify --rls` on this branch.
## 3. The two pins
- **`showcase-anonymous-deny-surfaces.dogfood.test.ts`.**
- Adds anonymous `POST /batch` (empty operation list) and `GET
/security/explain` at 401, each with a member contrast (not 401, not
404).
- Both join the rest-flat envelope classification. 42 → 48 tests.
- **Merge round (`286870908c`):** merged with main's analytics
anonymous-deny change (#21061), keeping both sides. The pin now runs 61
cases (this PR's 6 plus its 13 analytics cases), and all 61 passed on
the rebuilt closure.
- **`showcase-smoke.spec.ts` + `playwright.config.ts`.**
- SURFACES grows from 31 to all 49 destinations `showcase_app` serves.
The 18 added ones are derived from `src/ui/apps/index.ts`, including the
three filter slices spelled the way objectui's nav renderer builds them.
Revenue Pulse and the Hours by Status chart report are chart surfaces.
- The main-content wait now treats the boot splash ("Initializing
application…") as not rendered, and the assertion refuses a surface
still on it.
- The config reads `SMOKE_API_URL` for baseURL and the booted server's
`-p`. `global-setup.ts` already signs in against that URL. It also reads
`OS_TEST_CHROMIUM_EXECUTABLE_PATH` for the browser. Unset, everything
stays on port 3000 exactly as before (CI sets neither).
## Tests (union re-run on `de73aa8444`, after merging `origin/main`)
- `pnpm check:platform-checklist`. Before, on the base `6f578888a8`:
exit 1, with one problem, the pre-existing `identity-auth` `#twoFactor`
row. After, on `de73aa8444`: exit 0, `15 areas, 269 items`. `node
scripts/check-platform-checklist.mjs --self-test`: exit 0 (221
assertions).
- Dogfood pin: `pnpm --filter @objectstack/dogfood exec vitest run
--maxWorkers=2 test/showcase-anonymous-deny-surfaces.dogfood.test.ts`.
Result: `48 passed (48)`, on a rebuilt closure.
- Ablation of the dogfood pin, run against the committed pin through
`scripts/ablation-replace.mjs`. Each mutation was proven on disk, and
each restore was proven by blob == HEAD:
- Unrouting the explain probe turned exactly its 3 cases red (`expected
404 to be 401`, `expected 404 not to be 404`).
- The same mutation on the batch probe also turned 3 red.
- Smoke: `SMOKE_API_URL=http://localhost:43871
OS_TEST_CHROMIUM_EXECUTABLE_PATH=/opt/pw-browsers/chromium pnpm exec
playwright test e2e/showcase-smoke.spec.ts`, against a console built at
the pin (`e420df310f5b`). Result: `49 passed (2.6m)`. The server booted
on 43871 and was down afterwards.
- An instrumented run (one-off and restored) logged 29 of the 49
surfaces on the boot splash first. The old non-chart check would have
passed all 29 on the splash.
- `pnpm --filter @objectstack/dogfood typecheck` and `pnpm --filter
@objectstack/example-showcase typecheck`: exit 0. Both programs include
the edited files.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 55 families, all run. 54 exit 0. NOT MEASURED:
`check:dual-build-cjs-loads`, reason "PREREQUISITE NOT MET": 8 untouched
packages have no `dist/` in this container. `check:type-check-debt` exit
0 on a clean standalone re-run; an earlier run's log was clobbered by a
timed-out batch.
## Card findings NOT acted on, and why
- **`owd-sharing-matrix` step 1 / crud persona recipe ("grant the
position, not the set directly").** The reason is withheld pending the
maintainer (RUNNER rule 2, #21052). It cannot be grounded publicly, so
it is left untouched.
- **Personas, "all member-persona items".** I updated the shared recipe
and the P0 items this run drove. About 28 more active items mention
sign-up personas, and several sit in `identity-auth.json` (open-PR
concurrency). That sweep is left for the PM: an open question, a RUNNER
fact versus a per-item sweep.
- **Patch round (`db092de284`):** the residual items are now covered by
one RUNNER "Environment facts" bullet. It states the stock `invite_only`
posture, the `403 SELF_REGISTRATION_CLOSED` refusal, and the two doors
(invite-member then sign-up, or admin create-user then change-password),
with `search:qa-contributor-bound-member` as the worked example. This
follows the seat's ACCEPT on the open question, option A.
- **`records-forms.crud-roundtrip` "list quick-search text persists
across reloads".** I could not confirm this from objectui source at the
pin, so it is not written as fact.
- **§3 fixture gaps and environment.** These are recorded on the card.
The card says no edit is needed unless chosen, and they lie outside the
claimed RUNNER lines.
## Acceptance notes
- Two RUNNER lines outside the named "Environment facts" lines changed
as a direct consequence of the smoke change. The Playwright fact's test
count went from 31 to 49, and it now names the browser override.
- The `datasource-credential-redaction.ts` docblock still says the
wizard persists through a structural name/label check. A live probe
refuses an alias key 400, so this is comment drift; no carrier found.
- The URL-branch mongo entry's "Deliberately NOT refused … the composed
branch" sentence reads as if the platform did not refuse that branch.
Its twin does, since #9147. This is spec registry text, outside this
ledger.
## Line budget
15 files changed: +375 / −130 against `origin/main`. Breakdown:
- 12 checklist JSON/MD files
- the dogfood pin (+54)
- the smoke spec (+56) and config (+33)
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JAhu8u8QfBvRjVZDox7CP9)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b11d778 commit 670680e
15 files changed
Lines changed: 403 additions & 130 deletions
File tree
- docs/qa/platform-checklist
- areas
- examples/app-showcase
- e2e
- packages/qa/dogfood/test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
280 | 280 | | |
281 | 281 | | |
282 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
283 | 291 | | |
284 | 292 | | |
285 | 293 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
142 | | - | |
143 | | - | |
| 142 | + | |
| 143 | + | |
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
| 149 | + | |
150 | 150 | | |
151 | | - | |
152 | | - | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
153 | 155 | | |
154 | 156 | | |
155 | 157 | | |
| |||
160 | 162 | | |
161 | 163 | | |
162 | 164 | | |
163 | | - | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
164 | 168 | | |
165 | 169 | | |
166 | 170 | | |
| |||
174 | 178 | | |
175 | 179 | | |
176 | 180 | | |
177 | | - | |
| 181 | + | |
| 182 | + | |
178 | 183 | | |
179 | 184 | | |
180 | 185 | | |
181 | 186 | | |
182 | 187 | | |
183 | 188 | | |
184 | 189 | | |
185 | | - | |
| 190 | + | |
186 | 191 | | |
187 | 192 | | |
188 | 193 | | |
| |||
263 | 268 | | |
264 | 269 | | |
265 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
266 | 298 | | |
267 | 299 | | |
268 | 300 | | |
| |||
0 commit comments