Skip to content

QA run · tier2c:browser-2 (18/18) · e4e5c6e3 · 2026-08-18 · 10 PASS / 4 PARTIAL / 4 FAIL #9467

Description

@os-zhuang

Tier-2C — the final round of the platform-checklist QA wave (#9296). Scope: the last 18 unpinned P0/P1 items on the browser / mixed surfaces. All 18 consulted; every unpinned P0/P1 item in the ledger now has a verdict.

Environment fingerprint

framework sha (subject) e4e5c6e3c608b1b807c83a0d5b734f213eb1a1dd — unmoved throughout
.objectui-sha 665661ab093263f39f2e660a295ea615dbcee35a; check:console-sha ✓ stamp = pin
spec (checklist) re-synced from origin/main at round start — check-platform-checklist: OK — 15 areas, 190 items (190 active); 30 kinds mapped, 0 waived
tree objectstack-qa-wave, detached at the pin. objectui:build / objectui:refresh never run
ports / DBs 3191 file:/tmp/qa-wave/t2c-A.db (main) · 3192 t2c-B.db (pinyin-OFF) · 3193 t2c-C.db (fresh-boot repro) · 3194/3195 fresh scaffold. Max 2 concurrent
seed --seed-admin; personas minted with POST /auth/sign-up/email (bravo@qa.example, charlie@qa.example)
browser playwright-core 1.62.1 via executablePath=/opt/pw-browsers/chromium (the RUNNER-prescribed symlink)
date 2026-08-18
exit state all instances stopped; every probe datasource / dashboard / flow / overlay removed (sys_metadata runtime rows: none); git status = 11 docs/qa/** paths, 0 commits, HEAD still e4e5c6e3

Scope and per-item revision

# item rev pri surface verdict
1 identity-auth.session-list-revoke 1 P1 mixed PASS
2 identity-auth.api-key-ui-lifecycle 1 P1 mixed PASS
3 identity-auth.self-service-profile-password 1 P1 mixed PARTIAL
4 identity-auth.org-membership-team-management 2 P1 mixed PASS
5 integration-system.job-scheduled-run 2 P1 mixed PARTIAL
6 integration-system.notify-inbox-delivery 2 P1 mixed PASS
7 integration-system.datasource-credential-refusal-matrix 1 P0 mixed FAIL
8 dashboards.strict-widget-rejects-stray-keys 2 P1 mixed FAIL
9 dashboards.chart-type-matrix 3 P1 browser PARTIAL
10 dashboards.global-filters-rescope 2 P1 browser FAIL
11 i18n.notification-localized-and-clears 3 P1 browser FAIL
12 i18n.surface-matrix 3 P1 browser PARTIAL
13 i18n.strict-translation-key-rejection 1 P1 mixed PASS
14 search.pinyin-flag-both-sides 2 P1 mixed PASS
15 search.console-global-search 3 P1 mixed PASS
16 automation.flow-run-step-nesting 2 P1 mixed PASS
17 automation.flow-error-handling 2 P1 mixed PASS
18 cli.scaffold-console-first-paint 1 P1 mixed PASS

10 PASS / 4 PARTIAL / 4 FAIL.


Per-clause verdicts

1 · identity-auth.session-list-revoke rev 1 — PASS

clause verdict oracle evidence (text)
c0 owner-scoped list pass A(bravo) GET /auth/list-sessions + GET /data/sys_session → 3 rows, all user_id=fUn5OO…; B(charlie) → 2 rows, all user_id=pTunKp…. B's row ids GVz0GATJ…, N5G8bwKM… absent from A's read
c1 Account sessions surface pass screenshot: /apps/com.objectstack.account/sys_session/view/mine as bravo renders "1 record" with IP Address / Active Organization / Created At / Expires At columns — count matches bravo's post-revoke API list (exactly 1)
c2 revoke-session by token pass POST /auth/revoke-session {token:<device-2>} → {"status":true}; device-2 GET /data/showcase_task → 401; device-1 → 200
c3 revoke-other-sessions pass called from device-1 → list-sessions returns exactly 1 row (device-1's own token); a previously-other token → 401; caller → 200
c4 admin all-users surface pass screenshot: Setup → Sessions all_sessions shows a User column with cross-user rows (Dev Admin / QA bravo / QA Charlie) and a Revoked view; API: admin read = 7 rows across 3 users, 2 with revoked_at set and revoke_reason="user_revoked"
c5 read-only over data API pass PATCH and DELETE /data/sys_session/{id} → OBJECT_API_METHOD_NOT_ALLOWED, allowed:["get","list","aggregate","export"] — as bravo and as admin

2 · identity-auth.api-key-ui-lifecycle rev 1 — PASS

clause verdict oracle evidence
c0 show-once pass POST /api/v1/keys → 201 {id, name, prefix:"osk_0LHT6VwR", key:"osk_0LHT6VwRXOKC…"}; follow-up GET /data/sys_api_key/{id} returns prefix only, grep for the raw secret = 0
c1 authenticates as owner pass x-api-key:<secret> on GET /data/showcase_task → 200, 10 rows — identical to the owner's session read (10) and to admin's (10); no superuser widening
c2 revoke immediate pass PATCH /data/sys_api_key/{id} {revoked:true} → 200; next x-api-key call → 401
c3 restore reversible pass {revoked:false} → 200; next x-api-key call → 200
c4 mine-view scoping pass charlie's sys_api_key read = 0 rows, key id absent; admin all_keys read contains it
c5 dialog + list render pass screenshot of Setup → Connect an Agent after a ref-targeted "Create key" (native setter + input/change on the name field): panel reads "For CI, scripts, and agents without a browser. The key acts as you — treat it like a password. It is shown ONCE." and "Key "qa-console-mint" created — copy it now, it will not be shown again:" with the copyable secret and the x-api-key: header form. Re-opened all_keys list shows prefix-only rows

3 · identity-auth.self-service-profile-password rev 1 — PARTIAL

clause verdict oracle evidence
c0 name persists pass POST /auth/update-user {name} → {"status":true}; GET /auth/get-session → "name":"Dev Admin QA2C"; after a hard reload the profile card still renders "Dev Admin QA2C"
c1 avatar is a real storage write pass network trace: POST /storage/upload/presigned → 200 → PUT /storage/_local/raw/<token> → 200 → POST /storage/upload/complete → 200 {fileId:"efa8853a-…"} → POST /auth/update-user → 200; get-session.image = "/api/v1/storage/files/efa8853a-…"; rendered <img src> equals it. Not a data: URL
c2 avatar remove pass profile-avatar-remove-btn → POST /auth/update-user → 200; get-session.image = null; zero <img> on the card, initials "DA" fallback returns
c3 password rotation pass change-password (correct current) → 200; sign-in with new → 200; sign-in with old → 401
c4 wrong current refused server-side pass change-password with a wrong current → 400 {"code":"INVALID_PASSWORD"}; the old password still signs in (200) → credential unchanged
c5 passwordless set-initial blocked(fixture) stock showcase ships no configured IdP, so no hasLocalPassword=false user exists — exactly the item's own knownGap

Also observed (c6-adjacent, no clause): the profile card renders "Email cannot be changed." with the email input disabled.

4 · identity-auth.org-membership-team-management rev 2 — PASS

clause verdict oracle evidence
c0 {current_org_id} resolves pass the Setup nav emits /_console/apps/com.objectstack.setup/sys_organization/record/org_msy0avxsf5eiqdj6 — the record page, not the list; screenshot shows Members (3) / Invitations / Teams (1) tabs. Cross-checked: get-active-member and get-full-organization both return org_msy0avxsf5eiqdj6
c1 role writes and bites pass update-member-role bravo→admin → membership read shows admin; bravo's organization/update → 200. Demote to member → same call → 403 YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION
c2 closed vocabulary pass role guest → 400 {"code":"ROLE_NOT_FOUND","message":"ROLE_NOT_FOUND: guest"}; stack-declared position contributor → same; delegated_admin → accepted. Follow-up list-members shows no stray row
c3 rename persists pass organization/update {name:"QA Renamed Org"} → 200; get-full-organization.name = "QA Renamed Org"; the org record page header follows
c4 remove shrinks access pass charlie before: get-full-organization 200 / list-members 200 / get-active-member 200. After remove-member: 403 / 400 / 400 with {"code":"NO_ACTIVE_ORGANIZATION"}
c5 team join rows pass create-team → team V1ZCv5iv…; two add-team-member calls → GET /data/sys_team_member = exactly 2 rows, pairs (V1ZCv5iv…, fUn5OO…) and (V1ZCv5iv…, pTunKp…)
c6 gate both ways pass as a non-admin member, all five forged calls refused server-side — update-member-role 403, remove-member 403, update 403, create-team 403, add-team-member 403 — and org name + all three roles unchanged after. UI side: the same member loading the org record page gets "App not available" and zero management affordances

5 · integration-system.job-scheduled-run rev 2 — PARTIAL

clause verdict oracle evidence
c0 all three schedule variants fire blocked(fixture) interval ✓ flow-schedule:showcase_scheduled_digest run_count=75, last_status=success, with its own sys_job_run rows and real side effects (75 sys_inbox_message digest rows). once ✓ flow-wait:run_e97bd614…:hold run_count=1. cron ✗ showcase_health_sweep is registered with schedule_type=cron, schedule_expression="0 1 * * *", timezone=UTC but run_count=0 — its next fire is outside the session window. The item prescribes near-term scratch jobs in a writable package; the runtime job door is closed (c5), so that needs source + redeploy
c1 durable name-keyed truth pass sys_job row {name:"showcase_health_sweep", id:"job_58a9d9a1-…"} — keyed by name, adapter-minted row id, schedule persisted (schedule_type/schedule_expression/timezone). Counters move: run_count 75 / 15, last_run_at, last_status=success. sys_job_run rows carry {job_name, status, started_at, completed_at, duration_ms, attempt, trigger}
c2 retry ladder blocked(fixture) needs a deliberately-throwing scratch job; runtime authoring closed
c3 timeout status blocked(fixture) same
c4 two loud skip doors blocked(fixture) neither skip line appears in the boot log (grep -ic "job handler not found|skipping" = 0) because the stock showcase has no disabled job and no handler-missing job at this pin — the #4774/#4888 regression is fixed. Observing the lines needs authored specimens
c5 closed doors refuse loudly pass runtime create: PUT /meta/job/qa_runtime_job → [not_creatable] Metadata type 'job' is code-only: the metadata-type registry declares allowRuntimeCreate=false and allowOrgOverride=false … Declare it in source (**/*.job.ts) and redeploy. An operator may set OS_METADATA_WRITABLE=job …. Parse door (JobSchema from @objectstack/spec/system): id → "job.id was removed in @objectstack/spec 17.0.0 (#4667, ADR-0049) … name IS the job's identity everywhere"; stray wibble → named; aliases → cron→schedule, interval→schedule, fn→handler, each with a did-you-mean. Valid control accepted

