Repository navigation
docs(qa): re-point the half-B platform-checklist items that described an older product - #24141
Conversation
…tale cube-query reads the meta body as a bare data array; dataset-report-authoring reconciles the joined blocks per measure; chart-type-matrix gives the compareTo probe a dated window over invoice data that spans both windows; system-overview-live-counts tombstones one session so the live-session predicate discriminates. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
notification-localized-and-clears drives the recipient locale through the switcher and names the per-recipient template path beside the inline title; studio-follows-app-locale judges the object editor against the console catalogue it reads; surface-matrix fixes its group counts and specimens. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…n found stale Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…ound stale Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…ound stale Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…he sharingModel gate bites Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…n found stale Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…ound stale Each item gains a revision bump and a history row naming the wave finding it answers; clauses keep their rulings. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…dger-b Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…d and code as the re-run read them Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…at the re-runs measured notify-inbox-delivery knownGaps[2]: messaging is on the CLI's always-on slate, so no os dev boot lacks it. job-scheduled-run fixtures.requires[2]: the AppPlugin skip line is info-level; the boot banner carries the reason. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
… fields The re-run's name-only body answered 400 VALIDATION_FAILED before any hook ran; email and message are required on showcase_inquiry. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
… measured The settings-hub re-run read the dev-seeded timezone as tenant before any PUT, the effect through auth/me/localization and a rendered datetime, and the badge on default_country; three clause texts still carried the old premises. The draft-publish re-run found no chip until the package held an app whose navigation names the dashboard, so the step now says so. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…found stale Each item gains a history row naming the card. The mail-capture items read the token from the sys_email row; the sign-up gates say which address proves which door; the two-factor, session, org, linked-account, SSO, auth-method and OAuth items name the surfaces and oracles the slice measured. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
The reset mail's to_addresses carries a display name, so the step matches on the address. remove-member revokes the member's live sessions, so the membership oracle is read on a fresh sign-in. The meta read still serves sys_account apiMethods get and list, so the linked-accounts step says that and how the aggregate and export routes answer. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
席位核对 · ACCEPT · PR #24141 (draft, head
|
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 arevisionbump and ahistoryrow naming #24036. Docs only: no product code, no published package touched.node scripts/check-platform-checklist.mjsis 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-runrecords 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 answersdataas 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 stocksingleposture theeveryonebaseline 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 platform-checklist: revise the items the #23878 full run and the #23879 since:v18 run found stale (knownGaps, steps, fixtures, oracles, source anchors across 15 areas; no clause red) — ledger follow-up #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 onlyidentity-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 itsprojectlookup 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; QA run · area:integration-system (18/18) · 94114c1f · 2026-10-16 · 14 PASS / 4 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #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 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 QA run · area:platform-core (30/30) · 94114c1f · 2026-10-16 · 27 PASS / 1 PARTIAL / 2 FAIL / 0 BLOCKED / 0 NOT-RUN #23939, wave qa(checklist): run the whole platform checklist on main (selectorall, 272 items) and extract every new failure into its own card #23878; PR docs(qa): re-point six stale platform-checklist clauses across five items to the shapes their rulings shipped #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, butos devon 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 anamekey (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 QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #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 QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #23949, wave qa(checklist): run the whole platform checklist on main (selectorall, 272 items) and extract every new failure into its own card #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 QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #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 (QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #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 changedrecords-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-facingerroris 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 meta-field-write-inert: an acceptedfieldPUT never reaches the object — a runtime-created field is storedvalid=trueand is absent fromfieldsforever #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: steps7 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
dashboards.cube-queryplatform-core.activation-ledger-registration-homeplatform-core.activation-ledger-row-contractplatform-core.manifest-install-contractplatform-core.seed-mode-matrixplatform-core.settings-audit-secret-fingerprintstudio-authoring.org-override-registry-gateintegration-system.external-datasource-federated-readrecords-forms.validation-rule-type-matrixrecords-forms.predicate-relationship-traversalrecords-forms.field-unique-enforcementintegration-system.notify-inbox-deliveryintegration-system.job-scheduled-runintegration-system.email-template-renderintegration-system.notification-preference-suppressionintegration-system.flow-connector-pickerintegration-system.webhook-lifecycleplatform-core.metadata-authoring-roundtripplatform-core.app-management-toggleplatform-core.notification-centerplatform-core.lifecycle-retention-sweeprecords-forms.object-hook-lifecyclerecords-forms.concurrent-edit-conflictrecords-forms.field-group-visible-whenrecords-forms.import-job-undo-cancelrecords-forms.field-type-matrixrecords-forms.crud-roundtriprecords-forms.list-view-capabilitiesrecords-forms.conditional-rules-gridrecords-forms.action-location-matrixrecords-forms.adhoc-filter-sort-builderrecords-forms.grid-personalizationstudio-authoring.metadata-diagnostics-sweepstudio-authoring.permission-matrix-editor-uxstudio-authoring.custom-page-source-tiersstudio-authoring.object-designer-roundtripstudio-authoring.packaged-automation-studio-lockstudio-authoring.packaged-display-class-direct-editdashboards.system-overview-live-countsdashboards.chart-type-matrixdashboards.dataset-report-authoringi18n.notification-localized-and-clearsi18n.studio-follows-app-localei18n.surface-matrixplatform-core.settings-hub-roundtripplatform-core.shell-nav-personalizationplatform-core.marketplace-install-local-lifecycleplatform-core.marketplace-console-honestyux-conventions.write-feedbackux-conventions.labels-over-machine-namesstudio-authoring.draft-publish-lifecycleidentity-auth.self-service-password-resetidentity-auth.email-verification-loopidentity-auth.self-signup-gateidentity-auth.first-run-owner-bootstrapidentity-auth.two-factor-verify-to-activateidentity-auth.api-key-ui-lifecycleidentity-auth.session-list-revokeidentity-auth.org-membership-team-managementidentity-auth.invitation-scope-gatesidentity-auth.linked-accounts-socialidentity-auth.auth-method-matrixidentity-auth.oauth-app-consent-loopThe 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 greenrecords-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 notrecords-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 bytesrecords-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 reloadrecords-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 paramsrecords-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 themstudio-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 recordsstudio-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/afterstudio-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 buttonsdashboards.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/warningsdashboards.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 afterdashboards.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 belli18n.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 afterplatform-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 diffplatform-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 UNAUTHENTICATEDplatform-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 backlogux-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 "审批节点「处理人 Value」应改为记录 lookup(现查 metadata 端点→只能手填);附 approver value 语义核实 + queue 未实现 #3508" and "ADR-0097" only inside app-authored flow labels (Approver Binding Specimens (审批节点「处理人 Value」应改为记录 lookup(现查 metadata 端点→只能手填);附 approver value 语义核实 + queue 未实现 #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 Adminstudio-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 Studioidentity-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_TOKENidentity-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_TOKENidentity-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 200identity-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_CLOSEDidentity-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 falseidentity-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 keyidentity-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_revokedidentity-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 sessionidentity-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 canceledidentity-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 bothidentity-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 404identity-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_secretFindings disproved, corrected or routed
sys_accountapiMethods are live[get,list,aggregate,export]" does not hold as a metadata reading. The meta read still serves['get','list'], assys-account.object.tsdeclares. What is true is that the aggregate and export read routes pass the method gate: a bare aggregate answers 400VALIDATION_ERRORand export answers 403EXPORT_NOT_PERMITTED, never 405.linked-accounts-socialsteps[5] now states both. Writes still answer 405.remove-memberalso revokes the member's live sessions (revoke_reasonorganization_membership_ended). The org-membership oracle is therefore read on a fresh sign-in, which still reads showcase rows (theeveryonebaseline, as the slice said) and draws 403USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION.out_of_scope_findings, not filed by this PR):__searchcolumn leaks onto user surfaces;two-factor-disable-lifecycle: an MFA-gated admin gets 403MFA_REQUIREDonPUT /api/settings/auth.two-factor-enrollment-reveal:get-totp-urire-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)
ux-conventions.labels-over-machine-namesA3 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.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 bothgroupandisolated, 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.invitation-scope-gates: the delegated_admin cancel refusal was not driven, because no delegated_admin persona was provisioned.linked-accounts-socialsteps[1]: needs a provider.auth-method-matrixgeneric-OIDC leg: needs a localhost IdP.first-run-owner-bootstrapzero-user wizard legs: unchanged by this diff.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-menuandux-conventions.disabled-control-reason.studio-authoring.metadata-diagnostics-sweepA6 stays blocked(fixture), as recorded above.Acceptance notes
These are observations, not filed:
HookConditionErrorreaches the caller as a generic 500INTERNAL_ERROR.package-writability.ts.amountnull.sys_session.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