Repository navigation
Commit 9ffe8af
docs(qa): re-point the half-B platform-checklist items that described an older product (#24141)
Part of #24036
Clause-②: no
## Summary
Half B of #24036: revises the eight half-B area files of the platform
checklist (dashboards, i18n, identity-auth, integration-system,
platform-core, records-forms, studio-authoring, ux-conventions) against
current `main`. Each item gets a `revision` bump and a `history` row
naming #24036. Docs only: no product code, no published package touched.
`node scripts/check-platform-checklist.mjs` is green.
Each revised item whose edit changed a fixture, a step's request or an
oracle was re-run once, on an app built from this tree. 26 re-runs were
filed as `qa-run` records before the PM ruling. Every later re-run is
evidenced below instead, per the maintainer ruling relayed by the PM:
「或者这种测试只有有问题才立卡」 (file a card only when there is a problem). A re-run
that showed a stale clause was fixed in this diff. Nothing is loosened
past its ruling: clauses score codes, statuses and data, never wording.
## What changed, per area and item
### dashboards (4 items)
- `dashboards.chart-type-matrix` → r5: fixtures.requires[2], steps[9]
and acceptance[7].verify re-pointed so the compareTo probe can render
and discriminate. F1: as written the probe declared no dateRange, and
the dataset query refused it with a…
- `dashboards.dataset-report-authoring` → r2: acceptance[5] (clause,
verify, evidence) and steps[6] made computable.
- `dashboards.system-overview-live-counts` → r3: steps[2] and
acceptance[1].verify gain the discriminating fixture. On a fresh boot no
sys_session row is revoked or expired, so the live-session predicate and
the table count were both 34 and clause 1 could…
- `dashboards.cube-query` → r4: acceptance[0].verify and source[3]
re-pointed to the meta wire shape. GET /api/v1/analytics/meta answers
`data` as a BARE array of CubeMeta projections —
AnalyticsMetadataResponseSchema says outright there i…
### i18n (3 items)
- `i18n.notification-localized-and-clears` → r4: knownGaps[0], steps[1]
and [2], acceptance[0] (clause, verify) and acceptance[4] (clause,
verify)
- `i18n.studio-follows-app-locale` → r6: knownGaps[0],
fixtures.requires[0], steps[2], acceptance[0] (clause, verify),
negative[0] and source re-pointed. The clause-0 premise was wrong: the
Studio object editor's strings come from objectui's metada…
- `i18n.surface-matrix` → r8: variants[5], acceptance[7].clause,
steps[8] and [9], source[0] and automated.ref corrected. variants[5]
still marked the view-empty-state variant blocked(fixture),
contradicting knownGaps[1] and steps[6] — i…
### identity-auth (18 items)
- `identity-auth.sso-enforced-first-paint` → r3: steps[4] and
acceptance[2].verify and [3].verify corrected. The step's 'IdP that
never redirects back' is outside the console's watchdog, which covers
only the hung sign-in request (20 s) — the console is no…
- `identity-auth.auth-method-matrix` → r8: knownGaps[3], steps[3] and
[4], acceptance[3].verify and variants[3] and [7] corrected. The no-SMS
state needs NODE_ENV=production: in development the SMS log transport
delivers, so send-otp succeeds.
- `identity-auth.invitation-scope-gates` → r4: steps[1] and [6] and
acceptance[5].verify corrected. cancel_invitation is owner/admin only,
so 'the entitled persona' in steps[6] was ambiguous and the
delegated_admin the item drives is refused it; and the…
- `identity-auth.admin-lifecycle-operations` → r8: steps[9] names where
Impersonate lives: only in the Users list row menu, so 'the admin
surface' sent a runner to the record page, which does not offer it.
- `identity-auth.session-list-revoke` → r3: steps[2] and [6] and
acceptance[0].verify and [4] (clause, verify) corrected.
- `identity-auth.api-key-ui-lifecycle` → r3: fixtures.requires[0] and
[1], steps[0] and [3] and acceptance[1].verify re-pointed. showcase_task
returns the same rows to a member and an admin, so the owner-view
comparison could not tell an RLS-scoped key…
- `identity-auth.org-membership-team-management` → r4: steps[6] and
acceptance[4].verify name an org-membership-scoped oracle. On the stock
`single` posture the `everyone` baseline reads survive remove-member, so
'a resource they could read as a member now refus…
- `identity-auth.self-service-password-reset` → r2:
fixtures.requires[0], knownGaps[0], steps[1] and acceptance[0] (verify,
evidence) re-pointed to the capture oracle that exists.
- `identity-auth.oauth-app-consent-loop` → r3: steps[1] names a redirect
URI the default client type accepts: create-client defaults to type
'web', which refuses http and loopback redirect URIs, so a runner
registering a localhost callback met a refusal…
- `identity-auth.linked-accounts-social` → r5: steps[1] and [5] and
acceptance[3].verify and [4].verify corrected. The slice read
sys_account's live methods as [get, list, aggregate, export]; the re-run
on #24036 found the meta read still serving ['get',…
- `identity-auth.two-factor-enrollment-reveal` → r3: steps[6] and
acceptance[4].clause corrected. Both read the enable dialog's 'shown
only once' as a promise about the secret, but the copy says 'The backup
codes are shown only once' — so a password-gated get-…
- `identity-auth.two-factor-verify-to-activate` → r2: steps[3] and
acceptance[1] (clause, verify, evidence) re-pointed.
- `identity-auth.two-factor-backup-codes` → r3: steps[8] and
acceptance[5].verify cite the regenerate by its index: they said 'step-5
regenerate', but the regenerate is steps[4]. Wording only
- `identity-auth.first-run-owner-bootstrap` → r2: steps[7] and
acceptance[4] (verify, evidence) corrected.
- `identity-auth.self-signup-gate` → r4: knownGaps[0], steps[5] and
acceptance[4] (verify, evidence) corrected.
- `identity-auth.email-verification-loop` → r4: fixtures.requires[2],
knownGaps[1], steps[0], [2] and [3] and acceptance[1].verify, [2].clause
and [3].verify re-pointed. Three premises had moved: the dev transport
logs no body or link, so the mail is capt…
- `identity-auth.workspace-org-switch` → r4: steps[6] and acceptance[3]
(clause, verify) split by posture.
- `identity-auth.implicit-account-linking-ownership` → r3:
fixtures.requires[2] names where the verification mail is read: the dev
log transport logs no body or link, so the link comes from the mail's
sys_email row.
### integration-system (7 items)
- `integration-system.flow-connector-picker` → r5: steps[2] and
acceptance[0] (verify, evidence) corrected.
- `integration-system.webhook-lifecycle` → r7: steps[2] names the create
body the delivery leg needs: a showcase_task create over REST requires
its `project` lookup and answers 400 VALIDATION_FAILED 'Project is
required' without one, so the create varian…
- `integration-system.job-scheduled-run` → r4: fixtures.requires gains
the scheduling switch, and acceptance[4] (clause, verify) is corrected.
- `integration-system.email-template-render` → r7: knownGaps[1] and
steps[1] name the HTTP-reachable send door. IEmailService.sendTemplate
is not something a runner can call, and no shipped flow invokes it;
#23922 drove every send through a scratch notify fl…
- `integration-system.notify-inbox-delivery` → r4: steps[6] and
acceptance[4] (clause, verify) re-pointed.
- `integration-system.external-datasource-federated-read` → r3:
acceptance[0].verify corrected: 'filters/$top work as on a native
object' — OData $filter is refused 400 INVALID_FILTER on /data (the
filter must be JSON), while the JSON filter param and top work, which
is…
- `integration-system.notification-preference-suppression` → r3:
steps[6] and acceptance[4].clause corrected. 'with locale fallback'
overstated the ladder: the store walks only DOWN (en-US → en → en), so a
recipient with no locale resolves the deployment default 'en' and…
### platform-core (15 items)
- `platform-core.metadata-authoring-roundtrip` → r3: steps[1], [3] and
[7] and acceptance[0].verify and [5].verify re-pointed. A plain PUT
/meta/view/NAME saves state=active, so the draft never appeared in
/meta/_drafts — ?mode=draft is required — and a view's…
- `platform-core.settings-hub-roundtrip` → r4: steps[1], [2], [3], [5]
and [12] and acceptance[7] (clause, verify) and [11] (clause, verify,
evidence)
- `platform-core.notification-center` → r2: fixtures.requires[0],
knownGaps[0], steps[8] and acceptance[6].verify re-pointed. The
showcase_scheduled_digest fixture never fires on a default boot —
OS_AUTOMATION_SCHEDULED_WORK_ENABLED leaves packaged sc…
- `platform-core.shell-nav-personalization` → r4: steps[1] and [3] name
the affordances that exist. A nav entry is pinned through its own More
actions menu ('Add to favorites')
- `platform-core.app-management-toggle` → r3: knownGaps[0], steps[5] and
[6] and acceptance[5] (clause, verify) re-pointed.
- `platform-core.manifest-install-contract` → r3: steps[1], [4] and
[7](b) re-pointed.
- `platform-core.activation-ledger-registration-home` → r5:
acceptance[3].verify corrected. It called sys_automation_run 'the
control proving the resolver is not answering undefined for everything',
but the dogfood test asserts sys_automation_run is undefined as well…
- `platform-core.activation-ledger-row-contract` → r4:
acceptance[0].verify, steps[6] and acceptance[2].clause corrected
(residual checklist-accuracy finding of QA run #23939, wave #23878; PR
#24000 re-pointed clause 4).
- `platform-core.packaged-object-extend-only` → r2: steps[2] and
source[3] corrected. The NOT_OVERRIDABLE refusal no longer names
allowRuntimeCreate/allowEnvironmentOverlay and the OS_METADATA_WRITABLE
escape hatch; it says, in one sentence, that the item is…
- `platform-core.marketplace-install-local-lifecycle` → r4: steps[4] and
[5] and acceptance[3].clause corrected. storageDir is served once, on
the listing envelope (data.storageDir), not on each entry — the admin
entry carries installedBy only — so 'installedBy + sto…
- `platform-core.marketplace-console-honesty` → r5: steps[2] and
acceptance[2] (clause, verify) re-pointed.
- `platform-core.keyboard-shortcut-surface` → r4: steps[3]'s pin note
brought forward: the settled inventory — Esc, ⌘ K, ? and ⌘ B, nothing
else off the chat page — matched exactly at objectui daeb77fc84dd too,
not only at 1fb1ced36243.
- `platform-core.lifecycle-retention-sweep` → r4: fixtures.requires[0],
knownGaps[1] and steps[1] re-pointed. The knownGap said no telemetry
datasource exists on stock, but `os dev` on a file DB now provisions
data.telemetry.db beside the primary and routes…
- `platform-core.seed-mode-matrix` → r2: fixtures.requires[0], steps[0]
and [6] and acceptance[4].verify corrected. A seed body refuses a `name`
key (SeedSchema is strict; the URL names the seed)
- `platform-core.settings-audit-secret-fingerprint` → r2: steps[2], [3]
and [6], acceptance[1].clause and [4].verify and negative[0] corrected.
The non-secret digest is FNV-1a 32 of canonical JSON ('fnv32:'), not
SHA-256 (crypto-adapter.ts digest), so the step-7 co…
### records-forms (16 items)
- `records-forms.crud-roundtrip` → r10: negative[0] re-pointed: the
Lifecycle select has no empty option and defaults to Prospect, so 'save
with required status empty' could not be staged; the negative now
empties the required Account Name, which…
- `records-forms.field-type-matrix` → r3: steps[2] and
acceptance[1].clause say what counts for the textarea-based widgets.
'not a generic text input' with 'richtext→editor' read as a FAIL for
richtext, markdown, html and code, whose dedicated widge…
- `records-forms.list-view-capabilities` → r3: steps[1], [2], [4], [6]
and [8] and acceptance[1], [2], [3], [6] and [8] re-pointed. The console
sends its list params without a `$` (filter / top / skip / search / sort
— the same AST), so every $-spelled e…
- `records-forms.conditional-rules-grid` → r4: acceptance[0].verify
records what 'blocked' means on this surface (the #23949 note for the
next author): a client-blocked grid save still sends one batch, which
the server refuses 400, by design per recheck…
- `records-forms.action-location-matrix` → r11: steps[3] names the
selection the toolbar dispatch needs: showcase_bulk_reassign on the list
toolbar runs on a selected row, and with none selected it only toasts
'select a row first'.
- `records-forms.validation-rule-type-matrix` → r3: variants[3] and
acceptance[4] (clause, verify) corrected.
- `records-forms.adhoc-filter-sort-builder` → r3: steps[4],
acceptance[2] (clause, verify), acceptance[3] (clause, verify),
negative[1] and source[1] re-pointed. Since objectui#12418 the toolbar
filter and sort live in the user's sys_user_preference row ui.…
- `records-forms.concurrent-edit-conflict` → r3: source[2],
acceptance[0].verify, steps[3] and [4] and acceptance[1] and [2]
(clauses) re-pointed (the carried reading and a checklist-accuracy
finding of QA run #23949, wave #23878).
- `records-forms.grid-personalization` → r4: steps[2], [3], [4] and [5]
and acceptance[2] (clause, verify) and [4] (clause, verify, evidence)
- `records-forms.lookup-picker-create-new` → r3: steps[5] corrected: the
picker debounces, so it sends one scoped server request per settled
term, not one per keystroke — still server-side, which is what
acceptance[2] scores.
- `records-forms.import-job-undo-cancel` → r4: fixtures.requires[2]
names the scratch object the console spec needs: the spec imports a
name,email,status CSV onto LIVE_IMPORT_OBJECT, and no showcase object
has those three fields, so #23949 authored qa_le…
- `records-forms.object-hook-lifecycle` → r5: steps[0], steps[1] and
acceptance[8].verify corrected. The audit oracle is a ctx.log.info line,
and the CLI's default log level is warn, so the boot step now exports
OS_LOG_LEVEL=info (#23949 ran on an info…
- `records-forms.cascading-multilevel-and-clear` → r3: steps[1] uses the
canonical app segment: /_console/apps/showcase_app/… names the app; the
package-id spelling still resolves. Wording only: no request or oracle
changed
- `records-forms.field-group-visible-when` → r4: steps[1] gains
highlightFields: with none declared, the derived detail page for
qa_group_gate puts every field in the highlight strip and draws no body
sections, so the detail half of the console clause coul…
- `records-forms.field-unique-enforcement` → r3: acceptance[0] (clause,
verify) re-pointed: on the SQLite leg the object-naming sentences are in
the envelope's developerMessage, and the user-facing `error` is the
generic 'A record with this value already e…
- `records-forms.predicate-relationship-traversal` → r3:
acceptance[1].verify corrected: the 400 carries the authored message,
not the rule name — a rule that evaluates true answers {field:
'_record', code: 'rule_violation', message: authored}
(rule-validator.ts),…
### studio-authoring (9 items)
- `studio-authoring.object-designer-roundtrip` → r4: steps[0], [1], [4],
[6] and [7] and acceptance[0].verify and [2] (clause, verify)
re-pointed.
- `studio-authoring.draft-publish-lifecycle` → r6: steps[2] and
acceptance[6] (clause, verify) re-pointed.
- `studio-authoring.org-override-registry-gate` → r3: variants[1]
corrected. The field variant still read 'create open', but
DEFAULT_METADATA_TYPE_REGISTRY has declared field
allowRuntimeCreate:false since #7893 (a standalone field row reached no
object's field…
- `studio-authoring.permission-matrix-editor-ux` → r3: acceptance[5]
(clause, verify) re-pointed.
- `studio-authoring.custom-page-source-tiers` → r3: steps[7](a) names
how to reach the draft preview.
- `studio-authoring.packaged-automation-studio-lock` → r3: acceptance[1]
(clause, verify), acceptance[2] (verify, evidence), steps[4] and [7],
knownGaps[0] and source re-pointed. Clause 1 named the server refusal as
422 [writable_package_required]; the forged flow w…
- `studio-authoring.packaged-display-class-direct-edit` → r2:
fixtures.requires[0] and [1], steps[0], [1] and [7] and acceptance[2]
(clause) and [4] (clause, verify)
- `studio-authoring.metadata-diagnostics-sweep` → r4:
acceptance[1].verify names the read that carries the envelope lock. It
told the runner to compare stats.locked against the lock GET
/api/v1/meta/TYPE/NAME publishes, but that default read is the cached
arm a…
- `studio-authoring.doc-on-app-menu` → r3: knownGaps[1] closed: the
.objectui-sha pin has been past the fork window since daeb77fc84dd — the
console carries the refusal page and the menu pruning — so the knownGap
no longer asks for a console built el…
### ux-conventions (3 items)
- `ux-conventions.write-feedback` → r3: knownGaps[1],
acceptance[3].verify and variants[1] and [6] corrected. knownGaps[1] was
stale: Save All toasts '1 row saved', Discard draft gives feedback and
Approve toasts the declared 'Approved "RECORD"';…
- `ux-conventions.disabled-control-reason` → r3: knownGaps[1] corrected.
It said Studio Publish calibrates hover only and that Publish and the
import wizard's Next carry reasons a keyboard cannot reach; at objectui
daeb77fc84dd both are keyboard-reachable…
- `ux-conventions.labels-over-machine-names` → r3: knownGaps[0] and
acceptance[3].verify corrected. knownGaps[0] was mostly stale: at
objectui daeb77fc84dd the permission-set list, the Changes panel and the
Display-field picker lead with the label, and the s…
## Re-runs: item → record → verdict
| item | record | verdict |
|---|---|---|
| `dashboards.cube-query` | #24074 | pass |
| `platform-core.activation-ledger-registration-home` | #24080 | pass |
| `platform-core.activation-ledger-row-contract` | #24079 | pass |
| `platform-core.manifest-install-contract` | #24076 | pass |
| `platform-core.seed-mode-matrix` | #24077 | pass |
| `platform-core.settings-audit-secret-fingerprint` | #24078 | pass |
| `studio-authoring.org-override-registry-gate` | #24075 | pass |
| `integration-system.external-datasource-federated-read` | #24094 |
pass |
| `records-forms.validation-rule-type-matrix` | #24095 | pass |
| `records-forms.predicate-relationship-traversal` | #24096 | pass |
| `records-forms.field-unique-enforcement` | #24098 | pass |
| `integration-system.notify-inbox-delivery` | #24099 | partial (A3 501
half blocked(fixture): messaging is always on) |
| `integration-system.job-scheduled-run` | #24100 | pass |
| `integration-system.email-template-render` | #24101 | pass |
| `integration-system.notification-preference-suppression` | #24102 |
partial (mandatoryTopics half blocked(fixture)) |
| `integration-system.flow-connector-picker` | #24103 | pass |
| `integration-system.webhook-lifecycle` | #24110 | partial (A2 bulk
pair blocked(dependency)) |
| `platform-core.metadata-authoring-roundtrip` | #24112 | pass |
| `platform-core.app-management-toggle` | #24115 | pass |
| `platform-core.notification-center` | #24116 | pass |
| `platform-core.lifecycle-retention-sweep` | #24117 | pass |
| `records-forms.object-hook-lifecycle` | #24121 | pass |
| `records-forms.concurrent-edit-conflict` | #24122 | pass |
| `records-forms.field-group-visible-when` | #24123 | pass |
| `records-forms.import-job-undo-cancel` | #24124 | pass |
| `records-forms.field-type-matrix` | #24125 | pass |
| `records-forms.crud-roundtrip` | no record (PM ruling) | pass |
| `records-forms.list-view-capabilities` | no record (PM ruling) | pass
|
| `records-forms.conditional-rules-grid` | no record (PM ruling) | pass
|
| `records-forms.action-location-matrix` | no record (PM ruling) | pass
|
| `records-forms.adhoc-filter-sort-builder` | no record (PM ruling) |
pass |
| `records-forms.grid-personalization` | no record (PM ruling) | pass |
| `studio-authoring.metadata-diagnostics-sweep` | no record (PM ruling)
| partial (A6 blocked(fixture)) |
| `studio-authoring.permission-matrix-editor-ux` | no record (PM ruling)
| pass |
| `studio-authoring.custom-page-source-tiers` | no record (PM ruling) |
pass |
| `studio-authoring.object-designer-roundtrip` | no record (PM ruling) |
pass |
| `studio-authoring.packaged-automation-studio-lock` | no record (PM
ruling) | pass |
| `studio-authoring.packaged-display-class-direct-edit` | no record (PM
ruling) | pass |
| `dashboards.system-overview-live-counts` | no record (PM ruling) |
pass |
| `dashboards.chart-type-matrix` | no record (PM ruling) | pass |
| `dashboards.dataset-report-authoring` | no record (PM ruling) | pass |
| `i18n.notification-localized-and-clears` | no record (PM ruling) |
pass |
| `i18n.studio-follows-app-locale` | no record (PM ruling) | pass |
| `i18n.surface-matrix` | no record (PM ruling) | pass |
| `platform-core.settings-hub-roundtrip` | no record (PM ruling) | pass
(clause text fixed in this diff) |
| `platform-core.shell-nav-personalization` | no record (PM ruling) |
pass |
| `platform-core.marketplace-install-local-lifecycle` | no record (PM
ruling) | pass |
| `platform-core.marketplace-console-honesty` | no record (PM ruling) |
pass |
| `ux-conventions.write-feedback` | no record (PM ruling) | pass |
| `ux-conventions.labels-over-machine-names` | no record (PM ruling) |
pass |
| `studio-authoring.draft-publish-lifecycle` | no record (PM ruling) |
pass (step text completed in this diff) |
| `identity-auth.self-service-password-reset` | no record (PM ruling) |
pass (step text completed in this diff) |
| `identity-auth.email-verification-loop` | no record (PM ruling) | pass
|
| `identity-auth.self-signup-gate` | no record (PM ruling) | pass |
| `identity-auth.first-run-owner-bootstrap` | no record (PM ruling) |
pass |
| `identity-auth.two-factor-verify-to-activate` | no record (PM ruling)
| pass |
| `identity-auth.api-key-ui-lifecycle` | no record (PM ruling) | pass |
| `identity-auth.session-list-revoke` | no record (PM ruling) | pass |
| `identity-auth.org-membership-team-management` | no record (PM ruling)
| pass (step text completed in this diff) |
| `identity-auth.invitation-scope-gates` | no record (PM ruling) |
partial (the delegated_admin cancel refusal was not driven: no
delegated_admin persona provisioned on this boot) |
| `identity-auth.linked-accounts-social` | no record (PM ruling) | pass,
with one finding corrected by the re-run |
| `identity-auth.auth-method-matrix` | no record (PM ruling) | pass |
| `identity-auth.oauth-app-consent-loop` | no record (PM ruling) | pass
|
The filed records stay open; the seat closes them out.
## Evidence for the re-runs without a record
Format: item · clauses driven · verdict · the oracle line.
- **`records-forms.crud-roundtrip`** · A0–A7, N0, N3 · **pass** · UI
create POST 201 re-read verbatim (website null); PATCH
{"annual_revenue":1234567} → diff = that key + updated_at; History shows
display values; 华宁 round-trip + search; clone 201 new id, owner = cloner
(source owned by another user); contributor-bound member clone of
INV-1003 → 404 RECORD_NOT_FOUND, count 13→13; header-menu Delete →
DELETE 200, filtered GET 0; pins 11+21 green
- **`records-forms.list-view-capabilities`** · A0–A9 (11 variants) ·
**pass** · In Progress: GET
showcase_task?…filter=[["status","equals","in_progress"]] 200, "2
records" = API total 2; Accounts search → `search=huaning` → 华宁科技; Task
List → `sort=-estimate_hours`, server order descending, 0 page errors;
55 inquiries via createMany (bare array) → Inquiries list requests
top=50 then top=50&skip=50, union 59 = total, no unpaged fetch; Grid
inline edit → Save All (1) → PATCH {"estimate_hours":31}, diff = that
key + audit; export grant qa_task_export bound (before: 403
EXPORT_NOT_PERMITTED) → Display footer "Export as CSV / XLSX / JSON",
each request carries the in_progress filter, CSV / JSON / XLSX each hold
the 2 rows; Display → Layout offers exactly Grid / Kanban / Gallery /
Calendar / Timeline / Gantt, each re-renders the tasks; Mark Done on 3
rows → 3 POST /actions/showcase_task/showcase_mark_done, Recalculate
Selection → 1 POST /api/v1/apps/showcase/recalc {"_selectedIds":[3
ids]}; legacy row menu Reopen / Reassign… / Quick View (once) /
Recalculate Estimate, Recalc → POST /apps/showcase/recalc {recordId};
Field Zoo: Specimen — Full row carries rgba(37, 99, 235, 0.08), Minimal
does not
- **`records-forms.conditional-rules-grid`** · A0–A4 · **pass** ·
INV-1002 Edit form grid (second line seeded at position 1): line 1 qty
120 + empty description → only that cell flagged (data-testid
line-items-invalid-0-description, "Description is required"), line 2
(qty 1) unflagged; Update → the one batch POST /api/v1/batch answers 400
VALIDATION_FAILED {description, required}, rows unchanged (scored per
the revised verify); filled → batch 200, only line 1 changed, sibling
updated_at unchanged; direct POST line qty 120 no description → 400
description/required, count 2→2; Status Paid in the form → product / qty
/ unit price disabled, Draft → enabled, no reload; receipt upload →
batch 200, line receipt {id, name, size, url /api/v1/storage/files/ID};
GET of that url answers 302 to the stored object and the sys_file row
reads qa-receipt.txt, 29 bytes
- **`records-forms.action-location-matrix`** · A0–A4 · **pass** ·
placement matrix: list_toolbar [Reassign…]; row menu not-done [Mark
Done, Reassign…, Quick View], done [Reopen, Reassign…, Quick View]; Task
Detail header not-done [Mark Done, Log Time, Archive], done [Log Time,
Archive]; ⋯ [Open Docs, Recalculate Estimate, Recalculate Selection];
quick-actions bar [Mark Done, Log Time, Archive]; Project Detail Related
→ Tasks rows carry Log Time beside the list_item actions; no extra
placement, New Task on no surface (half (b); half (a) blocked(fixture)
per knownGaps). Dispatch: toolbar Reassign… with nothing selected toasts
"select a row first" (revised step), with a row selected → POST
/actions/…/showcase_bulk_reassign, wizard "Reassign Task", Submit → POST
/automation/showcase_reassign_wizard/runs/RUN/resume → assignee set;
Quick View modal; Log Time →
/_console/forms/showcase_task.form?recordId=…; Open Docs opens a new tab
(external host unreachable from the container); Recalculate Selection →
POST /apps/showcase/recalc {recordId}, estimate 18 → 8; header Mark Done
→ POST /actions/…/showcase_mark_done, done true, Mark Done gone after
reload
- **`records-forms.adhoc-filter-sort-builder`** · A0–A4, N1, N3 ·
**pass** · In Progress + toolbar Priority = High and Estimate › 4 → GET
showcase_task?…filter=["and",[["status","equals","in_progress"]],["and",["priority","=","high"],["estimate_hours","›",4]]]
(view rule kept; the empty Title row dropped), rows = Build homepage
only; sort → `sort=due_date`, then Z → A → `sort=-due_date`, rendered
Ingest pipeline then Build homepage = server order; in-app nav away and
back, In Progress re-issues the merged filter and the builder shows both
conditions and the sort; storage = sys_user_preference
ui.list.showcase_task (user_id = admin,
perView.showcase_task.in_progress.filters / sort) + localStorage
list-state:ADMIN_ID:showcase_task; a second account on the same browser
opens In Progress with only the view filter and an empty builder;
Workbench Priority quick filter → ?uf_priority=high, a fresh tab issues
the identical filter request (5 rows = API count); Triage Urgent preset
→ ?uf__tab=urgent, fresh tab 2 rows → 2; bare object list carries no
userFilter elements, only the reserved uf__filter / uf__sort list-state
params
- **`records-forms.grid-personalization`** · A0–A4 · **pass** · Grid
view: drag the Assignee header handle → width 240 → 520, survives reload
(widths byte-equal); Display density cycle (Compact / Comfortable /
Spacious) → Spacious, row height 56 px, survives reload; storage =
sys_user_preference ui.list.showcase_task perView.showcase_task.grid
{columnState.widths.assignee 520, rowHeight tall} under the admin
user_id, localStorage grid-columns-showcase_task as cache; a fresh
browser context as admin restores the same widths and height; a second
account sees default widths and 41 px rows; group by Status → bands
Backlog 2 / To Do 3 / In Progress 2 / In Review 2 / Done 2 = per-status
server totals; clearing removes the bands; rows paint by the declared
rowColor (low slate-100, medium blue-100, high amber-100, urgent
red-100) after the final reload; reorder / pin n/a on the stock view as
the clause scopes them
- **`studio-authoring.metadata-diagnostics-sweep`** · A0–A7 · **partial
(A6 blocked(fixture))** · GET /meta/diagnostics bare body {entries,
total, scannedTypes, scannedItems, stats}, total 0 = entries.length,
scannedTypes 27 of 34 registry types (schema-bearing); stats.count =
list length for object 113 / view 36 / flow 32, package sets equal,
stats.locked = the /layers envelope lock count (107 / 0 / 32 — the
revised verify read); ?package=com.example.showcase → 245 items, object
24 (unscoped 113) and the scoped directory tiles read Object 24 / View
36 / Flow 32 / Action 71 / Page 28; ?severity=warning 200, entries
identical; ?type=viewes 400 INVALID_REQUEST naming view / views;
repeated severity 400 "supplied 2 times"; /metadata/_diagnostics "All
clear — Scanned 408 item(s) across 27 type(s)" and no diagnostics link
on the directory; outage pin protocol.diagnostics-store-outage.test.ts +
union-issues 25 passed; A6 blocked(fixture): no invalid stored row is
plantable on stock (knownGaps[0])
- **`studio-authoring.permission-matrix-editor-ux`** · A0–A5 · **pass**
· writable set qamx_matrix (package com.qa.matrix) in the metadata-admin
editor, Account expanded: filter "rea" → counter "3 / 23", 6 field
checkboxes in the DOM; Write all → those 3 read+write, clearing the
filter shows the 20 hidden fields unchanged (diff []); website Read all,
billing_email Clear; Save → PUT /meta/permission/qamx_matrix; saved
fields keys = tax_id (pre-existing), churn_reason / created_at /
created_by (the "rea" set), website (readable only) — billing_email
absent after Clear; at 820 px the matrix container is overflow-x auto,
scrollWidth 960 › clientWidth 772, and the Bulk column None button is
visible after scrolling; showcase_contributor: metadata admin has no
Save, 0 enabled bulk buttons, 791 of 791 checkboxes disabled, badge
"Read-only (this set is provided by a code package)" (artifact gate);
Studio Access badge "Read-only package — switch to or create a writable
package to edit." (package gate); no write sent on either; wall and type
gates not reachable on stock, as the revised clause records
- **`studio-authoring.custom-page-source-tiers`** · A0–A5 · **pass** ·
capability-map and command-center-jsx render static composed boards (no
compile panel, no script); command-center-jsx says its numbers are
static sample copy and the two html pages issue no showcase_* /data or
/analytics request (showcase_task total 11); crm-workbench: Total
projects 5 = showcase_project total 5 with GET
/data/showcase_project?top=200 on the wire; a row click binds the form
to "Mobile App", "+ New project" switches to create mode;
OS_PAGE_REACT=off boot: crm-workbench shows "React pages are disabled on
this deployment … (OS_PAGE_REACT=off …)" and fires no showcase_project
request, html pages unchanged; faulting html at the active save → 422
INVALID_METADATA (jsx-forbidden-tag …), draft save 200, publish-drafts
refused, GET 404, and — per the revised step — in a writable package
with an app referencing it, Studio
/_console/studio/PKG/interfaces?surface=page:NAME shows "HTML page
failed to compile (6)"; a throwing react page renders "React page error
— Error: QA deliberate react runtime fault"
- **`studio-authoring.object-designer-roundtrip`** · A0–A3 · **pass** ·
designer at
/_console/apps/setup/metadata/object/new?package=com.qa.repairs
(namespace repairs): Label typed first, then the name typed key by key
reads back "repairs_repair_asset" (underscores kept, prefix kept — the
revised step order); Asset Code label → field-apiname-input asset_code
while typing and after blur; Picklist: the select-needs-options banner
shows before any option (expected), then with good/fair/poor present
plus one empty row no banner after a 3.5 s debounce and no "at least 2
characters" text; Save → PUT
/meta/object/repairs_repair_asset?mode=draft&package=… carrying exactly
3 options; Publish → publish-drafts; v1 meta order asset_code,
condition, options Good / Fair / Poor; reopened, Fair → Average and
Condition dragged above Asset Code, Publish → v2 order condition,
asset_code, options good:Good / fair:Average / poor:Poor (an
intermediate publish from a runner mislabel of poor was corrected before
v2; it is in the evidence log); Studio Records tab → Add record → POST
/data/repairs_repair_asset {"asset_code":"AC-001"}, stored keys
asset_code / condition (no field_N)
- **`studio-authoring.packaged-automation-studio-lock`** · A0–A3 ·
**pass** · GET /packages: com.example.showcase writable false
(precondition); Studio Automations rail has no New button; canvas
touches + drag + Delete + 5 s wait → 0 non-GET requests; Enabled switch
disabled, aria-describedby "Read-only package — switch to or create a
writable package to edit." (no title), forced clicks leave aria-checked
true and send nothing; Alert on Urgent Task → Notify node inspector: all
13 visible inputs disabled, reload sends nothing; forged PUT
/meta/flow/showcase_urgent_task_alert?mode=draft&package=com.example.showcase
→ 403 ITEM_LOCKED, without ?package= → 403 NOT_OVERRIDABLE (the revised
clause); the flow byte-identical before/after
- **`studio-authoring.packaged-display-class-direct-edit`** · A0–A4, N1,
N2 · **pass** · showcase_task.default (picked from GET
/meta/view?object=showcase_task) opens editable, chip "writable", no
lock banner; label "All Tasks QAB" + owner_id column autosave PUT
?mode=draft 200 → Publish POST …/publish 200; served label/columns
changed, /layers still carries code; minted end user renders "All Tasks
QAB" + Owner header with no restart; system_overview (layers packageId
com.objectstack.plugin-auth) opens editable, widget "Total Users QAB"
draft+publish 200, rendered
/_console/apps/setup/dashboard/system_overview shows it; Reset overlay
(native confirm "Reset overlay for view/showcase_task.default?") →
DELETE 200 reset:true for view and dashboard, post-reset GET bodies
sha256 b6a5a989… / 765c0c00… byte-identical to a pristine boot of the
same tree, overlay null, list renders "All Tasks" (QAB 0), board renders
"Total Users" (QAB 0); packaged flow showcase_urgent_task_alert: lock
banner, 3/3 inputs disabled, 0 save/publish/reset buttons
- **`dashboards.system-overview-live-counts`** · A0–A5, N0 · **pass** ·
run after the packaged-display-class reset (board byte-identical to a
pristine boot); second admin sign-in revoked via POST
/auth/revoke-session 200 → row tombstoned (revoked_at stamped,
revoke_reason user_revoked, expires_at 1 s earlier); recount after board
load: sys_user 9, sys_session unfiltered 199, live (revoked_at null,
expires_at › now) 197; tiles Total Users 9, Active Sessions 197 (not
199); wire: sys_user_metrics user_count 9, sys_session_metrics
runtimeFilter {revoked_at:null, expires_at:{$gt:now}} → SQL "WHERE
(revoked_at IS NULL AND expires_at › $1)" → 197; served body lacks
widget_organizations, carries widget_packages_installed, neither
rendered; Audit Events by Action pie 5 segments = direct sys_audit_log
group-by 5 actions (config_change/create/delete/login/update), Events by
User bars, table rows + Total 1158; console 0 errors/warnings
- **`dashboards.chart-type-matrix`** · A7 (the revised compareTo probe:
fixtures.requires[2], steps[9]; the other clauses are unchanged by this
diff) · **pass** · literal windows on showcase_invoice.issued_on, same
clock: 2026-07-19..2026-10-17 → 7, 2026-04-20..2026-07-19 → 3;
qa_compareto_probe (dateRange issued_on last_90_days; line on issued_on
+ metric, showcase_invoice_metrics invoice_count, compareTo
previousPeriod) PUT ?mode=draft 200 → publish 200; wire: dataset query
with timeDimensions dateRange [2026-07-19, 2026-10-17] + compareTo
answers rows [{invoice_count 7, invoice_count__compare 3}] and the
per-month series carries invoice_count__compare; render: metric tile "7
· 133% vs previous period", line chart draws 2 series ("Invoices" and
"Invoices · vs previous period"); 0 page errors; probe deleted after
- **`dashboards.dataset-report-authoring`** · A5 (the revised
joined-report partition; the other clauses are unchanged by this diff) ·
**pass** · stock-data boot (10 tasks, 2 done): direct
showcase_task_metrics aggregates est_hours done:false 220 + done:true 24
= unfiltered 244, task_count 8 + 2 = 10; rendered Task Overview
(Joined): Open Tasks total 220.0 (Backlog 30 / To Do 36 / In Progress
100 / In Review 54) = est_hours under done:false, not the unfiltered
244; Completed Tasks total 2 (Done 2) = task_count under done:true, not
the unfiltered 10; wire: the two block queries carry runtimeFilter
{done:false} / {done:true}, SQL "WHERE done = $1"
- **`i18n.notification-localized-and-clears`** · A0, A4 (the revised
steps[1] switch and steps[2] read; A1–A3 are unchanged by this diff) ·
**pass** · zh recipient picks 中文(中国) in the avatar-menu Language
switcher (menu offers English / 中文(中国)) → PATCH /api/v1/data/sys_user/ID
{"locale":"zh-CN"}, sys_user.locale reads zh-CN, html lang zh-CN; served
showcase_task_assigned_notify node notify_assignee: inline title "New
task assigned: {{ record.title }}", template null, channels [inbox];
admin reassigns "Audit current IA" → zh recipient, "SEO migration plan"
→ EN control (sys_user.locale null): zh row title "New task assigned:
Audit current IA" (exact, no raw {{ }} token), shown in the zh bell
(chrome 消息中心 / 全部标记为已读); EN row "New task assigned: SEO migration plan",
identical modulo the record value, shown in the en-US bell
- **`i18n.studio-follows-app-locale`** · A0 (the revised object-editor
source; A1 dotted-key pass and A4 return trip read on the same surface)
· **pass** · admin switches to 中文 through the avatar-menu Language
switcher → sys_user.locale zh-CN, html lang zh-CN;
/_console/apps/setup/metadata/object/showcase_project renders the pinned
objectui designer.object.* zh strings 基础信息 / 标识、显示名与分类 / 名称 / 显示名 /
复数显示名 / 图标 / 描述 / 访问控制 / 暴露姿态, none of the EN counterparts, and neither
served metadataForms string (分类标签 / 显示名称); no dotted key (the one dotted
token is the package id com.example.showcase); switching back to English
restores Identity, display names & classification / Plural label /
Exposure posture and drops 基础信息
- **`i18n.surface-matrix`** · variants[5], [7], [8] and A7 (the revised
parts; the other variants are unchanged by this diff) · **pass** · admin
on zh-CN via the avatar-menu switcher; GET /i18n/translations/zh-CN 200,
11 top-level groups (objects, apps, dashboards, pages, metadataForms,
globalActions, datasets, flows, messages, settingsCommon, settings) —
every one inside the twelve-group platform vocabulary; picklists absent
because no bundle on this boot authors one (grep: no picklists: key in
showcase or platform bundles); variants[5]: the two urgent rows demoted
→ /showcase_task/view/urgent renders 暂无紧急任务 /
当前没有需要立即处理的事项。任务优先级调整为「紧急」后会出现在这里。 (the bundle key
objects.showcase_task._views.urgent.emptyState), no EN source copy;
variants[7]: Chart Gallery renders 任务数与平均进度; variants[8]:
/setup/page/connect_agent renders 连接智能体 + its zh subtitle,
showcase_command_center renders 活跃项目 / 待办任务 / 待复审 / 风险项目 / 客户 / 总预算;
rows and locale restored after
- **`platform-core.settings-hub-roundtrip`** · A0–A3, A7, A11 (the
revised steps; A4–A6, A8–A10, A12 are unchanged by this diff) · **pass
(clause text fixed in this diff)** · dev-seeded boot: GET
/api/settings/localization timezone UTC source tenant before any PUT,
default_country US source default; PUT {timezone: Asia/Tokyo} 200 →
re-read Asia/Tokyo source tenant; GET /auth/me/localization timezone UTC
→ Asia/Tokyo; showcase_field_zoo f_datetime (stored
2026-06-17T14:30:00Z) renders 6/17/2026 11:30 pm at Tokyo and 2:30 pm at
UTC (exactly +9 h); sys_setting_audit row action set, source api, scope
tenant, actor_id = admin, new_hash set; console
/setup/system/settings/localization: Default country shows no badge,
Save changes → PUT {"default_country":"GB"} → "Overrides Default |
Tenant", server source tenant; anon GET /api/settings/localization and
/api/settings → 401 UNAUTHENTICATED each, no manifest; PUT
mail.smtp_password 200 (response without the plaintext) →
sys_platform_setting value null, value_enc sec_e558…, 0 sys_setting
rows, sys_secret row with 63-byte ciphertext, plaintext in neither
table. The run showed acceptance[0].clause, [1].clause and [3].verify
still carried the moved premises; fixed in this diff
- **`platform-core.shell-nav-personalization`** · A1, A3 (the revised
steps[1] and [3]; the other clauses are unchanged by this diff) ·
**pass** · no hover pin button on any nav row (0); the Capability Map
row's "More actions for Capability Map" menu offers Add to favorites /
Open in new tab / Copy link / Design → the Favorites section shows
Capability Map, sys_user_preference ui.favorites holds
nav:nav_capability_map pinned:true; survives reload; the same menu now
reads Remove from favorites → section empty. Record "Audit current IA":
no star button in the header (0); header More actions menu → Add to
favorites → Favorites shows the record (record:showcase_task:3NinA5_…);
survives reload; Remove from favorites → section empty, ui.favorites []
- **`platform-core.marketplace-install-local-lifecycle`** · A3 (the
revised steps[4] and [5]; the other clauses are unchanged by this diff)
· **pass** · boot B (OS_CLOUD_URL=off) from a scratch copy; os package
install of examples/app-crm/dist/objectstack.json → "air-gapped inline
install", com.example.crm 4.0.0, crm_app listed, crm_account 200; admin
GET /marketplace/install-local 200: envelope keys items/total/storageDir
(data.storageDir = the copy's .objectstack/installed-packages), entry
keys
packageId/versionId/manifestId/version/installedAt/withSampleData/installedBy,
no per-entry storageDir; minted member 200: same entry WITHOUT
installedBy, envelope items/total WITHOUT storageDir (both keys omitted,
not nulled); anonymous 401 UNAUTHENTICATED
- **`platform-core.marketplace-console-honesty`** · A2 (the revised
steps[2]; the other clauses are unchanged by this diff) · **pass** ·
boot B: Installed Apps lists com.example.crm v4.0.0 with Details /
Uninstall and no reseed/purge control on the list (0); Details →
/setup/system/marketplace/com.example.crm, "More install options" menu =
Re-seed sample data / Purge sample data / Uninstall from this runtime;
Purge (native confirm accepted) → same-origin POST
…/com.example.crm/purge-sample-data 200 {deleted 28, errors 0,
withSampleData false}, menu then offers Add sample data → same-origin
POST …/reseed-sample-data 200 {inserted 28, errors 0, withSampleData
true}
- **`ux-conventions.write-feedback`** · variants[1] and [6], A3 (the
revised parts; the other variants and clauses are unchanged by this
diff) · **pass** · variants[1]: Grid view, double-click the Priority
cell of "Audit current IA" (no Edit inline button) → select opens, High
→ "Save All (1)" → PATCH /data/showcase_task/ID 200, toast "1 row
saved", server priority medium → high; variants[6]: /system/approvals
row click opens the decision drawer (Request 1 of 3, EXP-2001) → Approve
→ confirm dialog with Comment → POST /approvals/requests/ID/approve 200,
toast "Approved \"EXP-2001\"" (the declared successMessage),
sys_approval_action approve row by admin with the comment (the request
stays pending: dual sign-off, the second group has not approved); A3
shape 2: New task submitted empty → no write request, field errors
"Title is required" / "Project is required" + toast "Please check the
highlighted fields: Title, Project", no success toast; A3 shape 1:
backlog task edited to Done → PATCH 400 VALIDATION_FAILED, dialog shows
"Invalid task status transition.", toast "Please check the highlighted
fields: Status", server status unchanged backlog
- **`ux-conventions.labels-over-machine-names`** · A3 under the revised
scoring rule, plus the knownGaps[0] regression rows for the
permission-set list and the Changes panel · **pass** · scratch package
with a draft object (QA Widget) and permission set (QA Readers): Studio
Access lists "QA Readers" first with qalqna_readers after it, Changes ·
2 panel leads with QA Readers / QA Widget, names secondary (regression
rows PASS); A3 scan (id-shaped, dotted Zod path, TS class name, repo#N /
bare #NNN, ADR-NNNN) across Studio Access, Changes, Data, Interfaces,
the automations flow surface, the task list and record, Setup audit log
and Profile: no platform-built match; the flow surface carries "#3508"
and "ADR-0097" only inside app-authored flow labels (Approver Binding
Specimens (#3508), Declarative Connector Ping (ADR-0097)) → authoring
findings, recorded, not scored; the id-shaped strings on the task
list/record are user ids this run wrote into the TEXT field assignee
(fixture artifact), and the audit log's are in its Record ID column,
with its User column reading Dev Admin
- **`studio-authoring.draft-publish-lifecycle`** · A6 (the revised
steps[2] Studio half; the API clauses are unchanged by this diff) ·
**pass (step text completed in this diff)** · writable package
(namespace qa): dashboard draft qa_lifecycle_probe PUT
?mode=draft&package=PKG 200, runtime GET 404 RESOURCE_NOT_FOUND; first
attempt with no app in the package: Interfaces shows "This package has
no app yet", no chip (the step gave no way to open the item); with an
app draft whose nav names the dashboard: chip "Unpublished draft" ×1 and
Changes pill "· 2" → Publish → Pending changes dialog (1 dashboard, 1
app) → POST /packages/PKG/publish-drafts 200 → chip 0, Changes with no
pill, same after reload; runtime GET 200 "QA Lifecycle Probe". steps[2]
now says how to reach the draft in Studio
- **`identity-auth.self-service-password-reset`** · A0 and steps[1]–[4]
(the revised capture oracle) · **pass (step text completed in this
diff)** · POST /auth/request-password-reset 200 (non-leaking message);
the reset mail is a sys_email row, subject "Reset your ObjectStack
password", status sent, to_addresses "QA Reset ‹EMAIL›" (an equality
filter on the bare address misses it — step now says match on the
address), body carries reset-password/TOKEN; no LogTransport line
carries it; reset-password 200 → old password 401
INVALID_EMAIL_OR_PASSWORD, new 200; token reused → 400 INVALID_TOKEN
- **`identity-auth.email-verification-loop`** · A1–A3 on an open-posture
boot (OS_AUTH_AUDIENCE_POSTURE=open via the env door forces
verification) · **pass** · auth/config requireEmailVerification true;
uninvited sign-up 200; sys_email row "Verify your ObjectStack email
address" with a verify-email token; unverified sign-in 403
EMAIL_NOT_VERIFIED; resend 200 → a second row with a different token;
POST verify-email 404; GET verify-email with the resent token 200
{"status":true,"user":null}; sign-in 200 with a session; the first token
presented after verification 200 {"status":true,"user":null}, the
consumed resent token again 200 — silent, as the revised clause records;
garbage token 401 INVALID_TOKEN
- **`identity-auth.self-signup-gate`** · A4 / steps[5] on an
OS_DISABLE_SIGNUP=true boot, and the knownGaps[0] open variant ·
**pass** · auth/config disableSignUp true, posture invite_only:
UNINVITED forge 403 SELF_REGISTRATION_CLOSED, 0 sys_user rows; INVITED
address (invite-member 200) forge 400 EMAIL_PASSWORD_SIGN_UP_DISABLED, 0
rows — the invited address is the one that shows the toggle; open
variant: OS_AUTH_AUDIENCE_POSTURE=open +
OS_AUTH_AUDIENCE_SELF_REGISTRATION_PERMISSION_SET=showcase_member_default
exported → settings read source env, locked; an uninvited sign-up 200
- **`identity-auth.first-run-owner-bootstrap`** · A4 door legs (steps[7]
on an OS_DISABLE_SIGNUP boot whose owner is the seeded admin; steps[8]
on the stock boot); the zero-user wizard legs are unchanged by this diff
and were not re-run · **pass** · door 1: uninvited 403
SELF_REGISTRATION_CLOSED (recorded, not door 1), invited 400
EMAIL_PASSWORD_SIGN_UP_DISABLED, no rows; door 2 (stock boot): uninvited
forge 403 SELF_REGISTRATION_CLOSED
- **`identity-auth.two-factor-verify-to-activate`** · A1 / steps[3]
(OS_AUTH_TWO_FACTOR=true boot) · **pass** · features.twoFactor true;
/two-factor/enable 200 (otpauth totpURI, 10 backup codes); password
sign-in before any verify → 200 with a session token, no
twoFactorRedirect, no two-factor cookie; sys_user.two_factor_enabled
false
- **`identity-auth.api-key-ui-lifecycle`** · A1 / steps[0] and [3] (the
revised object and page path) · **pass** · member mints via POST
/api/v1/keys 201 (key returned once); x-api-key GET /data/sys_session
200 → 3 rows, all the owner's user_id, the same id set as the owner's
session read; admin reads 11 rows over 4 users (the control);
showcase_task via key 10 rows = admin 10 (why it cannot discriminate);
/_console/apps/com.objectstack.account/developer/integrations renders
"Connect an AI agent" with Generate key
- **`identity-auth.session-list-revoke`** · A0, A2, A4 (the revised
steps[2] and [6]) · **pass** · A signs in twice, B once; revoke-session
by device-2 token 200 → device-2 401; A's raw /data/sys_session: 3 rows,
2 live (revoked_at null) + 1 own tombstone, B absent; list-sessions 2;
Setup → Sessions all_sessions headers User / IP Address / Active
Organization / Created At / Expires At (no revoke columns), A and B live
rows; revoked view headers User / IP Address / Revoked At / Revoke
Reason / Created At, A's row with user_revoked
- **`identity-auth.org-membership-team-management`** · A4 / steps[6]
(the revised oracle) · **pass (step text completed in this diff)** ·
before removal the member: get-full-organization 200, set-active 200,
showcase data 200; remove-member 200 → the member's live sessions are
revoked (revoke_reason organization_membership_ended), the old session
answers 401 to everything; a FRESH sign-in reads 5 showcase rows
(everyone baseline survives — the finding) while get-full-organization
and set-active answer 403 USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION. The
step now says to read the after-side on a fresh session
- **`identity-auth.invitation-scope-gates`** · steps[1] page path and A5
owner/admin cancel · **partial (the delegated_admin cancel refusal was
not driven: no delegated_admin persona provisioned on this boot)** ·
/_console/apps/com.objectstack.account/sys_invitation (opened by URL)
lists the pending invite with an "Invite User" button; cancel-invitation
as admin 200 → row status canceled
- **`identity-auth.linked-accounts-social`** · A3, A4, steps[5]
(provider-less stock boot); steps[1] needs a provider and was not re-run
· **pass, with one finding corrected by the re-run** · meta read serves
sys_account apiMethods ["get","list"] (the slice's
"[get,list,aggregate,export]" is not the meta read); aggregate route 400
VALIDATION_ERROR and export 403 EXPORT_NOT_PERMITTED — read routes pass
the method gate, neither 405; forged POST and DELETE 405
OBJECT_API_METHOD_NOT_ALLOWED; socialProviders []; Linked Accounts
surface offers no link/connect action (only the My Links view tab) —
absent on every boot, as the revised A3 records. The steps[5] wording
was corrected to say both
- **`identity-auth.auth-method-matrix`** · steps[3]/A3 and variants[7];
the generic-OIDC localhost-IdP leg was not re-run · **pass** ·
NODE_ENV=production boot with OS_AUTH_PHONE_NUMBER_ENABLED=true:
features.phoneNumberOtp false, send-otp 400 SMS_SERVICE_REQUIRED;
control, development boot: phoneNumberOtp true, send-otp 200 "code sent"
(the log transport delivers); magic-link on stock 404
- **`identity-auth.oauth-app-consent-loop`** · steps[1] (the revised
redirect URI) · **pass** · well-known AS 200; create-client with
http://localhost, http://127.0.0.1, https://localhost and
https://127.0.0.1 callbacks → each 400 invalid_redirect_uri ("web
clients require https redirect URIs on non-loopback hosts" — the vendor
rule refuses http OR loopback); https://app.example.test/callback → 201
with client_id and a one-time client_secret
## Findings disproved, corrected or routed
- **Corrected by the re-run:** the slice's "`sys_account` apiMethods are
live `[get,list,aggregate,export]`" does not hold as a metadata reading.
The meta read still serves `['get','list']`, as `sys-account.object.ts`
declares. What is true is that the aggregate and export read routes pass
the method gate: a bare aggregate answers 400 `VALIDATION_ERROR` and
export answers 403 `EXPORT_NOT_PERMITTED`, never 405.
`linked-accounts-social` steps[5] now states both. Writes still answer
405.
- **Confirmed live, beyond the slice's wording:** `remove-member` also
revokes the member's live sessions (`revoke_reason`
`organization_membership_ended`). The org-membership oracle is therefore
read on a fresh sign-in, which still reads showcase rows (the `everyone`
baseline, as the slice said) and draws 403
`USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION`.
- **Routed as card drafts for the seat** (in the os-dev-report's
`out_of_scope_findings`, not filed by this PR):
- the internal `__search` column leaks onto user surfaces;
- Installed Apps shows the installer as a raw user id.
- **Design observations for a ruling**, recorded and not edited:
- `two-factor-disable-lifecycle`: an MFA-gated admin gets 403
`MFA_REQUIRED` on `PUT /api/settings/auth`.
- `two-factor-enrollment-reveal`: `get-totp-uri` re-reveals the same
secret behind the password. The enable dialog's "shown only once" covers
the backup codes only, so no copy is contradicted.
## Open questions (on #24036)
1. `ux-conventions.labels-over-machine-names` A3 records an ADR number
cited in server-built prose but does not score it. Should a user-facing
ADR citation FAIL? The re-run met none in platform-built text; the two
it met (`ADR-0097`, `#3508`) were in app-authored flow labels.
2. `two-factor-enrollment-reveal`: is a password-gated re-reveal of the
TOTP secret accepted design?
## Items not re-run, and why
- `identity-auth.workspace-org-switch`: needs a walled-posture scratch
boot for both `group` and `isolated`, with `@objectstack/organizations`,
a platform owner and a membership policy. Not driven in this run.
- `identity-auth.sso-enforced-first-paint`: needs an SSO-enforced boot
with a registered provider. Not driven.
- Partial re-runs:
- `invitation-scope-gates`: the delegated_admin cancel refusal was not
driven, because no delegated_admin persona was provisioned.
- `linked-accounts-social` steps[1]: needs a provider.
- `auth-method-matrix` generic-OIDC leg: needs a localhost IdP.
- `first-run-owner-bootstrap` zero-user wizard legs: unchanged by this
diff.
- Wording-only edits owe no re-run:
`implicit-account-linking-ownership`, `two-factor-enrollment-reveal`,
`two-factor-backup-codes`, `admin-lifecycle-operations`,
`platform-core.packaged-object-extend-only`,
`platform-core.keyboard-shortcut-surface`,
`records-forms.lookup-picker-create-new`,
`records-forms.cascading-multilevel-and-clear`,
`studio-authoring.doc-on-app-menu` and
`ux-conventions.disabled-control-reason`.
- `studio-authoring.metadata-diagnostics-sweep` A6 stays
blocked(fixture), as recorded above.
## Acceptance notes
These are observations, not filed:
- A `HookConditionError` reaches the caller as a generic 500
`INTERNAL_ERROR`.
- Authoring into a never-created package id is admitted as an authoring
workspace, by design per `package-writability.ts`.
- The showcase's Quick View row action opens a "Component Gallery" page.
- An API-created invoice line reads `amount` null.
- Audit-log login rows carry the user id in Record ID under object
`sys_session`.
- The vendor's redirect-URI refusal reads "web clients require https
redirect URIs on non-loopback hosts" for an https loopback URI too,
because the rule is https AND non-loopback.
- The two-factor arming, verification and open-posture re-runs drove env
doors on separate boots: `OS_AUTH_TWO_FACTOR`, `OS_DISABLE_SIGNUP`,
`OS_AUTH_AUDIENCE_POSTURE=open`, `NODE_ENV=production`.
Local gates and the governed-merge check are in the os-dev-report on
#24036. The seat applies `skip-changeset`; this PR writes no labels.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5c92b01 commit 9ffe8af
8 files changed
Lines changed: 675 additions & 365 deletions
File tree
- docs/qa/platform-checklist/areas
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
0 commit comments