6 · integration-system.notify-inbox-delivery rev 2 — PASS

clause verdict oracle evidence
c0 delivery pass after reassigning showcase_task/5BiNGEhsVjoH0UeB to bravo, bravo's GET /notifications → {id:"kq18I3z8jXVFhSnD", type:"task.assigned", title:"New task assigned: Warehouse schema", body:"You have been assigned \"Warehouse schema\".", actionUrl:"/showcase_task/5BiNGEhsVjoH0UeB"}
c1 recipient-scoped pass charlie's authenticated listing → {"notifications":[],"unreadCount":0}
c2 mark-read persisted per receipt pass POST /notifications/read {ids:[…]} → {readCount:1}; ?read=false relist → empty; sys_notification_receipt as bravo → {notification_id:"kq18…", channel:"inbox", state:"read"} (was delivered)
c3 both sides of the gate pass anonymous GET /notifications → 401 {"code":"UNAUTHENTICATED"}. (The 501 capability-unavailable half was not exercised — the clause makes that side optional/citable from a minimal boot.)
c4 unimplemented channel dead-letters pass scratch flow with channels:['push'] triggered → zero sys_inbox_message rows for the recipient (bravo still holds only the task.assigned row) and sys_notification_delivery gained exactly one row channel="push", state="dead" against 13 inbox/success rows. No fabricated inbox row
c5 backing rows reflect surface pass as bravo: sys_inbox_message 1 row {notification_id:"kq18…", topic:"task.assigned"}, sys_notification_receipt 1 row keyed (notification_id, user_id, channel:"inbox"). RLS-scoped — admin's read of the same tables shows only admin's 11 rows

7 · integration-system.datasource-credential-refusal-matrix rev 1 — FAIL (P0)

