Repository navigation
QA run · priority:P0 + 17.6-risk (42/42) · 617f25f8 · 2026-10-02 · 30 PASS / 3 PARTIAL / 9 FAIL / 0 BLOCKED / 0 NOT-RUN #21330
Description
17.6.0 release verification run: the standing P0 smoke plus the 23 checklist items most exposed to 17.6.0's changes, against the 17.6.0 release build. Text only; the durable truth stays in docs/qa/platform-checklist/areas/.
Environment fingerprint
- Framework (subject):
objectstack-ai/objectstack617f25f8(chore: version packages (#20639)), the commit@objectstack/*@17.6.0was published from (tags@objectstack/<pkg>@17.6.0). Built in a dedicated worktree:pnpm install --frozen-lockfile && pnpm build→Tasks: 72 successful, 72 total. - Console: objectui pin
.objectui-sha=31971ff1e28f, built bypnpm objectui:buildunder Node v22.22.2 (the container's v22.22.0 is below the pinned objectui'sjsdomengine floor; environment note).packages/console/dist/.objectui-sha= the pin. Single-zod canary over the shipped assets:exactly one zod version literal {major:4,minor:6,patch:5}(2450 files read). Spec-injection check: the bundle carries this tree's@objectstack/speconly. - Checklist: items,
RUNNER.mdandscripts/checklist-select.mjstaken frommainat393ae878; the revision of every item run is recorded below. - App / boots:
examples/app-showcase,OS_PORT=<p> objectstack dev --ui --seed-admin -p <p> -d file:/tmp/qa1760/<lane>/<item>.db— one file DB and one port per item, lanes on disjoint ranges 41000–42199 with the egress proxy port 41333 excluded. BannerSeeds: com.example.showcase 132 rows;Flows: 30 flow(s) 20 bound … 7 draft. Members minted by admininvite-member+sign-up/email(stock audience postureinvite_only), identity proved byGET /auth/get-sessionbefore each persona cell. - Execution: 9 parallel runner subagents (L1–L9) + 4 independent verifiers (V1–V4, RUNNER rule 7), Claude Code session
session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-02 03:55–05:25Z. Environment prep ≈ 30 min (framework build 7m40s, console build ≈ 15 min); per-lane runner time 14–41 min, of which 4–8 min env prep each.
Scope
One record for one release-verification selection, by maintainer direction: priority:P0 (node scripts/checklist-select.mjs priority:P0 --json → 19 runnable) + 23 named 17.6-risk items = 42 items. Hidden as blocked (not run): access-security.no-active-org-session-semantics. Planned (not run, not counted): records-forms.picklist-shared-across-objects, records-forms.picklist-org-append — see "Checklist-accuracy findings".
| item | rev | verdict | pin (automated.ref, run from the subject tree) |
|---|---|---|---|
| access-security.rls-both-sides | 5 | PASS | verify --rls exit 0; private-owd 5/5; invoice-seed-isolation 12/12 |
| access-security.write-path-guards | 3 | PASS | owner-anchor 9/9 · static-readonly 3/3 · readonly-when-parent 5/5 · objectql static-readonly-strip 18/18 |
| access-security.crud-permission-matrix | 4 | FAIL | persona-matrix 46/46 · permission-zoo 13/13 |
| access-security.owd-sharing-matrix | 3 | PASS | private-owd 5 · public-read-owd 3 · controlled-by-parent 4 · invoice-cbp 4 |
| access-security.anonymous-deny-surfaces | 3 | PASS | anonymous-deny-surfaces 61/61 |
| ai.mcp-stdio-fail-closed | 4 | PASS | mcp plugin.test.ts 17/17 |
| api-backend.query-contract-matrix | 3 | PASS | objectql engine + filter-array-lowering 196/196 · rest request-schema-gate 16/16 |
| api-backend.packaged-action-disabled-dispatch | 3 | PASS | action-activation-dispatch 11/11 · activation-ledger-reach 11/11 |
| automation.packaged-flow-disable-durable | 2 | PASS | none |
| platform-core.seed-integrity | 3 | PASS | none |
| cli.dev-boot-contract | 3 | PASS | unified-db-resolution 7/7 · resolve-project-database 26/26 |
| platform-core.boot-health | 6 | PASS | none |
| integration-system.datasource-credential-refusal-matrix | 2 | PARTIAL | none (service-datasource pins 86/86 alongside) |
| platform-core.console-login | 4 | PASS | none |
| platform-core.nav-surfaces-render | 5 | PASS | showcase-smoke.spec.ts 49 passed (4.8m) |
| platform-core.builtin-apps-nav-render | 4 | PASS | none |
| records-forms.crud-roundtrip | 8 | PARTIAL | none |
| approvals.account-app-entry | 2 | FAIL | none |
| studio-authoring.first-run-loop | 3 | PASS | none |
| access-security.fls-mask-and-strip | 3 | PASS | permission-zoo + fls-read-mask-strip + client-liaison 26/26 |
| access-security.record-access-explain | 2 | PASS | permission-zoo explain case (in the 26/26) |
| access-security.audit-log-browser | 2 | FAIL (stale clause) | none |
| access-security.public-form-intake | 2 | FAIL | showcase-public-form + read-back-masking + config-change-audit pass · rest public-form-routes 23/23 |
| access-security.packaged-flow-write-door-parity | 1 | PASS | (unnamed) packaged-flow-write-door-parity.dogfood 5/5 |
| api-backend.filter-comparand-conformance | 2 | FAIL (stale clause) | engine-comparand-type-door 12/12 · driver conformance memory 23 / sqlite-wasm 22 / mongodb 22 / turso 22 / sql 46 (+2 live-DB skips) |
| api-backend.aggregate-contract-matrix | 1 | PASS | none |
| api-backend.date-range-preset-matrix | 1 | FAIL (assertion defect) | none |
| dashboards.cube-query | 3 | PASS | none (runtime analytics-anonymous-deny 29/29 alongside) |
| dashboards.dataset-report-authoring | 1 | PASS | dashboard-designer-roundtrip 3/3 |
| dashboards.global-filters-rescope | 2 | PASS | none |
| dashboards.strict-widget-rejects-stray-keys | 2 | PASS | none |
| records-forms.field-type-matrix | 2 | PASS | field-zoo-roundtrip + value-shape 91/91 |
| records-forms.import-transform-matrix | 1 | FAIL (stale clause) | none |
| records-forms.named-import-mapping | 1 | PASS | none |
| automation.packaged-flow-subflow-disable-refusal | 1 | PASS | flow-activation-ledger 47/47 |
| automation.packaged-flow-clone-contract | 1 | FAIL | automation-flow-clone 18/18 |
| automation.flow-toggle-kill-switch | 2 | PASS | none |
| automation.setup-packaged-automation-board | 2 | PARTIAL | setup-packaged-automation-nav 4/4 |
| integration-system.datasource-admin-lifecycle | 3 | PASS | admin-routes + envelope + credential-migration + route-ledger 86/86 |
| platform-core.metadata-authoring-roundtrip | 2 | PASS | package-first-authoring 5/5 |
| studio-authoring.view-authoring-live | 1 | FAIL (assertion defect) | none |
| studio-authoring.custom-page-source-tiers | 1 | PASS | none |
Totals: 30 PASS / 3 PARTIAL / 9 FAIL / 0 BLOCKED / 0 NOT-RUN. Of the 9 fails, 4 are product defects (2 of them on P0 items, both already tracked) and 5 are checklist clauses that 17.6.0 or earlier design deliberately contradicts. Every P0 fail and every new product-defect candidate was re-derived by an independent verifier from a fresh reproduction.
vs #21056 (2026-10-01, ef1ed17f, Console db11afd4967c: 13 PASS / 6 FAIL)
| # | item · clause | #21056 | 17.6.0 (617f25f8, Console 31971ff1e28f) |
|---|---|---|---|
| F1 | access-security.rls-both-sides · A6 | fail (#21052, withheld) | FIXED — the standing probe now holds (fix 99398542, #21104, in the subject tree); item PASS |
| F2 | access-security.crud-permission-matrix · A1 | fail (#21057) | STILL FAILING — same 403 "outside the delegated subtree"; seeded business units still carry organization_id: null (verifier V1 confirmed) |
| F3 | api-backend.query-contract-matrix · A6 | fail (stale clause) | RECLASSIFIED → PASS — clause re-pointed in rev 3 (#21060 / #21111); 17.6.0 answers exactly the re-pointed codes |
| F4 | integration-system.datasource-credential-refusal-matrix · N2 | fail (#21058) | FIXED by c6954d6 (#21133) — both #21056 bodies now 400 DATASOURCE_ADMIN_ERROR naming config.url / config.username |
| F5 | approvals.account-app-entry · A7 | fail (objectui#11326) | STILL FAILING at the pin — now English on every path, SPA included; fixed upstream in objectui d0fba91aa, which is not an ancestor of 31971ff1e28f (verifier V4) |
| F6 | studio-authoring.first-run-loop · A1 | fail (objectui#11327) | FIXED by the pin (one zod instance, objectui#11353) — the New package dialog renders all fields and POST /api/v1/packages → 201 |
Per-clause verdicts
Clauses are numbered A1..An (acceptance) and N1..Nn (negative) in array order. Evidence is server truth unless it says screenshot.
P0
access-security.rls-both-sides (rev 5) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | member A lists exactly A's two notes, B exactly B's one (2 runs) |
| A2 | pass | foreign by-id GET → 404 RECORD_NOT_FOUND both ways |
| A3 | pass | foreign PATCH → 403 PERMISSION_DENIED; admin re-read byte-identical |
| A4 | pass | admin list contains every id the run created |
| A5 | pass | verify --rls: per-persona 21 PROVEN (21 consistent, 0 HOLES) · 3 NOT PROVEN; total all personas: 38 PROVEN (38 consistent, 0 HOLES) · 226 NOT PROVEN; 10 of 10 declared position(s) probed; CRUD tail 15 verified, 0 gaps, 0 FAILED, 1 needs-fixture, 8 skipped |
| A6 | pass | the standing probe holds on fresh personas (2 runs); the 3 skipped objects are benign (no plain-text probe field / external read-only) |
| N1–N3 | pass | forged PATCH 403, row unchanged |
access-security.write-path-guards (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | POST contact lead_score → 201, x-objectstack-dropped-fields: lead_score;reason=readonly, stored null |
| A2 | pass | PATCH lead_score:99 → 200, same header, null after |
| A3 | pass | member POST owner_id=<other> → 403 "requires the transfer grant"; count 0 |
| A4 | pass | self-owner insert 201; transfer / disown PATCH 403, owner unchanged |
| A5 | pass | createMany ×3 → every owner_id = caller |
| A6 | pass | paid-invoice locked fields dropped reason=readonly_when; mixed updateMany drops per row (results[0].droppedFields) |
| A7 | pass | updateMany with options.continueOnError:true → violating row VALIDATION_FAILED, compliant row persisted |
| N1 | pass | admin forge / transfer 201 / 200 persisted |
| N2 | pass | objectql pin "isSystem seeds the readonly column" (no live evidence: stock seeds never set lead_score) |
access-security.crud-permission-matrix (rev 4) — FAIL
| # | verdict | evidence |
|---|---|---|
| A1 | fail | 87 allow cells green in the pin; delegate in-subtree sys_user_position create → 403 "outside the delegated subtree" (3 boots + verifier 2 boots) |
| A2 | pass | pin: 77 deny cells 403 PERMISSION_DENIED; delegate C/U/D on sys_business_unit, sys_business_unit_member, sys_permission_set, sys_position 403; sys_user create/delete 405 |
| A3 | pass | denied creates leave 0 marker rows |
| A4 | pass | auditor lists and reads C1's line by id |
| A5 | pass | ops PATCH of a foreign announcement 200 persisted; member 403 unchanged |
| A6 | blocked(dependency #21158) | anonymous POST and GET on /data/showcase_inquiry → 401 before and after binding guest → showcase_guest_portal; 17.6.0's deny baseline gives anonymous callers no channel to a permission set until #21158; the public-form lane still answers 201 |
| A7 | pass | 164 pin cells + 24 delegate sys_* cells, one verdict each |
| N1–N2 | pass | admin payload controls 201; no withheld cell answered 2xx |
access-security.owd-sharing-matrix (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | private: B list excludes, by-id 404, PATCH 403 unchanged; owner 200 |
| A2 | pass | public_read: B lists + reads 200, PATCH 403 unchanged |
| A3 | pass | ops (modifyAll) PATCH of foreign announcement 200 |
| A4 | pass | public_read_write: contributor PATCH 200; member 403 |
| A5 | pass | controlled_by_parent: list excludes, by-id 404, PATCH 403; owner 200 |
| A6 | pass | auditor (VAMA) lists + reads C1's line |
| A7 | pass | all four OWD models driven per persona and verb |
| N1–N2 | pass | forged public_read PATCH 403; own-line probes 200 |
access-security.anonymous-deny-surfaces (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | data, meta, actions, automation _status, batch, security/explain, 3 analytics faces → 401 UNAUTHENTICATED |
| A2 | pass | anonymous action on a nonexistent id → 401, not 404 |
| A3 | pass | each body in exactly one envelope family |
| A4 | pass | analytics: anonymous 401 incl. malformed bodies; member 200, malformed 400 VALIDATION_FAILED |
| A5 | pass | member data / meta 200 |
| A6 | pass | anonymous GET /api/v1/forms/contact-us → 200 |
| A7 | pass | all 7 variant families probed |
| N1 | pass | anonymous automation DELETE → 401; flow still served |
ai.mcp-stdio-fail-closed (rev 4) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | keyless → exit 1 "OS_MCP_STDIO_API_KEY is not set … Refusing to start an unscoped stdio server (ADR-0101)"; nothing listening |
| A2 | pass | unknown key → exit 1 "did not resolve to a valid identity" |
| A3 | pass | --log-level info: "principal-bound to OS_MCP_STDIO_API_KEY identity … (RLS/FLS/tenant applied)" |
| A4 | pass | member MCP invoice ids = member REST ids; foreign row "not found" / 404; liaison budget/spent absent on both |
| A5 | pass | member aggregate draft=1 on both doors; admin 3/5/5 |
| A6 | pass | liaison sum / count_distinct / groupBy budget → "Field read denied: not permitted to aggregate [budget]"; REST 403 |
| A7 | pass | mid-session revoke → "MCP stdio identity is no longer valid"; REST 401 |
| A8 | pass | stdio off → clean boot, health 200 |
api-backend.query-contract-matrix (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | 18 operator variants × 3 doors (POST $-object, POST AST, GET $filter) = the expected id sets over a 14-row baseline |
| A2 | pass | $top/$skip and limit/offset exact slices |
| A3 | pass | $select / fields exact keys; dotted account.name → 400 INVALID_FIELD naming expand |
| A4 | pass | asc/desc match; bad field → 400 INVALID_SORT |
| A5 | pass | contributor gets the raw contact id, admin the expanded object |
| A6 | pass | POST scalar $nin → 400 VALIDATION_FAILED at query.where.status.$nin (both spellings); GET → 400 INVALID_FILTER |
| A7 | pass | $pageSize → 400 UNSUPPORTED_QUERY_PARAM; unknown field → 400 INVALID_FIELD |
| A8 | pass | $in [] → 0 rows; $nin [] → 14/14 |
| A9 | pass | every variant recorded |
| N1 | pass | {"limit":"ten"} → 400; undeclared key → 400 INVALID_FIELD |
api-backend.packaged-action-disabled-dispatch (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | disabled → 409 ACTION_DISABLED; 0 tasks changed; ledger row has no organization_id |
| A2 | pass | disabled + bad params → 409; re-enabled → 400 "Invalid action params…" |
| A3 | pass | 409 with a bogus recordId |
| A4 | pass | D4-refused action 403 byte-identical enabled vs disabled |
| A5 | pass | HTTP MCP run_action disabled → isError ACTION_DISABLED 409; re-enabled ok |
| A6 | pass | survives restart; re-enable updates the same row |
| A7 | pass | flow-type action 409 |
| N1–N3 | pass | anonymous 401; absent row = active |
automation.packaged-flow-disable-durable (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | toggle → exactly one sys_metadata_activation row, no organization_id key |
| A2 | pass | trigger → 409 FLOW_DISABLED naming the ledger and both remedies |
| A3 | pass | two cold boots: /_status {enabled:false, bound:false}; boot line (at OS_LOG_LEVEL=info) names it unbound |
| A4 | pass | runs 1 → 1 across both boots |
| A5 | pass | re-enable updates the same row id; runs → 2 |
| N1–N4 | pass | row present; restart does not re-arm; not deleted |
platform-core.seed-integrity (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | 19 seed sets / 132 rows, every live count equal to the authored seed |
| A2 | pass | Specimen — Full values verbatim (time canonicalised 14:30:00, password masked) |
| A3 | pass | banner Seeds: com.example.showcase 132 rows, no dropped-record line |
| A4 | pass | replay against the same DB: identical sweep |
| A5 | pass | deliberately unseeded fields null |
| N1–N2 | pass | no zero counts; no growth on restart |
cli.dev-boot-contract (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | health + ready 200; /_console/ login form; form sign-in → /_console/home with cookie + token |
| A2 | pass | restart with --admin-password changed99: old password 200, new 401 |
| A3 | pass | -d, OS_DATABASE_URL, --database-driver memory, nothing-chosen (persists), legacy dev.db (one notice, no empty sibling) each resolve as specified; config tier by pin |
| A4 | pass | --fresh tempdir used, gone after SIGINT; #5594 carve-out survives |
| A5 | pass | busy port → ↪ server bound to port 41532 (requested 41531); health 200 there |
| A6 | pass | stale dist/objectstack.json warned with newest source + fix; boots |
| A7 | pass | no config → exit 1 with the remedy line |
| N1–N3 | pass | existing DB not wiped; legacy file read; shift reported |
platform-core.boot-health (rev 6) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | health 200 {"status":"ok","version":"17.6.0"} in 38.7 s |
| A2 | pass | ready 200 {"status":"ready","state":"running"} |
| A3 | pass | 0 ERROR lines; the only ⚠ are the two stock NOT bound — disabled by deployment policy lines |
| A4 | pass | /_console/ 200 HTML; apps [showcase_app, setup, account] |
| A5 | pass | 12 authored data-nav objects = 12 served |
| N1–N2 | pass | no seed rejection, no ERROR |
integration-system.datasource-credential-refusal-matrix (rev 2) — PARTIAL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | 15 inline-credential shapes refused at all three doors (build exit 2 / POST /datasources 400 / PUT /meta 422); every refusal names external.credentialsRef |
| A2 | blocked(environment) | credentialsRef shapes publish (build + admin door 201); "connects" unprovable — no real Postgres / Mongo in the sandbox |
| A3 | pass | 0 planted values across every admin-door and meta-door body |
| A4 | pass | hasSecret / redactedConfigKeys distinguish set from unset |
| A5 | pass | legacy alias rows (planted with the server stopped) redacted on both doors |
| A6 | pass | unknown driver's password / authToken absent on read |
| A7 | pass | echoed redacted config preserves the stored credential |
| A8 | pass | member 403 on every read door incl. external_catalog and sys_secret; anonymous 401 |
| N1 | pass | 0 planted values |
| N2 | pass | fixed since #21056: both mongo pairings → 400 DATASOURCE_ADMIN_ERROR naming config.url / config.username |
| N3–N7 | pass | doors agree; aliases and unknown drivers redacted |
platform-core.console-login (rev 4) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | form sign-in 200 → /_console/home launcher; cookie + localStorage token (2 runs) |
| A2 | pass | after reload meta/app 200, no login redirect |
| A3 | pass | after POST /auth/sign-out the old bearer → 401 |
| A4 | pass | navigation after expiry → /_console/login?redirect=… |
| A5 | pass | re-auth returns to the same route; data GET 200 |
| N1–N2 | pass | wrong password 401, visible alert, no cookie / token |
platform-core.nav-surfaces-render (rev 5) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | pin 49/49; all chart surfaces pass the bounded poll |
| A2 | pass | 49 served destinations (+1 separator) = SURFACES exactly |
| A3 | pass | bad object route → named "Object Not Found" inside the live shell, 0 pageerrors (2 loads) |
| A4 | pass | no render failure encountered |
platform-core.builtin-apps-nav-render (rev 4) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | Setup 45/45 and Account 8/8 render for admin; Browse Marketplace catalog content blocked(environment) — named "Failed to load marketplace · Forbidden" (egress to cloud.objectos.ai denied) |
| A2 | pass | all 9 Setup groups; ?id=studio → items [] |
| A3 | pass | member: Setup → "This app can't be opened"; Account opens |
| A4 | pass | member meta/app?id=setup → items [] |
| A5 | pass | Account nav 8 vs 8 |
| A6–A7 | pass | gated / off-capability entries absent as specified |
records-forms.crud-roundtrip (rev 8) — PARTIAL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | form create → 201, read back verbatim |
| A2 | pass | PATCH changes only annual_revenue (+ updated_at) |
| A3 | pass | delete 200; filtered 0; by-id 404 |
| A4 | pass | list reflects each mutation after full reload |
| A5 | pass | History shows CREATE and UPDATE with option labels and the real actor |
| A6 | pass | CJK name round-trips byte-exact; $search=华宁 finds it |
| A7 | pass + blocked(fixture) | clone → new id, values copied, autonumber / formula re-derived, cloner owns; readonly-column half unobservable on the stock seed |
| A8 | pass | clone of an RLS-invisible source → 404 RECORD_NOT_FOUND; admin count unchanged |
| N1–N4 | pass | empty required blocked client-side; server 400 required / invalid_format, counts unchanged |
approvals.account-app-entry (rev 2) — FAIL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | dist (stamp = pin) carries the approvals:inbox registration |
| A2 | pass | Inbox → Approvals renders tabs, rows, keyboard hints and drawer |
| A3 | pass | non-admin persona: apps [showcase_app, account], inbox entry present |
| A4 | pass | Approve → 200; tally 0/2 → 1/2, persona dropped from pending |
| A5 | pass | ?request= cold load opens the drawer |
| A6 | pass | record link keeps the com.objectstack.account segment |
| A7 | fail | zh-CN selected: nav 待我审批 (server-served), but h1 "Approvals Inbox", tabs "My Pending / Submitted by me / All", badge "Pending" on every path; only i18n-locale-en-*.js fetched |
| N1–N2 | pass | inbox rendered every load; Setup absent |
studio-authoring.first-run-loop (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | fixed since #21056: New package dialog renders Display name / Package ID / Object namespace / Version / Type / Description; Create → POST /api/v1/packages 201, same document |
| A2 | pass | published object served with exactly the 3 authored status options |
| A3 | pass | Records grid add → row in GET /data/repairs_repair_ticket |
| A4 | pass | app in the launcher; list shows the label chip, not the raw value |
| A5 | pass | one "Server is ready", one PID throughout |
| A6 | pass | authoring into the read-only package → 422 WRITABLE_PACKAGE_REQUIRED / 403 NOT_OVERRIDABLE / 403 ITEM_LOCKED; nothing stored |
| N1–N2 | pass | no silent acceptance; labels not raw values |
17.6-risk items
access-security.fls-mask-and-strip (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | contributor PATCH name → 200, persisted |
| A2 | pass | contributor PATCH budget → 403 "Field write denied … [budget]"; value and updated_at unchanged |
| A3 | pass | admin PATCH budget 200; budget_remaining recomputes |
| A4 | pass | screenshot: contributor's Edit dialog shows Budget / Spent disabled, name enabled |
| A5 | pass | liaison: by-id, list, select, $select all omit budget/spent/budget_remaining keys; filter / sort / cross-field {"$field":"budget"} → 403 (17.6.0 #20954); console hides the Financials section; admin sees all |
| N1 | pass | the UI lock is backed by the server 403 |
access-security.record-access-explain (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | Studio › Access › Explain access lists the 10 layers with a THIS RECORD block, "Decided by: View/Modify All bypass" |
| A2 | pass | POST /security/explain → record:{visible:true, decidedBy:"vama_bypass"} |
| A3 | pass | explain verdicts agree with each persona's own GET (200 / 404) |
| A4 | pass | plain member → visible:false, decidedBy:"sharing", no EXPLAIN_FAILED |
| A5 | pass | the panel's response equals the direct API call layer by layer |
| N1 | pass | member explaining another user → 403 (needs manage_users) |
access-security.audit-log-browser (rev 2) — FAIL (stale clause)
| # | verdict | evidence |
|---|---|---|
| A1 | fail (stale) | login row (actor = member, sys_session) and config_change row served; the admin's delete row → 0 rows on the data API, although stored with correct attribution in the telemetry DB — 17.6.0 30c530e5 (#21194) |
| A2 | pass | /apps/setup/system/audit-log: page rows equal the API rows |
| A3 | pass | Action select config_change re-issues GET …/sys_audit_log?$filter={"action":"config_change"} |
| A4 | pass | drawer shows separate Before / After JSON panels (checked on an update row — no delete row is reachable in 17.6.0) |
| A5 | pass | page ↔ API reconcile both directions |
| A6 | pass | POST / PATCH / DELETE → 405 OBJECT_API_METHOD_NOT_ALLOWED; nothing forged |
| N1–N2 | pass | every event wrote a row; writes refused |
access-security.public-form-intake (rev 2) — FAIL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | anonymous GET /api/v1/forms/contact-us → 200 with exactly name/email/company/message |
| A2 | pass | forged owner/org/created_by never land |
| A3 | pass | forged status / source → landed new / web |
| A4 | pass | anonymous submit 201, row persisted (17.6.0's deny baseline does not reach this route) |
| A5 | pass | GET /forms/contact-us/lookup/owner_id → 404 ENDPOINT_NOT_FOUND |
| A6 | pass | anonymous /_console/f/contact-us submit → 201, "Thanks!" panel (screenshot) |
| A7 | fail | detail withheld pending maintainer (RUNNER rule 2); reproduced twice by the runner and twice by verifier V1 |
| N1–N2 | pass | no forged anchors; __proto__ body inert |
access-security.packaged-flow-write-door-parity (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | PUT /meta/flow/showcase_urgent_task_alert → 403 NOT_OVERRIDABLE (?package= → 403 ITEM_LOCKED); unchanged |
| A2 | pass | PUT /automation/showcase_urgent_task_alert → 403 NOT_OVERRIDABLE, same lock message — the item's "EXPECTED FAIL" is stale since 4b45afae (#20817) |
| A3 | pass | DELETE /automation/… → 403 NOT_OVERRIDABLE; GET still 200 |
| A4 | pass | GET byte-identical to the pre-probe capture; flow still fires |
| N1–N3 | pass | refusals quote the package lock; doors scored separately |
api-backend.filter-comparand-conformance (rev 2) — FAIL (stale clause)
| # | verdict | evidence |
|---|---|---|
| A1 | pass | string / number / boolean / null / ISO date strings answer the local computation; bigint and Date by pin |
| A2 | pass | object comparand → 400 quoting the accepted types; no rows |
| A3 | pass | pin: a bigint beyond ±2^53 is refused (REST not applicable, as the item records) |
| A4 | pass | relation / virtual / scalar dotted heads → 400 INVALID_FIELD with the class prescription |
| A5 | fail (stale) | POST /query → 400 VALIDATION_FAILED at query.where.f_number.$eq; GET $filter and the engine → 400 INVALID_FILTER — the #20116 door split (cfc3bcf1 #20247, dd1b8031 #20325), same as query-contract-matrix rev 3 |
api-backend.aggregate-contract-matrix (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | all 6 functions grouped and ungrouped = local computation; count(field) skips nulls |
| A2 | pass | day / week / month / quarter / year buckets reconcile; ISO Monday weeks; nulls share one null key |
| A3 | pass | empty set: counts / sum 0, avg / min / max null, grouped [] |
| A4 | pass | array_agg / string_agg → 400 with the retirement text; median → 400 enum; never 500 |
| A5 | pass | contributor grouped count = its own baseline; admin full |
| A6 | pass | variant record complete; 17.6.0 refusals spot-checked (groupBy json, sum text, count_distinct tags → 400 INVALID_FIELD) |
api-backend.date-range-preset-matrix (rev 1) — FAIL (assertion defect)
| # | verdict | evidence |
|---|---|---|
| A1 | pass | all 13 presets: macro query = literal-window query = local computation (clock 2026-10-02 UTC recorded first; boundaries checked on both sides) |
| A2 | pass | 13 discriminating rows |
| A3 | fail (assertion) | bare {"signed_on":"today"} (the step-5 example) → 400 INVALID_FILTER with the temporal-door message, not bareDateRangePresetComparandMessage; the preset message appears only in ordering positions ($gte:"this_week" → 400 VALIDATION_FAILED with it) — by design (18.filter-preset-ordering-comparand-refused.ts); the guarded failure (200 with zero rows on a date field) never occurs |
| A4 | pass | unknown preset → 400 INVALID_FILTER / FILTER_TOKEN_UNKNOWN |
dashboards.cube-query (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | /analytics/meta?cube=showcase_delivery lists the measures and dimensions (no done_rate, as rev 3 expects) |
| A2 | pass | count by status = /data groupBy = raw tally |
| A3 | pass | SUM / AVG(estimate_hours) by status reconcile; single-table GROUP BY |
| A4 | pass | cube aggregate runs in the caller's execution context |
| A5 | pass | slot served; empty-slot arm by pin (29/29); anonymous 401 (17.6.0) |
| A6 | pass | filters / query / format bodies → 400 with their retirement hints |
| N1–N4 | pass | no 200-empty, no 500 |
dashboards.dataset-report-authoring (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | draft PUT + publish 200; widgets carry dataset + values only |
| A3 | pass | screenshot: metric 10, 5 bars, 4 donut slices |
| A4 | pass | summary totals = /data SUM by status (244.0) |
| A5 | pass | matrix cells = /data groupBy [status, priority] |
| A6 | pass | joined blocks partition per measure (220 + 24 = 244 est_hours; 8 + 2 = 10 tasks) |
| A7 | pass | chart bars match the table |
| A8 | pass | summary / matrix / joined / tabular each rendered |
| N1–N2 | pass | joined block type → 422; dangling dataset → publish 422 |
dashboards.global-filters-rescope (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | region and custom date window re-query every bound widget with the right field per object |
| A3 | pass | opted-out widget never re-queries |
| A4–A5 | pass | widget values = /data counts under the same filters |
| A6 | pass | the composing status tile (kpi_awaiting_review) = /data (2 / 0) |
| A7–A9 | pass | Studio field override persists and drives the widget; Reset only when dirty; optionsFrom list from a server GROUP BY |
| N1–N4 | pass |
dashboards.strict-widget-rejects-stray-keys (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | 11 legacy widget keys → 422 naming the key, each with the dataset prescription |
| A3 | pass | chartFlavour → 422 echoing the key; near-keys get did-you-mean |
| A4 | pass | layer-misplaced keys get layer guidance |
| A5 | pass | no rejected PUT replaced the last good body |
| A6 | pass | corrected widget renders (screenshot) |
| N1–N2 | pass | metadata-editor Source tab edit → autosave 422, nothing persisted |
records-forms.field-type-matrix (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | pin 91/91 |
| A2 | pass | screenshot then labels: every f_* field renders its own widget |
| A3 | pass | bad option → 400 invalid_option; missing name → 400 required |
| A4 | pass | password / secret masked on read; plaintext 0 hits |
| A5 | pass | formula / autonumber re-derived; caller-supplied values ignored (17.6.0 formula strip) |
| A6 | pass | dangling references → 400 reference_not_found per field |
| A7 | pass | expense and invoice roll-ups equal their lines; live line add moves the total |
records-forms.import-transform-matrix (rev 1) — FAIL (stale clause)
| # | verdict | evidence |
|---|---|---|
| A1 | pass | none / map / constant / split / join each produce the expected row values |
| A2 | fail (stale) | javascript mapping → 400 UNSUPPORTED_TRANSFORM naming the missing server-side sandbox, 0 rows — but the message no longer names framework#2611: f115b1f (#21188) states the decision in words |
| A3 | pass | lookup resolves the id; unknown name fails its row reference_not_found |
| A4 | pass | JSON through a csv mapping → 400 MAPPING_FORMAT_MISMATCH; xlsx accepted |
records-forms.named-import-mapping (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | 2 rows created with mapped fields and value maps |
| A2 | pass | re-import → 0 created, 2 updated, same ids |
| A3 | pass | unknown mapping → 404 MAPPING_NOT_FOUND, nothing landed |
automation.packaged-flow-subflow-disable-refusal (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | disabling a called subflow → 409 DELETE_RESTRICTED naming the caller |
| A3 | pass | ledger 0 rows after refusals; caller run's subflow step succeeded |
| A4 | pass | enabling a child is never guarded |
| A5 | pass | caller off → child off 200 (the clause's "EXPECTED FAIL" note is stale: 36d043b #20724, 0d9349f #20759) |
automation.packaged-flow-clone-contract (rev 1) — FAIL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | missing name / label → 400 VALIDATION_FAILED; nothing created |
| A2 | pass | clone differs from source only in name / label / status: draft; no ancestry keys |
| A3 | pass | clone bound as draft; both flows fire once per urgent create; notice = FLOW_CLONE_NOTICE |
| A4 | pass | same-name 409; unknown source 404; bad name 400; unknown body key 400 |
| A5 | pass | subflow reference preserved |
| A6 | pass | PUT /automation/<clone> 200, persisted |
| A7 | fail | durability half now PASSES (cold restart: GET 200, still fires — cb4c31d #20907); reachability half FAILS — no Studio rail lists the clone (verifier V2 confirmed) |
automation.flow-toggle-kill-switch (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A4 | pass | runs 0 → 1; off: 1 → 1; /_status flips both ways; on: → 2; unknown flow toggle 404 |
automation.setup-packaged-automation-board (rev 2) — PARTIAL
| # | verdict | evidence |
|---|---|---|
| A1 | pass | served nav_packaged_automation entry; sidebar click lands on …/component/automation/packaged |
| A2 | pass | 30 packaged flow rows = /meta/flow packaged set; clone absent |
| A3 | pass | switch persists across reload, same ledger row |
| A4–A5 | pass | Clone only on flow rows; no lineage text |
| A6 | blocked(fixture) | admin half passes (409 text word for word in a row alert); plain-member half undrivable — a plain member cannot reach Setup (setup.access) |
| A7 | pass | clone dialog validation and success notice |
| A8 | pass | plain member does not reach the page ("This app can't be opened"); read postures recorded |
integration-system.datasource-admin-lifecycle (rev 3) — PASS
| # | verdict | evidence |
|---|---|---|
| A1 | pass | drivers catalog 200 for admin, 401 anonymous |
| A2 | pass | 11 routes: anonymous 401, no-capability member 403 manage_platform_settings |
| A3 | pass | POST /test draft ok:true, nothing persisted |
| A4 | pass | migrate-credential: already-bound / migrated / refused outcomes, no 400s |
| A5–A6 | pass | runtime create 201; secret never echoed |
| A7 | pass | bad drafts 400, never 500 |
| A8–A9 | pass | 503 arm by pin; unknown 404 RESOURCE_NOT_FOUND |
platform-core.metadata-authoring-roundtrip (rev 2) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | draft view in a writable package listed in _drafts; publish → served and rendered as a tab |
| A3 | pass | record page authored in the metadata editor → draft PUT 200 |
| A4–A5 | pass | writes into the read-only package → 422 WRITABLE_PACKAGE_REQUIRED; object overlay 403 NOT_OVERRIDABLE |
| A6 | pass | bad view body → 422 INVALID_METADATA, nothing stored |
studio-authoring.view-authoring-live (rev 1) — FAIL (assertion defect)
| # | verdict | evidence |
|---|---|---|
| A1–A2 | pass | draft save 200; nothing served before publish |
| A3 | pass | after publish the authored tab and form layout apply, no restart |
| A4 | fail (assertion) | GET /meta/view?object=repair_asset serves the authored bodies as repair_asset.default / repair_asset.form; the container name never appears — the object door returns expanded items by design (expandViewContainer, #7163) |
| A5 | pass | second publish re-orders the columns live |
studio-authoring.custom-page-source-tiers (rev 1) — PASS
| # | verdict | evidence |
|---|---|---|
| A1–A4 | pass | html pages render compiled static boards; react page binds live data (5 = /data total) and edits rows |
| A5 | pass | OS_PAGE_REACT=off disables react pages with the named message; html pages still render |
| A6 | pass | react fault → "React page error"; html fault → "HTML page failed to compile (3)" in the editor preview |
Fails — reproduction rules and dispositions
Product defects:
R1 · access-security.crud-permission-matrix · A1 — product defect, already tracked at #21057 (fails closed: over-refusal, not an exposure). Verifier V1: CONFIRMED.
- Fresh boot; admin invites + signs up a delegate D and a target T.
- Admin
POST /api/v1/data/sys_user_permission_set {"user_id":D,"permission_set_id":<id of showcase_field_ops_delegate>}→ 201;POST /api/v1/data/sys_business_unit_member {"business_unit_id":"bu_field_ops","user_id":D}→ 201. - D
POST /api/v1/data/sys_user_position {"user_id":T,"position":"contributor","business_unit_id":"bu_west_coast"}. - Expected 2xx (adminScope = Field Operations + subtree). Actual
403 PERMISSION_DENIED "delegated 'insert' on sys_user_position rejected — business unit 'bu_west_coast' is outside the delegated subtree"— also forbu_field_opsitself. Admin with the identical payload → 201. - Mechanism oracle: every seeded
sys_business_unitrow readsorganization_id: null; after admin PATCHes the active org onto the seeded units, D's create → 201 and an out-of-subtree create (bu_hq_finance) stays 403.
R2 · access-security.public-form-intake · A7 — product defect. Detail withheld pending maintainer (RUNNER rule 2). Verifier V1: CONFIRMED.
R3 · automation.packaged-flow-clone-contract · A7 (reachability half) — product defect, not previously filed (it was the FOLLOW-UPS §8 D18 row, marked "safe to file"). Verifier V2: CONFIRMED.
- Admin
POST /api/v1/automation/showcase_urgent_task_alert/clone {"name":"qa_urgent_alert_clone","label":"QA urgent alert clone"}→ 200. POST /api/v1/data/sys_metadata/query {"where":{"name":"qa_urgent_alert_clone"}}→package_id: null, scope: platform.GET /api/v1/meta/flow?package=com.example.showcase→ 30 flows without the clone; bareGET /api/v1/meta/flow→ 31 with it.- Console
/_console/studio→ "No writable packages yet";/_console/studio/com.example.showcase/automationslists the 30 packaged flows, not the clone;…?surface=flow%3Aqa_urgent_alert_cloneredirects to another flow. - Expected (ADR-0126 §1.3 / §7.1, the Setup page's "Editing happens in Studio") the clone is listed on a Studio Automations rail; actual no Studio surface at the pin lists or opens a package-less flow (the rail is
loadPackageSurfaces(client,'flow',packageId)).
R4 · approvals.account-app-entry · A7 — product defect in the console, objectui#11326, still present at the pin (fixed upstream in objectui d0fba91aa, not an ancestor of 31971ff1e28f). Verifier V4: CONFIRMED (the zh catalogue at the pin holds every expected string, so the clause is not stale).
- Fresh boot; sign in; open
/_console/apps/com.objectstack.account/component/approvals/inbox. - Avatar → Preferences → Language → 中文(中国); then a full load of the same URL.
- Expected h1 / tabs / badge in Chinese (审批中心 · 待我审批 / 我发起的 / 全部 · 待审批). Actual "Approvals Inbox", "My Pending / Submitted by me / All", "Pending";
localStorage['objectui-locale']="zh-CN",<html lang="zh-CN">, the server-served nav label 待我审批; onlyi18n-locale-en-*.jsfetched althoughi18n-locale-zh-*.jsis in dist. Mechanism at the pin:packages/i18n/src/locales/registry.tskeys built-in loaders by base language, and the provider asks with the region-tagged code.
Checklist clauses (no product defect — the docs PR linked in the close-out re-points them):
R5 · access-security.audit-log-browser · A1 — stale clause. Admin DELETE /api/v1/data/showcase_task/<id> → 200; admin GET /api/v1/data/sys_audit_log?filter={"action":"delete"} → expected (clause) 1 row, actual total 0; the row is stored with correct attribution (direct read of the telemetry DB). Changed deliberately by 30c530e5 (#21194): the ledger serves a non-system reader, administrators included, only rows about records it can read (17.6 notes, "The compliance ledger and the activity stream…").
R6 · api-backend.filter-comparand-conformance · A5 — stale clause. POST /api/v1/data/showcase_field_zoo/query {"where":{"f_number":{"$eq":{"x":1}}}} → 400 VALIDATION_FAILED (fields[0].field = query.where.f_number.$eq); GET …?$filter={"f_number":{"$eq":{"x":1}}} → 400 INVALID_FILTER; engine door → INVALID_FILTER. Every door refuses with the same contract sentence and none returns rows; the code differs because #20116 moved the POST refusal into the request schema (cfc3bcf1 #20247, dd1b8031 #20325) — the split query-contract-matrix rev 3 already records.
R7 · api-backend.date-range-preset-matrix · A3 — assertion defect. POST /api/v1/data/showcase_account/query {"where":{"signed_on":"today"}} → 400 INVALID_FILTER with the temporal-door message; {"where":{"signed_on":{"$gte":"this_week"}}} → 400 VALIDATION_FAILED with bareDateRangePresetComparandMessage. The preset message is deliberately an ordering-position refusal (18.filter-preset-ordering-comparand-refused.ts: "Ordering positions only at the schema door"); an equality on a date field is refused by the temporal door. The guarded failure (200 with zero rows) does not occur.
R8 · records-forms.import-transform-matrix · A2 — stale clause. PUT /api/v1/meta/mapping/qa_l7_js with one entry transform:"javascript"; POST /api/v1/data/showcase_inquiry/import {"format":"csv",…,"mappingName":"qa_l7_js"} → 400 UNSUPPORTED_TRANSFORM naming the missing server-side sandbox, 0 rows; the message no longer names framework#2611 since f115b1f (#21188: refusals state each decision in words instead of a tracker number).
R9 · studio-authoring.view-authoring-live · A4 — assertion defect. PUT /api/v1/meta/view/qa_repair_asset_views?mode=draft&package=<writable pkg> with {name, object:"repair_asset", list, form}; publish; GET /api/v1/meta/view?object=repair_asset → repair_asset.default and repair_asset.form carrying the authored config; the container name appears 0 times. The object door returns expanded <object>.<key> items by design (expandViewContainer, #7163).
Fixture gaps and environment notes
- crud-permission-matrix A6: unobservable until security(core, plugin-security): an unauthenticated principal never resolves the permission sets bound to the
guestanchor; ADR-0090 D9 is declared and seeded but not enforced #21158 gives anonymous callers a permission-set channel (17.6.0 deny baseline); the reason is a dependency, not a fixture. - datasource-credential-refusal-matrix A2: no real Postgres / MySQL / Mongo in the sandbox (
ECONNREFUSED). - crud-roundtrip A7 readonly half: no readonly column is settable over HTTP on the stock seed.
- setup-packaged-automation-board A6: needs a persona with
setup.accessbut withoutmanage_metadata(e.g.organization_admin); a plain member never reaches Setup. - dashboards global-filters D2 needs a writable package (the showcase package is read-only in Studio;
duplicate→ 422 DUPLICATE_SOURCE_NOT_A_BASE). - Sandbox egress denies
cloud.objectos.ai(marketplace catalog). - Container memory (15 GB, 4 CPUs) was contended with 9 lanes: several vitest runs were SIGKILLed and Chromium renderers crashed until launched with
--disable-dev-shm-usage; every affected run was repeated and its verdict rests on the repeat. - Killing a recorded
pnpmwrapper PID leaves theobjectstack devchild listening; teardown needs the process tree or the port's listener. - The showcase's
dist/objectstack.jsonis converted forward on load at boot (ADR-0087page-component-filter-record-to-rule-array) — harmless.
Close-out (extracted cards, checklist-accuracy findings, the docs PR) follows in a comment.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 2, 2026
Close-out
Every
failhas a disposition, and each was re-derived by an independent verifier (RUNNER rule 7) or rests on the commit that changed the behaviour:The five stale / assertion-defect fails are re-pointed by draft PR #21339 (the re-point carrier, one history entry per item). This run re-judges all 19 items of #21056, so #21056 is superseded with nothing to carry forward.
Product defects
617f25f8; mechanism unchanged (seeded units carryorganization_id: null)d0fba91aa, not in the 17.6.0 pin31971ff1e28f; a later pin bump carries it$ne "true"returns the true row;?f=true/?f=falseboth 0 rows)liston another package's object replaces its packaged default on the object door; the by-name read disagreesjsonfield shows[object Object]in the edit form and saves as a stringStale clauses (the docs PR)
Draft PR #21339 re-points the five stale / assertion-defect clauses and retires three expected-fail notes 17.6.0 has made pass (subflow remedy, clone durability, write-door parity); each history entry cites the changing commit.
Partial-coverage gaps (named, not dropped)
records-forms.crud-roundtripA7 readonly half — no readonly column is settable over HTTP on the stock seed.integration-system.datasource-credential-refusal-matrixA2 "connects" — no real Postgres / MySQL / Mongo in the sandbox.automation.setup-packaged-automation-boardA6 member half — the clause's plain member never reaches Setup (setup.access); a persona withsetup.accessand withoutmanage_metadata(e.g.organization_admin) renders the refusal exactly.access-security.crud-permission-matrixA6 —blocked(dependency #21158): 17.6.0's deny baseline gives anonymous callers no channel to a permission set yet.Checklist-accuracy findings (not in #21339; for the checklist owner)
records-forms.picklist-shared-across-objectsandrecords-forms.picklist-org-appendare stillstatus: planned, although shared picklists shipped in 17.6.0 (addbbf0feat(spec): thepicklistmetadata kind — a shared option list select fields reference by name (#19518) #20823,88b484efeat(objectql): resolve picklist references at runtime — served options, additive extensions, write validation, load-time refusal #21047). They need steps and acceptance authored and a passing run before promotion (README "Promotion is earned").access-security.rls-both-sidesA6 still calls itself the "standing expected-fail probe"; it holds since99398542(fix(showcase): the contributor set's row-level policies apply to every holder of the set #21104). The text needs a non-disclosing update;showcase-invoice-seed-isolation.dogfood.test.tsis a candidateautomated.ref.access-security.write-path-guardsstep 6 / A7: one mixed batch shows both outcomes only withoptions.continueOnError: true(default stops at the first failing row,NOT_ATTEMPTEDafter). N2: namepackages/objectql/src/engine-insert-static-readonly-strip.test.tsinautomated.ref.access-security.owd-sharing-matrixstep 4:showcase_invoiceandshowcase_projectrequireaccount.access-security.audit-log-browserstep 5: at this Console pin the page/apps/setup/system/audit-log("Audit Log Browser") HAS an Action select that sends$filter; the Recent / Writes / Auth / Config tabs belong to the separatesys_audit_logobject view. The 405 listsget, list, aggregate, search, export.access-security.public-form-intakestep 6: the anonymous page is/_console/f/contact-us(/forms/contact-usin the console is the authenticated form). A2: an anonymous submit landsowner_id: null, not "server-stamped". Step 7 names no unpublish route.access-security.packaged-flow-write-door-parity(observation): the layered read of the packaged flow reportslock: none, editable: truewhile both write doors refuse 403.ai.mcp-stdio-fail-closedstep 3: the principal-bound line logs at info — needs--log-level info. MCPquery_recordscapslimitat 50.api-backend.aggregate-contract-matrix: the named seeded personas cannot drive the secured leg; usesearch:qa-contributor-bound-member. Step 6's non-array shape refusal carries no worked example.api-backend.packaged-action-disabled-dispatchA2: the enabled-control 400 code isVALIDATION_ERROR, notVALIDATION_FAILED.INVALID_FIELDenvelope carries{error, code, field, object}only — notfields/param.automation.packaged-flow-disable-durablestep 6: the activation-ledger boot line is info-level (OS_LOG_LEVEL=info).automation.flow-toggle-kill-switch:showcase_urgent_task_alertis not "the only record_change flow" (8 are).automation.setup-packaged-automation-boardA8:sys_metadata_activationis 403 for a plain member, not "open".integration-system.datasource-admin-lifecycle: sqlite drafts takeconfig.filename(17.6.0 refusesfile); migrate-credential answersresult.statusalready-bound/migrated/refused, notaction; the list's health field isstatus+statusReason.integration-system.datasource-credential-refusal-matrixA7 / N6: these doors never serve a mask — they omit the key and list it inredactedConfigKeys. Observation for the maintainer: PATCHing the literal••••••••as a tursoencryptionKeystores the literal (admin door and meta door) — matters only if a client echoes a rendered mask.dashboards.cube-queryA1: the meta body isdata[], nocubeskey.dashboards.global-filters-rescopeA6 nameskpi_at_risk, which opts out by design (showcase authoring gap: the ops dashboard's global filter isfield:'status'with *task* statuses, and the project widgets carry nofilterBindings— so it also lands onshowcase_project.statusand zeroes those tiles #7568) — the composing tile iskpi_awaiting_review.dashboards.dataset-report-authoringA6: the two joined blocks show different measures, so totals partition per measure only.dashboards.strict-widget-rejects-stray-keysstep 5: the browser door that can emit a stray key is the metadata editor's Source tab. D2 needs a writable-package recipe (duplicateof the showcase → 422 DUPLICATE_SOURCE_NOT_A_BASE).records-forms.field-type-matrixA6:f_treetargetsshowcase_field_zoo(notshowcase_category); autonumber is not on the edit form by design; Edit lives under More actions; steps 4–5 have no clause (objectui#11448 shows one is needed). Theqa-contributor-bound-memberrecipe writessys_user_position.position(a name).studio-authoring.first-run-loop: buttons read "Save as draft" / "Publish all".platform-core.metadata-authoring-roundtripstep 2: a standalone view name must be dotted<object>.<key>(422invalid_format), columns must be real fields (title), and step 8's surface is the metadata editor.studio-authoring.custom-page-source-tiersstep 8: sinceb531c7b(feat(cli,metadata-protocol): the save door compiles an html page's source against the deployment's SDUI manifest #20852) a faulted html page exists only as a draft — its compile panel is in the page editor preview.platform-core.nav-surfaces-renderstep 1:pnpm test:smokeruns every spec ine2e/, not only the pin — name the file.approvals.account-app-entry: the persona must own the invoice through the TEXT fieldowner(= its email) for the contributor RLS to admit its PATCH.content/docs/releases/v17/17-6.mdx"Known console issues at this pin" lists three issues but not the zh-CN (region-tagged locale) built-in catalogue gap (objectui#11326, fixd0fba91aanot in the pin).Environment blockers are recorded in the run body; nothing further to extract.
Generated by Claude Code
objectstack-fleet commentedon Oct 6, 2026
Close-out completed: closed
completedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-06T16:06Z. The maintainer approved clearing the openqa-runrecords' close-out debt: 「同意,动手」. This was audited row by row (read-only) against checklist-test §5 before this write.Extracted: PR #21339 · audit-log-browser · re-point
Extracted: PR #21339 · filter-comparand-conformance · re-point
Extracted: PR #21339 · date-range-preset-matrix · re-point
Extracted: PR #21339 · import-transform-matrix · re-point
Extracted: PR #21339 · view-authoring-live · re-point
Parked-on: #21932 · integration-system.datasource-credential-refusal-matrix · A2 blocked(environment), recorded as a checklist knownGap
Parked-on: #21060 · records-forms.crud-roundtrip · A7 readonly half blocked(fixture), recorded as a checklist knownGap
Superseded-by: #21782 · automation.setup-packaged-automation-board
objectstack devboot — seeded business units are organization-less while every session carries an active organization, so every in-scope delegated write is refused #21057, security(forms): access-security.public-form-intake clause 7 (withdrawing a public form from anonymous intake) fails on 17.6.0 — detail withheld pending maintainer #21331, automation: a cloned packaged flow reaches no Studio surface — the clone is stored with no package, and every Studio Automations rail lists only package-scoped flows #21332 → objectui#11553, objectui#11326).Generated by Claude Code
docs(qa): re-point eight checklist items the 17.6.0 release-verificat…
docs(releases): finalize the 17.6.0 notes after publish (objectstack-…
chore(objectui): bump the console pin to 89cad75d5570 (carries object…
fix(metadata-protocol): the save door refuses a view container saved …