clause verdict oracle evidence
c0 every inline door refuses pass 6/6 doors, each refusing with the offending key/position named and external.credentialsRef prescribed: config-key (config.password → "password is a credential and is not accepted inline in driver config (#7990)"), URL userinfo ("this url embeds a password in its userinfo … (#8082)"), URL query param ("carries ?password= in its query string … (#8337)"), mongo driver-options (config.options.auth.password → "not accepted in the driver-options passthrough (#9040)"), placeholder (${DB_PASSWORD} → same config-key refusal), connector inline authentication (PUT /meta/connector → "must not inline secrets via authentication … drop authentication (or set { type: 'none' })")
c1 credentialsRef path works pass the external/credentialsRef-shaped datasource published {success:true} with status:"ok" on the connect probe
c2 both read doors redact pass full-body grep of GET /api/v1/datasources, GET /api/v1/datasources/:name and GET /api/v1/meta/datasource for all 10 planted cleartext values → 0 hits on both doors. Shapes agree (both drop the key rather than masking)
c3 presence-preserving pass credential SET → hasSecret:false, redactedConfigKeys:["encryptionKey"]; credential UNSET → redactedConfigKeys:[]. Distinguishable
c4 legacy alias spellings pass planted passwd / pwd / authtoken through the metadata door (structural validation, as the knownGap predicts) and passwd through the datasource-admin door on an unknown driver. Metadata door serves config:{host:…} only; admin door reports redactedConfigKeys:["passwd"]. All three planted values: 0 hits on both doors
c5 unknown driver redacted by name pass driver cockroachdb (no shipped contract) with config.password → admin door config:{host:"h.qa.example"}, redactedConfigKeys:["password"]; metadata door config:{host:…}. Value absent from both
c6 echoed-mask write guard pass read the datasource, PATCH the response back unmodified → 200; post-round-trip read still reports redactedConfigKeys:["encryptionKey"] and the stored sys_metadata row still contains the credential (grep count 1). Live connect probe unavailable — @objectstack/driver-turso is not installed — so the "still works" half used the stored-credential oracle
c7 redaction is not a privilege artifact fail access-control finding — detail withheld pending maintainer; held by the runner. Item integration-system.datasource-credential-refusal-matrix, clause c7. Already filed as #9391; independently re-confirmed this round. No endpoint, method, payload or reproduction published here.

Note on encryptionKey (turso): write-accepted + read-redacted on both doors — the correct expectation per #8081 scope item 4, not a write-refusal failure. Separately observed and not a clause failure: the still-writable encryptionKey is stored in cleartext in sys_metadata, which the ordinary data API serves — but that path is admin-gated (PERMISSION_DENIED for a member, UNAUTHENTICATED for anonymous) and is exactly the "stays stored" behaviour datasource-credential-redaction.ts documents.

8 · dashboards.strict-widget-rejects-stray-keys rev 2 — FAIL

clause verdict oracle evidence
c0 all 11 legacy keys rejected, key named pass object, categoryField, categoryGranularity, valueField, aggregate, aggregation, rowField, columnField, xAxisField, yAxisFields, measures — 11/11 rejected, each error containing the literal key name
c1 error prescribes the ADR-0021 shape pass "The pre-ADR-0021 inline analytics shape (object + categoryField + valueField + aggregate, pivot rowField/columnField) was removed — bind a dataset and select dimensions + values by name. Renderer-only settings belong under options."
c2 hallucinated key pass chartFlavour → "Unrecognized key(s) on this dashboard widget: chartFlavour." — key echoed, no did-you-mean offered (honestly absent, no near match). Quarantined component/data and drill near-keys drillDown/drilldown/drill also rejected with their own prescriptions
c3 wrong-layer keys inside chartConfig pass chartConfig.dataset → "dataset is not part of the chart config — it is the dashboard widget's own key (ADR-0021), a sibling of chartConfig, not a key inside it."; chartConfig.drillDown → "it is a REACT-TIER prop, written beside objectName/aggregate: <ObjectChart objectName=… drillDown={…} />"; aggregate and objectName likewise named
c4 rejected draft not persisted pass after every rejected PUT …?mode=draft, GET /meta/dashboard/qa_stray_key_probe returns a bare lock descriptor with no widgets — nothing carrying the stray key
c5 corrected widget renders fail the corrected widget (dataset: showcase_task_metrics, dimensions:['status'], values:['task_count']) published {success:true, version:"sha256:319d0221…", seq:3} and then renders "Dashboard Not Found". See R-1 below

9 · dashboards.chart-type-matrix rev 3 — PARTIAL (matrix: 16 of 20 variants verified)

Ground truth first (/api/v1/data): 11 tasks · 5 distinct statuses · 4 distinct priorities · 2 month buckets · 5 projects across 3 accounts.

clause verdict oracle evidence
c0 each of 20 variants renders blocked(product-bug → R-1) 16 verified per-variant in the Chart Gallery (screenshot first, DOM after). 4 unreachable: kpi/gauge/solid-gauge/bullet need a scratch dashboard, which R-1 makes unrenderable
c1 mark counts match buckets pass bar Tasks by Status 5 recharts-rectangle paths = 5 statuses · column Tasks by Priority 4 = 4 priorities · horizontal-bar Hours by Status 5 bars (screenshot) · line Tasks Created (monthly) recharts-curve over 2 month buckets · area idem (curve + fill) · pie Status Split 5 recharts-sector · donut Priority Split 4 sectors · funnel Status Funnel 5 recharts-trapezoid · radar Priority Radar recharts-polar-grid + recharts-polygon over 4 priorities
c2 combo = mixed marks, dual axes pass Task Count vs Avg Progress carries both recharts-rectangle and recharts-curve in one SVG, with two y-axis tick sets rendered (0 2 4 6 8 left, 0 25 50 75 100 right)
c3 distinctive geometry pass treemap Hours Treemap = 6 nested <rect> (no cartesian grid); sankey Status Flow (Sankey) = recharts-sankey-link with 11 link paths; scatter Estimate vs Progress = its own cartesian point plot. None falls back to a bar
c4 four single-value synonyms blocked(product-bug → R-1) qa_single_value_gallery (kpi/gauge/solid-gauge/bullet) published {success:true, seq:4} → "Dashboard Not Found"
c5 pivot cross-tab pass Tasks by Status × Priority renders a real table: headers Status | Low | Medium | High | Urgent | Total, 5 status rows + Total. 3-cell spot check vs the API: Done×Medium=3 (API done=3), Backlog×Low=1, grand Total=11; the Total row 1,5,3,2 equals the API priority buckets {low:1, medium:5, high:3, urgent:2} exactly
c6 coverage ratchet pass examples/app-showcase/test/coverage.test.ts → 1 file, 13 tests passed
c7 compareTo renders blocked(product-bug → R-1) qa_compareto_probe published {success:true, seq:5} → "Dashboard Not Found"
c8 converged compareTo pinned at the parse pass packages/spec/src/ui/dashboard-compareto.test.ts → 1 file, 17 tests passed. Confirmed live through the dashboard metadata root too: {kind:'previousPeriod'} accepted; the bare string → "dashboard.widgets[].compareTo: 'previousPeriod' (the bare string form) was removed in @objectstack/spec 17.0.0 (#5011)…"; {offset:'7d'} → "dashboard.widgets[].compareTo.offset was removed in @objectstack/spec 17.0.0 (#5011, ADR-0049 …)"

Enum re-read as the item's step 0 requires: ChartTypeSchema has exactly 20 members — the variants list is current.

10 · dashboards.global-filters-rescope rev 2 — FAIL

clause verdict oracle evidence (network trace)
c0 region re-scopes per widget's own field pass selecting EMEA re-issues 9 POST /analytics/dataset/query; invoice widgets carry {"region":"emea"}, account widgets {"sales_region":"emea"} — per-widget field mapping in one trace
c1 dateRange through each object's own date field pass invoice widgets {"issued_on":{"$gte":"2026-05-20","$lte":"2026-08-18"}}; account widgets {"signed_on":{…}}
c2 opted-out KPI is real pass "Accounts (all time)" issues {"measures":["account_count"]} with no runtimeFilter on load and is absent from all re-query sets; its value stays 14 across region and reset changes
c3 displayed aggregate = server truth pass under EMEA + last-90-days the Invoices tile shows 1; the identical direct query returns {"invoice_count":1} with SELECT COUNT(*) … WHERE ((issued_on >= $1 AND issued_on < $2) AND region = $3)
c4 prospects excluded by date scope pass 14 accounts, 3 with signed_on = null; a wide signed_on window returns 11 = 14 − 3; unfiltered returns 14
c5 composition on Delivery Operations pass with global task_status=in_progress, the "Awaiting Review" tile's query is $and[{status:"in_review"}, $and[{created_at:…},{status:"in_progress"}]] and the tile goes 2 → 0; the direct aggregate with both filters returns {"task_count":0} and with the tile filter alone {"task_count":2}
c6 Studio inspector authors filterBindings fail see R-1 — the authored binding does not persist and does not drive the widget
c7 Reset is dirty-gated pass pristine bar exposes only the two filter buttons (no Reset); after selecting APAC the bar reads Last 90 days | APAC | Reset; clicking Reset restores Last 90 days | Region: All, fires 9 re-queries in the default (region-free) shape, and the Invoices tile returns to 7 while the opted-out tile stays 14
c8 {value,label} options + server GROUP BY blocked(fixture) {value,label} half proven: the trigger renders the LABEL "EMEA" while the committed value on the wire is "emea". The server-GROUP-BY half has no fixture — both revenue-pulse.dashboard.ts and ops-dashboard.dashboard.ts declare static inline options arrays, and opening the select fired 0 analytics requests. No dataset-sourced option list exists on stock showcase

11 · i18n.notification-localized-and-clears rev 3 — FAIL

clause verdict oracle evidence
c0 title is the flow-authored literal pass flow showcase_task_assigned_notify cfg.title = 'New task assigned: {record.title}'; on a zh-CN recipient session the bell entry reads exactly New task assigned: Warehouse schema, character for character. Session locale confirmed zh by the surrounding chrome (消息中心 / 全部标记为已读 / 未读 / 38秒钟前 / 查看全部通知). No raw {record.title}
c1 deep-link target renders zh-CN fail see R-3 — the click-through never reaches the record
c2 mark-as-read clears pass ref-targeted click inside the bell popover fired POST /api/v1/notifications/read {"ids":["enFXX2Z6ZecLsZtH"]} → 200 {"readCount":1}
c3 cleared state is authoritative pass after a full page reload the re-opened panel reads 已读完所有通知 with an empty 未读 tab — the server persisted it
c4 title is recipient-INDEPENDENT pass the same event notified an EN-session recipient (charlie) with the identical string New task assigned: Warehouse schema — no per-recipient divergence, which is the correct (retired-capability) contract

12 · i18n.surface-matrix rev 3 — PARTIAL

Expectations grounded first in GET /api/v1/i18n/translations/zh-CN (134,700 bytes; top-level groups objects, globalActions, dashboards, pages, messages, apps, metadataForms, settingsCommon, settings).

clause verdict oracle evidence
c0 every variant renders its known-translated key blocked(fixture) 12 of 13 variants verified — nav (setup/account app nav fully zh: 总览/软件包/API 密钥/审批中心/会话/数据源/连接智能体), list headers (标题/项目/负责人/状态/优先级/截止日期/进度/操作), form labels + placeholders (选择…/请选择), select options (待规划/待办/进行中/评审中/已完成; 低/中/高/紧急), view emptyState (see CF-1), _sections (task form 任务信息; contact form 这是谁), dashboard (widget title 任务数与平均进度), page (姓名/邮箱/公司/留言/提交咨询), Settings namespace (本地化 · 区域/格式/财务 · 默认时区/默认语言/日期格式), Studio metadataForms (数据/自动化/界面/权限/发布/保存草稿/记录/表单/验证/钩子), sys_ object (用户 · 身份 · 名称/邮箱/手机号码), feed verb (c10). 1 blocked: action dialog (c5)
c1 expectations from server truth pass the translation read was captured first and every per-variant expectation cites its key path
c2 fallback is honest pass untranslated declared strings render their source label (e.g. showcase_task's EN description; the showcase app's own nav labels — apps.showcase_app is legitimately absent from the bundle). A regex scan for dotted key paths across every driven zh surface found zero raw keys
c3 option labels localize everywhere blocked(environment) proven in 2 of 3 places — list cells (评审中/进行中/高/紧急) and form selects (待规划…已完成; 低/中/高/紧急). The list filter panel did not open under the driver, so the third place is unverified
c4 fully-covered list never mixes locales pass the showcase_task list header row is 100% zh-CN: 标题 | 项目 | 负责人 | 状态 | 优先级 | 截止日期 | 进度 | 操作
c5 action dialog copy blocked(fixture) the only two translated _actions (showcase_recalc_estimate → 重算工时, showcase_recalc_selection → 重算所选) are not reachable from the showcase_task list row menu (编辑/删除/Mark Done/Reassign…/Quick View) or the record page (Mark Done/Log Time/Archive); the actions that are reachable carry no _actions entry and so legitimately render EN
c6 /i18n/locales envelope pass {"success":true,"data":{"locales":[{"code":"en","label":"en","isDefault":true},{"code":"zh-CN","label":"zh-CN","isDefault":false}]}} — the declared envelope, exactly the configured set, no superset, not a bare array
c7 /i18n/translations/zh-CN vocabulary pass non-empty; all 9 top-level keys sit inside the declared translationDataShape vocabulary — no fourth dialect
c8 /labels/:object/:locale agrees with the UI pass the labels response {title:标题, project:项目, assignee:负责人, status:状态(+options), priority:优先级(+options), due_date:截止日期, progress:进度, …} matches the rendered zh list-header row field-for-field
c9 unknown locale degrades honestly pass /translations/zz-ZZ → 200 {"locale":"zz-ZZ","translations":{}}; /labels/showcase_task/zz-ZZ → 200 {"object":"showcase_task","locale":"zz-ZZ","labels":{}}. Empty maps, no 500, no dotted-key dump
c10 feed/audit verb localizes pass and it nearly went the other way — see CF-2. With the tenant localization setting locale=zh-CN, the next write produced the activity row 更新了 任务 "Warehouse schema" — the messages.activityUpdated template resolved, object label included

13 · i18n.strict-translation-key-rejection rev 1 — PASS

clause verdict oracle evidence
c0 every variant rejected at the item door pass 9 variants via PUT /meta/translation/<name>, each alongside a valid objects.showcase_task.label. All rejected with the surface named ("Unrecognized key(s) on this translation") and the key echoed: wibble, o, app, nav, dashboard, errors, notifications, validationMessages, _meta
c1 same variants rejected at the bundle door pass scratch script against @objectstack/spec: defineTranslationBundle (root export) and defineTranslation (via the /system subpath — see CF-3) each throw for all 11 variants with equivalent error content; both valid controls accepted
c2 did-you-mean on near misses pass field helpText → "Unrecognized key(s) on this field translation: helpText. Did you mean helpText → help?"; dashboard widget label → "Unrecognized key(s) on this widget translation: label. Did you mean label → title?"
c3 prescriptions on legacy + retired keys pass o → "use 'objects.<object_name>'"; app → "use 'apps.<app_name>'"; nav → "use 'apps.<app_name>.navigation.<node_id>.label'"; dashboard → "use 'dashboards.<dashboard_name>' (plural)"; errors → "has no replacement … Author the message on the rule itself (object.validations[].message)"; notifications → "notifications have no translation group, omit them"; validationMessages → "removed in @objectstack/spec 17.0.0 (#4667, ADR-0049) … author the message on the rule itself"; _meta → "use the top-level 'locale' field"
c4 failed save persists nothing pass after every rejection GET /meta/translation/qa_t2c_tr → RESOURCE_NOT_FOUND — the valid sibling key was not half-saved
c5 corrected document saves and resolves pass corrected save → {success:true, seq:1, state:'active'}, and it resolves: /i18n/translations/zh-CN then returned the authored objects.showcase_task.label = 任务(QA已改) and fields.title.help = 帮助; /i18n/labels/showcase_task/zh-CN served the new help too. Deleted afterwards, and the shipped values (任务 / 标题) came back

14 · search.pinyin-flag-both-sides rev 2 — PASS

clause verdict oracle evidence
c0 flag ON, every latin variant hits pass per-variant on /data/<obj>/query: zhangwei→张伟 (1), zw→张伟 (1), ZhangWei→张伟 (1), 张→张伟 (1), huaning→华宁科技 (1), hnkj→华宁科技 (1), 华宁→华宁科技 (1)
c1 additive, display-field scoped pass clean contrast: keji (科技's pinyin) returns 0 on showcase_contact — whose company field holds 华宁科技 — and 1 on showcase_account, where 华宁科技 IS the display name. Control: wangfang → 王芳 (1)
c2 flag OFF splits correctly pass after OS_SEARCH_PINYIN_ENABLED=false restart: initials zw → 0, hnkj → 0, wf → 0 (companion gone); 张 → 1 and 华宁 → 1 (source column). Full-pinyin zhangwei/huaning still return 1 — a source-column hit, evidenced by the exact stored values email=zhangwei@huaning.example and website=https://huaning.example / billing_email=billing@huaning.example. See CF-4
c3 OFF is inert end-to-end pass plugin roster diff: 48 loaded (ON) vs 47 (OFF); full-text grep of the 6.1 MB OFF debug boot log for "pinyin" → 0 occurrences
c4 seeded rows recallable on first boot pass every ON-side query above succeeded on a fresh boot with no manual rebuild

15 · search.console-global-search rev 3 — PASS

clause verdict oracle evidence
c0 one idempotent open path pass the header Search button (action:command-palette:open) and Ctrl+K each yield the overlay:command-palette overlay with ?palette=1 on the URL; calling open while open is a no-op
c1 palette calls the platform endpoint pass wire trace GET /api/v1/search?q=INV-1003&limit=24&objects=… → 200 (searchAll, not a per-object fanout)
c2 CJK/pinyin recall reaches grouped hits pass zhangwei → GET /api/v1/search?q=zhangwei… → 200 with the 张伟 hit; the palette renders it under the "Contact" object heading (screenshot before DOM)
c3 Enter navigates pass ref-targeted select → /_console/apps/com.example.showcase/showcase_contact/record/XiTyx8y4Db0CtnoA, and the record page renders 张伟 / 华宁科技 / zhangwei@huaning.example
c4 empty input shows recents pass with a blank query the overlay renders the "Recently viewed" group and 0 /api/v1/search requests fire
c5 RLS parity, both personas pass restricted member (signed-up, bound to the contributor position): GET /api/v1/search?q=INV-1003&objects=showcase_invoice → 200 {"hits":[],"totalObjects":1,"totalHits":0} — searched, no hit, no leaked count; the identical admin query returns INV-1003. Positive control that the member's read is not a blanket refusal: the same member's /data/showcase_invoice returns 200 with 0 rows (all 12 seeded invoices are owned by ada/linus/grace)
c6 /search page, same path + empty state pass /apps/com.example.showcase/search?q=INV-1003 fires GET /api/v1/search?q=…&limit=60 → 200 and renders 1 result for "INV-1003" grouped under Invoice; a no-hit query renders the designed empty state 0 results for "zzzznohitzzz" / No results found / Try adjusting your search terms — no error envelope, no error toast

A distinct defect surfaced while running this item and fails none of its clauses (the console always scopes its query): the unscoped documented route answers 400 — see R-4.

16 · automation.flow-run-step-nesting rev 2 — PASS

clause verdict oracle evidence
c0 3-item run completes, body ran 3× pass POST /automation/showcase_batch_reminders/trigger with 3 tasks → run run_b6640d78… status=completed; steps with nodeId=send_reminder = 3
c1 region tags pass the three body steps each carry {parentNodeId:"loop_tasks", regionKind:"loop-body"} with iteration 0, 1, 2 — no two share an iteration; start and loop_tasks carry no parentNodeId
c2 designer Runs panel renders a nested tree pass the Studio flow-designer Runs panel (/studio/com.example.showcase/automations?surface=flow:showcase_batch_reminders, Runs toggle → expand the 17 ms run) — not the developer Flow Runs page. Measured indentation: start/loop_tasks at x=950, the three send_reminder (NOTIFY) rows at x=974 each prefixed by a ↳ marker at x=912. Screenshot taken before the DOM read
c3 tree agrees with the API pass 5 rendered steps = 5 API steps; the 3 children grouped under loop_tasks = the 3 API steps tagged parentNodeId=loop_tasks, iterations 0/1/2
c4 empty-collection contrast pass trigger with tasks: [] → run run_45d2a21f… status=completed, steps [start, loop_tasks(success)], zero steps with parentNodeId=loop_tasks

17 · automation.flow-error-handling rev 2 — PASS

clause verdict oracle evidence
c0 handled run completes, container reads success pass run run_ebd1af1b… status=completed, durationMs=7156; the try_catch step guarded_push carries status="success" (by design per #7546 — not a finding)
c1 failed try attempts recorded, ordered before catch pass steps 2–5 all {nodeId:"push", nodeType:"http", status:"failure", parentNodeId:"guarded_push", regionKind:"try", error:{code:"NODE_FAILURE", message:"http: fetch failed"}}; the catch step {nodeId:"record_failure", regionKind:"catch", status:"success"} sits at index 6, after all four
c2 retry ladder countable pass retryAttempt values 0, 1, 2, 3 — exactly maxRetries(3)+1, each status=failure. Duration cross-check: durationMs=7156 ≥ the 1 s first backoff, and 1+2+4 = 7 s matches backoffMs:1000, backoffMultiplier:2
c3 $error binds into data pass the task after the run: sync_status="failed", sync_error="Node 'push' failed: http: fetch failed"
c4 unhandled run fails loudly pass the unwrapped probe flow → status="failed" with the run-level error: "Node 'push' failed: http: fetch failed" populated; steps [start(success), push(failure)]
c5 both surface in the designer Runs panel pass the Runs panel renders the region-labelled tree: SUCCESS start START 0ms / SUCCESS guarded_pu… TRY_CATCH 7.1s / ↳ TRY → 4× FAILURE p… HTTP …ms http: fetc… / ↳ CATCH → SUCCESS r UPDATE_RECORD 101ms. The unhandled probe's run row shows Failed
c6 unhandled failure is loud in the server log pass ERROR-level lines for trigger-fired unhandled failures, e.g. "ERROR Trigger-fired run of flow 'showcase_declarative_connector_ping' failed (trigger 'record_change') — no caller holds this result and nothing retries the run…" and the same for showcase_task_completed_rest_ping — structurally identical (an outbound call failing outside try_catch). See CF-5 on why an api-triggered probe legitimately produces no such line

18 · cli.scaffold-console-first-paint rev 1 — PASS

clause verdict oracle evidence
c0 npx os validate on a pristine scaffold pass npx -y create-objectstack@latest qa-console-first-paint → ✓ Environment created!; npm install → 438 packages; npx os validate → exit 0, ✓ Validation passed (79ms), Data: 1 Objects 2 Fields · Runtime: 3 plugins
c1 console PAINTS under the documented command pass npx os dev --ui → /_console/ 200 and the shell renders: login page reads "Development instance / Sign in with the seeded dev admin: admin@objectos.ai / admin123"; after sign-in the authed home renders ("Working late, Dev Admin", Build an app, Your apps 1, Setup). Not a 404, not a white screen, not a stack trace
c2 the scaffold's own dev script agrees pass package.json scripts.dev = "objectstack dev" (no --ui). Bare npm run dev → /_console/ 200, Plugins: 35 loaded, banner advertising ➜ Console: http://localhost:3194/_console/. npx os dev --ui → 200, Plugins: 35 loaded, identical painted shell. The two invocations agree
c3 the newcomer can complete the visual check pass an identity does exist out of the box — the seeded dev admin, printed both in the boot banner and on the login page (see CF-6). Driven end to end: the starter Note object list renders its designed empty state ("Nothing here yet / Create your first record to get started"), the create form renders (Title* / Body), Save lands the record, and the list then shows 1 record with working nav / filter / group / sort affordances

Honest caveat on c3: the scaffold registers no app of its own (GET /meta/app = setup, account), so its starter object qa_console_first_pai_note has no dedicated nav entry — a newcomer reaches it through the Setup app's object route or Studio.


Reproduction rules

R-1 — Runtime-authored / org-overlaid dashboard metadata is accepted with a 200 receipt and served by no read door (P1)

Fails dashboards.strict-widget-rejects-stray-keys c5 and dashboards.global-filters-rescope c6; blocks dashboards.chart-type-matrix c0/c4/c7. Same shape as the Tier-2B view finding (R-2 in #9453), now evidenced on dashboard.

pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin -p 3193 -d file:/tmp/qa-wave/t2c-C.db
POST /api/v1/auth/sign-in/email  {"email":"admin@objectos.ai","password":"admin123"}

PUT /api/v1/meta/dashboard/qa_repro2
{"name":"qa_repro2","label":"QA Repro 2",
 "widgets":[{"id":"w1","title":"Tasks","type":"metric",
             "dataset":"showcase_task_metrics","values":["task_count"]}]}
  • expected: the dashboard is served back and renders.
  • actual: the save returns {"success":true,"version":"sha256:15b715a1…","seq":1,"state":"active","message":"Saved dashboard 'qa_repro2' (org=org_msy16t4w8wiz56vr, state=active) [seq=1]"} and then
    • GET /api/v1/meta/dashboard/qa_repro2 → {"type":"dashboard","name":"qa_repro2","lock":"none","editable":true,"deletable":true,"resettable":false} — the envelope without the item key;
    • GET /api/v1/meta/dashboard → 4 items (showcase_chart_gallery, showcase_ops_dashboard, showcase_revenue_pulse, system_overview), the authored name absent;
    • browser /_console/apps/com.example.showcase/dashboard/qa_repro2 → "Dashboard Not Found — The dashboard "qa_repro2" could not be found. It may have been removed or renamed."

Overlay variant (same defect, drives c6): re-PUT the shipped showcase_revenue_pulse document with widgets[kpi_new_accounts].filterBindings.dateRange changed to "created_at" and widgets[kpi_invoices].filterBindings = {region:false}. The save answers {"success":true,"seq":6,"message":"Saved customization overlay (org=…, state=active) — type=dashboard, name=showcase_revenue_pulse"}, and then the re-read still returns the code values ({dateRange:"signed_on", region:"sales_region"}, and no filterBindings at all on kpi_invoices), and the reloaded dashboard still queries signed_on.

Controls and trap rulings (all measured):

  • Positive control, same door: GET /api/v1/meta/dashboard/showcase_chart_gallery returns {type, name, item:{…18 widgets…}} — the read door works; it simply does not resolve org-scoped runtime-authored rows.
  • Storage control: the rows are stored — sys_metadata holds 3 type=dashboard rows, state=active, organization_id=org_msy0avxsf5eiqdj6.
  • Org confound ruled out: exactly 1 sys_organization row and it is the session's active org (get-full-organization.id = the id in the save receipt).
  • Reproduced: 4 saves across 3 names on instance A, plus a 5th on a fresh boot with a fresh DB and a different org id (instance C) — deterministic, schema-driven, not state-dependent.
  • automation-input: not applicable — pure curl + direct navigation; no clicks in the write path.
  • hydration-race: the browser leg settled 6 s and was screenshot-confirmed before any DOM read.
  • stale-console-bundle: the console stamp equals the .objectui-sha pin, and the same bundle renders the shipped dashboards correctly in the same session.

Likely owner: framework metadata read path (the same layer as #9453's view finding), not objectui — the API read door is already wrong before the console is involved.

R-2 — (withheld)

integration-system.datasource-credential-refusal-matrix clause c7 — access-control finding — detail withheld pending maintainer; held by the runner. Already filed as #9391; independently re-confirmed this round. No reproduction published.

R-3 — The notification deep link does not reach the record (P1)

Fails i18n.notification-localized-and-clears c1.

pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin -p 3191 -d file:/tmp/qa-wave/t2c-A.db
POST /api/v1/auth/sign-up/email {"email":"bravo@qa.example","password":"QaWave!2026","name":"QA bravo"}
as admin: PATCH /api/v1/data/showcase_task/<id> {"assignee":"<bravo user id>"}
   → flow showcase_task_assigned_notify fires; the row's actionUrl is "/showcase_task/<id>"
sign in as bravo in the console → open the bell (button aria-label 打开消息中心 / "Open message center")
→ click the notification entry INSIDE the popover (ref-target the entry's own <button> ancestor)
  • expected: navigation to the task's record page.
  • actual: navigation to /_console/apps/showcase_app/page/showcase_capability_map — the app's landing page. Direct navigation to the authored shape /_console/showcase_task/<id> lands on the same page.
  • Positive control: /_console/apps/showcase_app/showcase_task/record/<id> renders the record page correctly, and in zh-CN (状态 评审中; sections 概览/排期/详细信息; labels 项目/负责人/优先级/截止日期/进度) — so the detail page and its localization are fine; only the deep link is.

Traps ruled out: automation-input — the first attempt was an artifact (a bare text match hit the home page's "Needs your attention" card, not the popover) and was discarded; the recorded run used a ref-targeted click on the popover entry's own <button> ancestor, and that same click provably ran the handler because POST /api/v1/notifications/read fired from it. hydration-race — 7 s settle, screenshot before DOM. shared-browser-tab — own context, absolute origin. wrong-panel — the DOM chain was scoped inside [data-radix-popper-content-wrapper]. Reproduced twice (popover click + direct navigation).

Two candidate homes, for triage not for me to decide: the showcase fixture authors actionUrl: '/showcase_task/{record.id}' (examples/app-showcase/src/automation/flows/index.ts), and the console resolves an app-relative actionUrl to the app root rather than to /apps/<app>/<object>/record/<id>.

R-4 — GET /api/v1/search (unscoped) is 400 whenever pinyin recall is on and a federated object is registered (P1)

Surfaced under search.console-global-search; fails none of its clauses — the console always sends an objects= allowlist that excludes the offending object, so the palette and /search page are unaffected. It bites any caller using the bare documented route.

pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin -p 3191 -d file:/tmp/qa-wave/t2c-A.db
GET /api/v1/search?q=INV-1003        (admin bearer)
  • expected: 200 with hits.
  • actual: 400 {"code":"INVALID_FILTER","error":"Filter on '__search' names a column that object 'showcase_ext_customer' has no column for, so the predicate never ran. A filter on a field that does not exist can only match zero records, so the query was refused instead of answered with an empty list. …"}

Isolation and controls:

  • Reproduced twice on distinct terms (INV-1003, Warehouse).
  • Flag discriminator: the identical request on a boot with OS_SEARCH_PINYIN_ENABLED=false returns 200 with hits. The pinyin search-companion ORs {__search:{$contains:term}} for every searchable object; showcase_ext_customer (external datasource) has no __search column, and the fail-closed filter guard refuses the whole query rather than skipping that object.
  • Culprit isolated: POST /data/showcase_ext_customer/query {"search":"INV"} reproduces the same 400 alone, while POST /data/showcase_invoice/query {"search":"INV-1003"} returns 200 with rows.
  • This is the stock configuration — the showcase declares supportedLocales: ['en','zh-CN'], so pinyin defaults ON.
  • automation-input not applicable (pure curl).

Checklist-accuracy findings

  • CF-1 · i18n.surface-matrix rev 3 step s6 and its _views.emptyState knownGap are stale. They instruct the runner to record the emptyState variant as blocked(fixture) citing showcase: no view authors an emptyState, so the objects._views.<view>.emptyState translation surface has no fixture to test #7714 because "no view in examples/app-showcase authors an emptyState at all". At this pin examples/app-showcase/src/ui/views/task.view.ts does author one on the filtered urgent view (its comment block explicitly cites showcase: no view authors an emptyState, so the objects._views.<view>.emptyState translation surface has no fixture to test #7714), and the zh-CN bundle carries objects.showcase_task._views.urgent.emptyState.{title,message}. Driven this round: with the two urgent rows demoted, the view renders 暂无紧急任务 / 当前没有需要立即处理的事项。任务优先级调整为「紧急」后会出现在这里。 The variant is runnable — remove the waiver and bump revision.
  • CF-2 · i18n.surface-matrix c10's premise is wrong, and it nearly produced a false resolver FAIL. The clause asserts the feed/audit verb "localizes on the zh-CN session". It does not, and cannot: packages/plugins/plugin-audit/src/audit-writers.ts:1393 renders the summary once at write time via resolveWriteLocale(tenantId, userId) and stores the string, with an EN hardcoded fallback. Switching only the browser session locale changes nothing — a write performed from the zh session still produced Updated Task "Warehouse schema". Flipping the tenant setting (PUT /api/settings/localization {"values":{"locale":"zh-CN"}}) and writing again produced 更新了 任务 "Warehouse schema". Re-word the clause around the write locale.
  • CF-3 · i18n.strict-translation-key-rejection step s3 names both bundle-door functions as if they share an entry point. defineTranslationBundle is on the package root (@objectstack/spec); defineTranslation is not — it resolves only through the @objectstack/spec/system subpath. A scratch script importing both from the root gets defineTranslation is not a function and reads exactly like a missing guard.
  • CF-4 · search.pinyin-flag-both-sides c2 over-specifies the OFF-side expectation. It says 'zhangwei' must no longer return 张伟 with the flag off. On the stock fixture it still does — via the source column email = zhangwei@huaning.example, and likewise huaning on the account via website/billing_email. The initials forms (zw, hnkj, wf) do go to zero, which is the real discriminator. Re-target the clause at the initials, or add a pinyin-only term with no source-column spelling.
  • CF-5 · automation.flow-error-handling step s4 prescribes an api-triggered probe, whose failure c6 then cannot observe. The ERROR line exists precisely because "no caller holds this result" — for a manually/api-triggered run the caller does hold it (the trigger response carries the error), so no ERROR line is written and the log stays clean. Score c6 from a trigger-fired specimen (this round used showcase_declarative_connector_ping and showcase_task_completed_rest_ping), or have the probe fire from a record-change trigger.
  • CF-6 · cli.scaffold-console-first-paint fixture note "no seeded admin unless one is requested" is stale. At this pin bare objectstack dev seeds one automatically on an empty DB and prints 🔑 Dev admin: admin@objectos.ai / admin123 in the banner, and the console login page repeats it. c3's open question ("is there any identity a newcomer can sign in with?") has a definite answer: yes.
  • CF-7 · identity-auth.session-list-revoke c4 describes revoke_reason as a closed vocabulary. It names {idle_timeout, absolute_max, concurrent_cap, admin}, but sys-session.object.ts:154 declares free text whose description lists those only as examples ("…, …"). The observed value after revoke-other-sessions is user_revoked — legitimate, not a violation. Soften the clause or declare the select.
  • CF-8 · Correction to the wave's standing bearer-token fact (tracking: full platform-checklist regression against main @ e4e5c6e3 — five-round orchestration #9296 briefing / QA run · tier2b:browser-1 (21/21) · e4e5c6e3 · 2026-08-18 · 7 PASS / 12 PARTIAL / 2 FAIL #9453 item 1). "The console authenticates by bearer token in localStorage, not by cookie" is true for the data client but incomplete: a real form sign-in also sets a better-auth.session_token cookie, and at least one route family requires it. Driving with a localStorage token alone made POST /api/v1/storage/upload/presigned answer 401 AUTH_REQUIRED while GET /auth/get-session on the same page returned 200 — a convincing fake "avatar upload is broken". With a real form sign-in the whole chain is 200. Runners driving any storage/upload surface must sign in through the form.

Fixture gaps

  • FG-1 — No passwordless/SSO-only user on stock showcase → self-service-profile-password c5 stays blocked. (Restates the item's own knownGap; unchanged at this pin.)
  • FG-2 — Job probes are unauthorable at runtime. job is allowRuntimeCreate=false, allowOrgOverride=false, so the interval/once/failing/timing-out/disabled/handler-missing scratch jobs job-scheduled-run prescribes need source + redeploy. Four of its six clauses (c0 cron arm, c2, c3, c4) are blocked on this alone. Candidate: ship the specimens in examples/app-showcase/src/automation/jobs/ behind an env flag.
  • FG-3 — No dataset-sourced dashboard filter anywhere in the showcase. Both revenue-pulse and ops-dashboard declare static inline options arrays, so global-filters-rescope c8's server-GROUP-BY half has nothing to observe (opening the select fires 0 requests). One filter sourced from a dataset would close it.
  • FG-4 — The two translated _actions on showcase_task (showcase_recalc_estimate, showcase_recalc_selection) are not surfaced on the list row menu, the list toolbar, or the record page, so i18n.surface-matrix c5 (action confirm/success copy) has no reachable specimen. Either surface one of them, or add an _actions translation for an action that is reachable (Mark Done / Reassign… / Log Time / Archive are all EN with no bundle entry).
  • FG-5 — No turso driver in the tree (@objectstack/driver-turso not installed), so datasource-credential-refusal-matrix c6's live connect probe after the mask round-trip had to fall back to a stored-row oracle.
  • FG-6 — The showcase app declares no apps.showcase_app translation entry, so the nav labels variant of i18n.surface-matrix is only demonstrable on the platform setup/account apps. Correct as fallback behaviour, but it means the app-authored nav surface is untested.
  • FG-7 — No seeded persona owns any showcase_invoice row, so a member bound to contributor sees 0 of 12 rather than a partial slice. console-global-search c5 still passes (invisible row absent, no leaked count, object provably searched), but a sharper "sees 11, not INV-1003" contrast needs one seeded invoice owned by a signup-able persona.

Derived observations (no clause failed)

Close-out inputs for the PM

Per this round's brief the run record carries qa-run + tests only; defect extraction is the PM's close-out duty. Cards this round warrants:

  1. R-1 — runtime-authored/org-overlaid dashboard metadata never served. Very likely the same root cause as QA run · tier2b:browser-1 (21/21) · e4e5c6e3 · 2026-08-18 · 7 PASS / 12 PARTIAL / 2 FAIL #9453's R-2 (view); consider one card covering both metadata types.
  2. R-3 — notification deep link does not reach the record.
  3. R-4 — unscoped GET /api/v1/search 400s whenever pinyin recall is on and a federated object is registered.
  4. R-2 — already filed as The datasource-admin HTTP family is reachable unauthenticated, read and write — mounts outside the enforceAuth seam #9391; nothing to extract, nothing published here.

CF-1…CF-8 and FG-1…FG-7 close out through this wave's anchor card (#9296), not as separate cards.

Wave anchor: #9296.

Activity

  1. added a commit that references this issue on Aug 18, 2026
  2. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    Close-out — extracted. Wave #9296, executed under #9480. Four-way FAIL disposition applied to this record's own evidence; nothing re-judged, nothing re-run.

    FAIL disposition (4 FAILs + 1 clause-less defect)

    1 · integration-system.datasource-credential-refusal-matrix c7 → (a) product defect, access-control → #9391, linked not refiled. Existence and clause only; no endpoint, payload or reproduction is restated here or anywhere else on GitHub, per the disclosure ruling at the top of #9296. The item's other seven clauses pass on this round's own evidence.

    2 · dashboards.strict-widget-rejects-stray-keys c5 and 3 · dashboards.global-filters-rescope c6 → (a) one product defect → #9454, linked not refiled. Both are the runtime-authored / org-overlaid dashboard read-door failure (R-1) — accepted with a 200 state:'active' receipt, served by no door, rendered as "Dashboard Not Found". The card was filed from Tier-2B's view finding and widened to dashboard with this round's evidence: 5 saves, 4 names, 2 boots including a fresh DB with a different org id, with the storage, read-door and org confounds each ruled out by a control. It also blocks chart-type-matrix c0/c4/c7, which are recorded blocked(product-bug) rather than as separate failures — correctly, and no card is owed for them.

    4 · i18n.notification-localized-and-clears c1 → (a) product defect → #9468, linked not refiled. The bell entry marks read correctly (POST /notifications/read → 200) and then navigates to the app landing page instead of the record. Both candidate homes named in this record — the showcase fixture's actionUrl: '/showcase_task/{record.id}' and the console's app-relative resolution — are carried on the card; the routing decision is triage's, not the runner's. If triage rules it a console resolution defect, the fix re-homes to objectstack-ai/objectui under the file-at-destination rule; the card says so.

    5 · R-4, no clause fails → (a) product defect → #9469, linked not refiled. Unscoped GET /api/v1/search answers 400 INVALID_FILTER on the stock configuration: the pinyin search companion ORs {__search:{$contains:…}} across every searchable object and the federated showcase_ext_customer has no __search column, so the fail-closed guard refuses the whole query. Isolated by a flag flip and by the culprit object reproducing it alone. Recorded honestly as failing no clause — the console always sends an objects= allowlist, so search.console-global-search is pass and the bare documented route is what breaks.

    PARTIAL gaps and corrections — no cards, by rule

    The 8 checklist-accuracy findings (CF-1…CF-8) and 7 fixture gaps (FG-1…FG-7) are parked on the wave anchor per the PM's 2026-08-18 comment. Two standing wave facts this round proved wrong are already corrected on #9296 — the bearer-token fact (a real form sign-in also sets a better-auth.session_token cookie, and the storage/upload chain needs it; driving with a localStorage token alone manufactures a convincing fake "avatar upload is broken"), and the i18n.surface-matrix feed-verb premise (the activity verb is rendered once at write time from the tenant locale, so the reader's session locale cannot change it). Both near-misses were caught by the rule-2 self-check, which is the protocol earning its cost twice in one round.

    Extracted: #9454 #9468 #9469
    Parked-on: #9296


    Generated by Claude Code

  3. added a commit that references this issue on Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions