Repository navigation
QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #23949
Description
Run record for the full-checklist wave anchored on #23878 (selector all, run per area). Runner session session_01K3qUoyh1xFGoWzAACPMbXJ, dispatched by the domain:devx seat 1 PM. Protocol: .claude/skills/checklist-test/SKILL.md + docs/qa/platform-checklist/RUNNER.md.
Environment fingerprint
- framework:
94114c1f269f6b3d885f24b6f4689dce29504574(origin/main at run start;pnpm install --frozen-lockfile && pnpm build, 75/75 green) - resolver:
node scripts/checklist-select.mjs allat that sha → 272 runnable · 2 blocked hidden · 1 planned .objectui-sha:daeb77fc84dd824e74c95b87cdede85afdb248e7. ⚠pnpm objectui:buildFAILS on this sha (Console pin does not build on main: objectui daeb77fc imports checkPageComponentViewData, deleted from spec by #23784 #23908). Browser/mixed verdicts ran on a console built at that same pin against@objectstack/specfroma35f7a67^(the commit before the deletion), served from a linked worktree at94114c1fwith identical framework dists.check:console-shamatches.- app: showcase · boot
OS_PORT=P objectstack dev --ui --seed-admin -p P -d file:…/data.db· per-runner port and DB · seeded admin + demo personas - browser: Playwright 1.63 on
/opt/pw-browsers/chromium(Chromium 141), one context per persona - timestamp: 2026-10-16 (UTC) · runners:
- g15 records-forms-1: ports [41280] · sequential · env 9 min / test 64 min
- G16 records-forms-2: ports [41300] · sequential · env 6 min / test 59 min
Scope
Selector area:records-forms (from the all resolution) · 40 runnable item(s):
records-forms.crud-roundtripr9 · P0 · browser → passrecords-forms.field-type-matrixr2 · P1 · mixed → passrecords-forms.list-view-capabilitiesr2 · P1 · browser → passrecords-forms.view-type-galleryr1 · P1 · browser → passrecords-forms.form-view-galleryr3 · P1 · browser → passrecords-forms.conditional-rules-headerr2 · P1 · mixed → passrecords-forms.conditional-rules-gridr3 · P1 · browser → passrecords-forms.cascading-optionsr3 · P1 · mixed → passrecords-forms.master-detail-atomic-saver3 · P1 · browser → passrecords-forms.related-list-server-paginationr4 · P1 · browser → passrecords-forms.action-param-widgetsr2 · P1 · mixed → passrecords-forms.upload-guard-blocks-confirmr2 · P2 · browser → passrecords-forms.import-wizard-encoding-and-hintsr6 · P2 · browser → passrecords-forms.action-location-matrixr10 · P1 · browser → passrecords-forms.validation-rule-type-matrixr2 · P1 · api → passrecords-forms.gantt-interactionsr4 · P2 · browser → partialrecords-forms.kanban-drag-persistencer1 · P2 · browser → passrecords-forms.form-dirty-guardr3 · P2 · browser → passrecords-forms.named-import-mappingr1 · P2 · api → passrecords-forms.adhoc-filter-sort-builderr2 · P1 · browser → passrecords-forms.calendar-interactionsr2 · P1 · browser → passrecords-forms.concurrent-edit-conflictr2 · P1 · mixed → passrecords-forms.record-discussion-mentionsr3 · P1 · browser → passrecords-forms.grid-personalizationr3 · P2 · browser → passrecords-forms.saved-view-managementr2 · P2 · browser → passrecords-forms.bulk-select-all-matchingr3 · P2 · browser → passrecords-forms.lookup-picker-create-newr2 · P2 · browser → passrecords-forms.import-job-undo-cancelr3 · P2 · mixed → passrecords-forms.object-hook-lifecycler4 · P1 · mixed → passrecords-forms.field-type-constraintsr1 · P1 · mixed → passrecords-forms.cascading-multilevel-and-clearr2 · P1 · mixed → passrecords-forms.encrypted-field-behaviorr2 · P1 · mixed → passrecords-forms.field-group-visible-whenr3 · P1 · mixed → passrecords-forms.field-unique-enforcementr2 · P1 · api → passrecords-forms.delete-behavior-matrixr1 · P1 · api → passrecords-forms.record-clone-contractr1 · P2 · api → passrecords-forms.field-history-trackingr1 · P1 · mixed → passrecords-forms.import-transform-matrixr2 · P2 · api → passrecords-forms.predicate-relationship-traversalr2 · P1 · api → passrecords-forms.script-action-hook-refusal-toastr1 · P2 · browser → pass
Blocked and hidden by the resolver (not driven): records-forms.record-edit-undo. Each carries its own blocked fixture reason in the ledger.
Planned (not runnable, not counted): records-forms.picklist-shared-across-objects.
Per-clause verdicts
| item | # | clause (short) | verdict | evidence |
|---|---|---|---|---|
| crud-roundtrip | 0 | create returns id; re-read verbatim | pass | UI POST /data/showcase_account 201 body {name,industry:finance,status:active,sales_region:amer}; filtered GET n=1 same values, website null |
| crud-roundtrip | 1 | update persists only edited field | pass | PATCH body {"annual_revenue":1234567} 200; full-row diff = annual_revenue null→1234567 + updated_at only |
| crud-roundtrip | 2 | delete authoritative | pass | row menu Delete + alertdialog confirm → DELETE 200; filtered GET records 0 total 0 |
| crud-roundtrip | 3 | list reflects mutations after reload | pass | post-reload list showed the new row (1 hit); after delete the reloaded list + quick-search shows only the -华宁/-clone rows |
| crud-roundtrip | 4 | History shows display values | pass | History tab: UPDATE "Annual Revenue: — → 1234567"; CREATE "Industry: Finance / Lifecycle: Active / Sales Region: AMER", actor Dev Admin; no value→null rows |
| crud-roundtrip | 5 | unicode round-trip + searchable | pass | name os-qa-g15-…-华宁 re-read identical; $search 华宁 → [华宁科技, the new row]; UI search huaning finds both |
| crud-roundtrip | 6 | clone 201 new id, values copied, engine cols re-derived, owned by cloner | pass | clone 201 id≠sourceId, sourceId=source; business fields equal, id/created_at/updated_at differ; clone by org-admin persona → owner_id=cloner; readonly half: pins 11+21 passed |
| crud-roundtrip | 7 | clone RLS-gated 404 | pass | contributor-bound member: GET INV-1003 404 RECORD_NOT_FOUND, list 0 then 1 own row; POST INV-1003/clone → 404 RECORD_NOT_FOUND; admin invoice count 14→14 |
| field-type-matrix | 0 | HTTP round-trip matrix (pin) | pass | field-zoo-roundtrip + value-shape in the dogfood batch: 101 passed, 0 failed; matrix declares no xfail rows |
| field-type-matrix | 1 | per-variant widget table | pass | 48 f_* controls typed on Specimen Full (date→input[date], color→input[color], file→input[file], signature→canvas, rating→stars…; table in zoo-table.txt); autonumber read-only on detail |
| field-type-matrix | 2 | constrained types reject server-side | pass | f_select not-a-value → 400 invalid_option; name "" → 400 required; zoo count 2→2 |
| field-type-matrix | 3 | credentials masked on read | pass | pin masked checks pass; raw GET f_password "••••••••", f_secret "••••••••" (dev boot has a CryptoProvider; secret write 201) |
| field-type-matrix | 4 | computed/system materialize | pass | created zoo row: f_formula 31.5 (42×75/100), f_autonumber "0004" |
| field-type-matrix | 5 | relational types refuse dangling id | pass | fabricated f_lookup / f_master_detail / f_tree → 400 reference_not_found each; pin round-trips real ids |
| field-type-matrix | 6 | summary on real carriers | pass | INV-1001..1004 total == sum(line.amount) (1619.96/198/299.94/450); EXP-2001 total 1500.5 approved 960 |
| list-view-capabilities | 0 | per-variant evidence | pass | 11 variants each have their own trace/screenshot (filter, search, sort, paging, switcher, inline edit, export, viz, bulk, row actions, conditional formatting) |
| list-view-capabilities | 1 | saved-view filter AST + server count | pass | GET showcase_task?…filter=[["status","equals","in_progress"]] 200, total 2 (Build homepage, Ingest pipeline); count bar "2 records" |
| list-view-capabilities | 2 | quick-search is server-side, pinyin recall | pass | GET showcase_account?top=50&search=huaning 200 → 华宁科技 (+ the CJK test row) |
| list-view-capabilities | 3 | paging top/skip, partition | pass | showcase_inquiry (60 rows): top=50 then top=50&skip=50; 50+10 ids, overlap 0, union 60 = all 60, no unpaged fetch |
| list-view-capabilities | 4 | string-form sort renders + server-ordered | pass | Task List view: GET …sort=-estimate_hours 200, rows 88,88,60,40,30,24,18,16,16,8,8,8 (descending), 0 pageerrors |
| list-view-capabilities | 5 | inline edit PATCH only edited cell | pass | Grid view dblclick Estimate → Save All(1) → PATCH {"estimate_hours":13} 200; re-read diff estimate_hours 12→13 (+audit only) |
| list-view-capabilities | 6 | export = declared formats, filtered rows | pass | grant qa_task_export bound (before: 403 EXPORT_NOT_PERMITTED); Display → Export as CSV/XLSX/JSON; each GET …/export?format=X&filter=in_progress 200; CSV 2 rows, JSON 2 items, XLSX header+2 |
| list-view-capabilities | 7 | viz switcher exactly six | pass | Layout menu: Grid, Kanban, Gallery, Calendar, Timeline, Gantt; each re-rendered task records (10/10/8-in-month/10/10/10), 0 pageerrors |
| list-view-capabilities | 8 | bulk dispatch by mode | pass | Mark Done on 3 rows → 3 POST /actions/showcase_task/showcase_mark_done; Recalculate Selection → 1 POST /api/v1/apps/showcase/recalc {"_selectedIds":[3 ids]} 200 |
| list-view-capabilities | 9 | legacy rowActions live, no dup | pass | Legacy Row Actions menu: Mark Done, Reassign…, Quick View (once), Recalculate Estimate; Recalc → POST /apps/showcase/recalc {recordId} 200; Quick View opens its modal page |
| view-type-gallery | 0 | 9 types render real visualization | pass | named views Board/Cards/Calendar/Activity Timeline/Schedule/Work Locations/Hours by Status + Grid + Org Chart each screenshotted showing their own structure; none fell back to grid |
| view-type-gallery | 1 | kanban columns + summarize totals | pass | 5 columns × 2 cards; Estimate sums 16/108/100/150/24 = API group-by of estimate_hours (backlog/todo/in_progress/in_review/done) |
| view-type-gallery | 2 | temporal placement | pass | Design system due 10-18 → calendar 18, created 10-02 → timeline 10/2, gantt 10/4–10/18 = start/end; Build homepage 10-26 / 10-08 / 10/10–10/26; progress shown on bars |
| view-type-gallery | 3 | map markers + chart SVG from dataset | pass | map: 10 markers for 10 located tasks (basemap tiles blocked by egress, markers drawn); chart: 16 recharts bars after GET meta/dataset/showcase_task_metrics + POST analytics/dataset/query 200 |
| view-type-gallery | 4 | tree renders hierarchy | pass | Org Chart expanded: Acme > Go-To-Market > Marketing, Sales > Enterprise Sales; Acme > Product & Engineering > Frontend Guild > Design Systems, Platform — matches API parent links |
| view-type-gallery | 5 | smoke pin green | pass | SMOKE_API_URL=:41280 playwright test --grep view surfaces → 7 passed (All Views, Task Board, Task Calendar, Task Gallery, Team Schedule, Activity Timeline, Work Map) |
| form-view-gallery | 0 | each layout renders its mechanics; drawer/modal on task-desk | pass | simple: Task, 2 cols; tabbed: 3 tabs; wizard on /new: "Step 1 of 3"; split: panes at x=417 / x=865; task-desk: 384px right drawer + modal dialog |
| form-view-gallery | 1 | wizard step order + one save | pass | Next with empty title → "Title is required", stays on step 1; walk 3 steps = 0 writes; final Save → 1 POST /data/showcase_task 201 |
| form-view-gallery | 2 | visibleWhen notes live, full width | pass | priority Urgent → notes mounts (width 896 vs title 440); back to Medium → unmounts |
| form-view-gallery | 3 | every save persists | pass | simple PATCH {title} 200; tabbed PATCH {title}; split PATCH {title}; drawer PATCH {priority:medium}; wizard POST 201; each re-read shows only the edited key changed |
| form-view-gallery | 4 | tab/step nav keeps unsaved values | pass | tabbed: typed title survives Overview→Schedule→Overview; wizard: Back to step 1 keeps "qa-g15 wizard task" |
| conditional-rules-header | 0 | requiredWhen live + blocked submit | pass | INV-1002 edit: status Draft→Sent puts the required marker on issued_on; Save with it empty → field error "required", 0 writes |
| conditional-rules-header | 1 | requiredWhen server-side | pass | POST invoice status sent w/o issued_on → 400 VALIDATION_FAILED fields[issued_on/required]; 0 rows persisted |
| conditional-rules-header | 2 | readonlyWhen client lock + server strip | pass | paid INV-1003 form shows tax_rate as read-only text; PATCH tax_rate 99 → 200, x-objectstack-dropped-fields tax_rate;reason=readonly_when; tax_rate 8 before and after |
| conditional-rules-header | 3 | visibleWhen paid_on | pass | paid_on absent from the draft form; present and required on the paid form |
| conditional-rules-header | 4 | row-action gating by CEL | pass | INV-1003 row menu [Edit(disabled), Submit for Sign-off] — no Delete; INV-1002 [Edit, Delete, Submit for Sign-off]; detail Edit disabled on paid |
| conditional-rules-header | 5 | one rule both ends | pass | form block ↔ API 400 on the same issued_on predicate; client-locked tax_rate = server-stripped field |
| conditional-rules-grid | 0 | row-scoped requiredWhen flags per row + blocked save | pass | row1 qty 120 + empty description → aria-invalid + "Description is required"; row2 (qty 1) unflagged; Save → batch 400 description/required, nothing written |
| conditional-rules-grid | 1 | server enforces same rule | pass | direct POST line qty 120 no description → 400 description/required; atomic batch [valid line, violating line] → 400; line count unchanged |
| conditional-rules-grid | 2 | parent-scoped readonlyWhen live | pass | status→Paid in form: Qty/Unit Price inputs LOCKED, product picker loses buttons; back to Draft: all editable, no reload |
| conditional-rules-grid | 3 | receipt cell stores managed file | pass | upload → presigned/raw PUT/complete 200; batch line update receipt=‹sys_file id>; re-read receipt {id,name small.pdf,size 45}; GET storage/files/‹id> 200 application/pdf 45 bytes |
| conditional-rules-grid | 4 | saved batch persists, siblings identical | pass | line pos0 changed description/quantity/amount only; line pos1 (distinct position) 0 changed keys; count 2→2 |
| cascading-options | 0 | dependsOn gates + live re-filter + clear | pass | province shows "Select country first" until country set; cn → [Zhejiang, Guangdong]; pick Zhejiang then switch to us → value cleared, options [California, Texas] |
| cascading-options | 1 | server invalid_option + in-set accepted | pass | POST cn+ca → 400 fields[province/invalid_option], 0 persisted; cn+zj → 201, re-read cn/zj |
| cascading-options | 2 | role gate both sides | pass | org_admin persona: options Standard+Restricted, POST restricted 201, re-read restricted; plain member: Standard only, POST restricted → 400 tier/invalid_option |
| cascading-options | 3 | dependent lookup scoped by account | pass | contact picker disabled "Select Account first"; Northwind → GET showcase_contact?top=50&filter=[account=Northwind] total 26, 26 options; Contoso → total 2, 2 options |
| cascading-options | 4 | legal selection persists | pass | created cn/zj row re-read country=cn province=zj |
| master-detail-atomic-save | 0 | one atomic batch with child refs | pass | Save → 1 POST /api/v1/batch atomic:true: invoice create + 2 line creates with invoice {"$ref":0} |
| master-detail-atomic-save | 1 | rows readable + autofill persisted | pass | re-read invoice Northwind/draft; lines WIDGET-A "Standard widget" 29.99×3=89.97, SERVICE-HR "Professional services, per hour" 150×2=300 |
| master-detail-atomic-save | 2 | total server roll-up on create/add/delete | pass | total 389.97 = sum; add WIDGET-B×2 → 489.95; delete it → 389.97 |
| master-detail-atomic-save | 3 | failing child aborts all | pass | line with no product → batch 400 product/required, toast "Product is required", dialog stays; filtered GET 0 invoices; line count 8→8 |
| master-detail-atomic-save | 4 | derived subform on stock New form | pass | New Invoice dialog renders data-testid line-items grid (Product/Description/Service Start/Qty/Unit Price/Receipt/Amount) |
| master-detail-atomic-save | 5 | amount live + persisted | pass | qty 3 → amount cell 89.97 before save; stored amount 89.97 |
| related-list-server-pagination | 0 | server top/skip scoped by parent | pass | showcase_contact?top=5&skip=0&filter=[account=Northwind], then skip=5…25; 0 child requests without top |
| related-list-server-pagination | 1 | pager = server total, pages partition | pass | pager "Page 1 of 6"/"Page 2 of 6", total 26 = direct filtered count 26; 6 pages 26 ids, 26 unique |
| related-list-server-pagination | 2 | related lists derived | pass | Northwind detail tabs Details/Invoices/Projects/Related(28)/History with no hand-built page |
| related-list-server-pagination | 3 | declared relatedList config honored | pass | Invoices tab label "Invoices", columns Invoice Number/Status/Total/Issued On; request select=id,name,status,total,issued_on |
| related-list-server-pagination | 4 | row navigation + re-read | pass | clicked Prospect 17 → /showcase_contact/record/‹id>; GET that id = Prospect 17, account Northwind |
| related-list-server-pagination | 5 | read gate both ends | pass | contributor: no Contacts section, page count probe 403, forged child GET 403 PERMISSION_DENIED; manager: Contacts 26 renders, forged GET 200 26 rows |
| action-param-widgets | 0 | per-param real widget | pass | text input; richtext RichTextField textarea; select combobox; date input[date]; 2 lookups record picker; color input[color]; autonumber read-only "—"; image/file input[file] |
| action-param-widgets | 1 | accept/multiple are DOM attributes | pass | p_cover accept="image/" no multiple; p_attachments accept="application/pdf,image/" multiple |
| action-param-widgets | 2 | contract enforced at dispatch | pass | bad p_priority → 400 VALIDATION_ERROR "Invalid option"; conformant → 200 received [p_text,p_priority,recordId,objectName]; pin in dogfood batch passed |
| action-param-widgets | 3 | required p_text both ends | pass | dialog Confirm empty → "Title is required", 0 action POSTs; direct POST without p_text → 400 "p_text is required" |
| upload-guard-blocks-confirm | 0 | Confirm disabled mid-upload | pass | 8MB PDF with upload throttled to 600KB/s: PUT storage/_local/raw in flight, button "Uploading…" disabled, forced click → 0 action POSTs; enabled after complete |
| upload-guard-blocks-confirm | 1 | dispatch carries file ref | pass | POST /actions/…/showcase_action_param_gallery 200, params.p_attachments ["‹file id>"], received includes p_attachments |
| upload-guard-blocks-confirm | 2 | maxSize enforced before the wire | pass | 11MB → "exceeds max size (10.0 MB)"; 14.5MB PNG → "exceeds max size (5.0 MB)"; 0 storage requests in that window |
| upload-guard-blocks-confirm | 3 | dialog usable after rejection | pass | small.pdf then: presigned/PUT/complete 200 → dispatch 200 |
| import-wizard-encoding-and-hints | 0 | GBK decode (pin) | pass | importParsers.test.ts 57/57 passed at objectui daeb77fc |
| import-wizard-encoding-and-hints | 1 | unmapped required disables Next + hint (pin) | pass | importMissingRequiredHint.test.tsx 2/2 passed at daeb77fc |
| import-wizard-encoding-and-hints | 2 | legacy fallback notice (pin-only) | pass | importLegacyReferenceGuard.test.tsx 3/3 passed at daeb77fc, incl "blocks the import and writes nothing when a lookup column is mapped" |
| import-wizard-encoding-and-hints | 3 | completed import lands readable rows | pass | wizard on showcase_contact with a GBK CSV: preview showed 测试联系人甲/经理; POST …/import 200 created 2; GET by email → 测试联系人甲 经理, 测试联系人乙 工程师 |
| action-location-matrix | 0 | every hosted location renders its actions | pass | toolbar Reassign…; row Mark Done/Reassign…/Quick View; header Mark Done/Log Time/Archive; ⋯ Open Docs/Recalc Estimate/Recalc Selection; related row Log Time; section Mark Done/Log Time/Archive |
| action-location-matrix | 1 | placement exclusive | pass | record_more-only actions absent from header/toolbar; toolbar has only Reassign…; Log Time absent from task-list rows; quick-actions bar has no Recalculate Estimate (not named) |
| action-location-matrix | 2 | each location dispatches | pass | flow wizard paused→resume 200 (assignee set); Quick View modal; Mark Done 200 done=true; Log Time → /forms/showcase_task.form; Open Docs → tab objectstack.ai; Recalc Selection POST 200, est 20→88 |
| action-location-matrix | 3 | row-level CEL both sides | pass | not-done task: Mark Done in row menu + header; done task: absent from both (Reopen instead); after header Mark Done the reload shows no Mark Done |
| action-location-matrix | 4 | empty locations = nowhere | pass | half (b): showcase_new_task ("New Task") on none of the six surfaces; recalc_selection under ⋯ only, not on the toolbar; half (a) blocked(fixture) per knownGaps (no packaged locations-less action) |
| validation-rule-type-matrix | 0 | per-type field codes | pass | all 400 VALIDATION_FAILED: invalid_initial_state, invalid_transition (project + account), invalid_format (tax_id, email), json_schema_violation, invalid_json, rule_violation (cross_field/script/conditional) |
| validation-rule-type-matrix | 1 | error targets declared field | pass | state_machine→status; format→tax_id/billing_email; json_schema→support_config; cross_field→start_date (=fields[0]); script/conditional→_record (no declared field) |
| validation-rule-type-matrix | 2 | no rejected write persists | pass | every rejected create 0 rows by name; rejected PATCHes leave full row byte-identical |
| validation-rule-type-matrix | 3 | happy-path twins land | pass | planned 201, planned→active 200, valid tax_id/email/support_config 201, end≥start 201, spent 110/100 201, churned+reason 200 |
| validation-rule-type-matrix | 4 | advisory warning flags, not blocks | pass | PATCH health green→red 200, re-read red; boot.log WARN "Validation rule 'project_health_progression' (warning): Health changed by more than one step" |
| gantt-interactions | 0 | bar drag PATCH + re-read | pass | drag Build homepage +2 days → PATCH {start_date:2026-10-12,end_date:2026-10-28} 200; API re-read matches; after reload grid shows 10/12–10/28 and bar x 351→571 |
| gantt-interactions | 1 | auto-schedule confirm-first (pin) | pass | GanttView.autoscheduledlg 4/4 (nothing written before confirm / cancel discards / confirm applies) + countinterp.i18n 5/5 at daeb77fc |
| gantt-interactions | 2 | locked task survives (pin) | pass | scheduling.selfextent 8/8 (locked task in skippedLocked) + GanttView.dnd 21/21 (summary drag skips locked) at daeb77fc; direct leaf-drag sub-leg blocked(fixture) per knownGaps |
| gantt-interactions | 3 | vetoed update leaves no write | blocked(fixture) | no host onBeforeTaskUpdate veto or server-side date rule on stock fixtures; no pin covers acceptance[3] |
| kanban-drag-persistence | 0 | drop issues update, re-read confirms | pass | Content backlog Backlog→To Do: PATCH {"status":"todo"} 200; API status todo |
| kanban-drag-persistence | 1 | survives reload | pass | after reload the card renders in the TO DO column |
| kanban-drag-persistence | 2 | rejected move reverts with error | pass | Evidence collection To Do→Done: PATCH {"status":"done"} 400 invalid_transition; toast "Invalid task status transition."; card back in TO DO; API todo |
| form-dirty-guard | 0 | accidental close guarded; Cancel and clean close do not prompt | pass | dirty Escape/X/backdrop → "Discard changes?" [Keep editing, Discard]; Keep editing keeps value; dirty footer Cancel closes, no prompt; clean Escape/X/backdrop/Cancel close, no prompt |
| form-dirty-guard | 1 | discard row identical, save persists | pass | after 3 discards + Cancel the Globex row is byte-identical; Save → PATCH {"annual_revenue":42424242} 200, no other keys changed |
| form-dirty-guard | 2 | unit pin | pass | discardGuard.test.tsx 14/14 passed at objectui daeb77fc |
| named-import-mapping | 0 | foreign headers land on mapped fields | pass | POST …/import mappingName showcase_inquiry_feed → created 2; rows name/email/company/message from Full Name/E-mail/…; Channel Webform→website, Partner Referral→referral |
| named-import-mapping | 1 | re-import idempotent | pass | same file again → created 0 updated 2; row count 2→2 (table 5→5) |
| named-import-mapping | 2 | unknown mapping fails loudly | pass | mappingName no_such_mapping → 404 MAPPING_NOT_FOUND "No mapping artifact named "no_such_mapping" is registered"; 0 rows |
| adhoc-filter-sort-builder | 0 | toolbar filter merges with view filter | pass | filter=["and",[[status equals in_progress]],["and",[priority = urgent],[estimate_hours > 4]]] → Ingest pipeline only; an empty Title row was dropped |
| adhoc-filter-sort-builder | 1 | sort reaches request, server-ordered | pass | SortBuilder Due Date Z→A → sort=-due_date; due dates come back descending, null last |
| adhoc-filter-sort-builder | 2 | filter + sort survive in-app nav | pass | nav Accounts → Tasks → In Progress: request re-issued with the same merged filter; restored from the per-user list preference |
| adhoc-filter-sort-builder | 3 | cache user-scoped | pass | state in sys_user_preference ui.list.showcase_task for the admin user_id + localStorage list-state:‹admin id>:showcase_task; second account on the same browser gets only the view filter |
| adhoc-filter-sort-builder | 4 | uf_* URL params reproduce rows | pass | Task Workbench ?uf_status=in_progress,in_review → 4 rows; Task Triage ?uf__tab=urgent → 2 rows; same URLs in a fresh context → same filter and same ids |
| records-forms.calendar-interactions | 0 | card drag issues PATCH + API re-read + reload | pass | drag Design system Oct18->21: PATCH /data/showcase_task/Lagl1ncaGxg_ESUF {"due_date":"2026-10-21"} 200; GET due_date 2026-10-21; post-reload card on Wed 21 |
| records-forms.calendar-interactions | 1 | failing PATCH reverts optimistic move + error toast | pass | scratch hook rejects "QA cal reject": card at Thu15 right after drop, PATCH 400, card back on Tue13, 1 error toast with the hook sentence, API due_date still 2026-10-13 |
| records-forms.calendar-interactions | 2 | month/week/day re-render same records by date | pass | Week Oct11-17 shows QA cal reject (13th); week Oct18-24 shows Warehouse schema Mon19 + Design system Wed21 (API 2026-10-19/21); Day Oct16 empty (no task due 16) |
| records-forms.calendar-interactions | 3 | empty-day quick-create inserts real row with clicked date | pass | click Oct23 cell -> Create Task dialog date input 2026-10-23; POST body due_date 2026-10-23 -> 201 6ZYPjBnt3Wlx0KBE; GET due_date 2026-10-23 |
| records-forms.concurrent-edit-conflict | 0 | losing save refused 409 CONCURRENT_UPDATE, A intact | pass | two sessions on Hooli: A PATCH If-Match 12:41:40.671Z -> 200; B PATCH same If-Match -> 409 CONCURRENT_UPDATE currentVersion 13:04:03; re-read website = A value |
| records-forms.concurrent-edit-conflict | 1 | conflict dialog with structured choice | pass | modal form: "Save conflict" dialog, Keep editing / Overwrite; inline: ConcurrentUpdateDialog with YOUR EDIT / CURRENT VALUE, Reload / Overwrite |
| records-forms.concurrent-edit-conflict | 2 | Reload discards B edit and refetches | pass | inline stale save on Wayne Enterprises -> 409 -> Reload: no PATCH/PUT after click, page shows https://concurrent-a.example (A value), API final = concurrent value |
| records-forms.concurrent-edit-conflict | 3 | Overwrite re-keys ifMatch to 409 currentVersion, one winner | pass | Globex: B 409 currentVersion 13:05:13.479Z; Overwrite retry PATCH If-Match 13:05:13.479Z body {website:ow-b-a} -> 200; final re-read = B value |
| records-forms.concurrent-edit-conflict | 4 | inline two-surface edit is ONE PATCH with exactly the 2 keys + If-Match | pass | dblclick Website (body) -> header Annual Revenue also editable, 1 Save bar; 1 PATCH {"website":"https://inline-b.example","annual_revenue":31000000} If-Match 2026-10-16T12:41:40.671Z -> 200 |
| records-forms.concurrent-edit-conflict | 5 | inline stale ifMatch -> 409 + same dialog | pass | concurrent API PATCH between read and save; inline PATCH If-Match 13:06:46.049Z -> 409 CONCURRENT_UPDATE; ConcurrentUpdateDialog rendered |
| records-forms.record-discussion-mentions | 0 | comment persists to sys_comment | pass | POST sys_comment 201 id 1261609c; GET sys_comment by thread_id showcase_account:1tE_vGstZ_g7MAvn -> 1 row with the body, mentions [usr_showcase_auditor_demo] |
| records-forms.record-discussion-mentions | 1 | optimistic row reconciles by id, no dupe/phantom | pass | client POST carried id 1261609c...; server 201 same id; marker leaf count sampled every 100ms for 3s then +2.5s = 1 every sample |
| records-forms.record-discussion-mentions | 2 | sys_activity interleaves oldest-first | pass | feed after edit: Updated Account (13:03:10) -> comment (13:09:16) -> "Industry: Finance -> Retail" (13:09:22), matching sys_activity timestamps |
| records-forms.record-discussion-mentions | 3 | mentioned user bell + deep link + unread decrement | pass | auditor.demo inbox badge 3, "Dev Admin mentioned you" with the comment; click -> POST /notifications/read, url /apps/com.example.showcase/showcase_account/record/1tE_vGstZ_g7MAvn; badge 3 -> 2 |
| records-forms.record-discussion-mentions | 4 | recipient-scoped both sides | pass | phone.demo inbox "You are all caught up", no mention; forged GET sys_inbox_message filter user_id=usr_showcase_auditor_demo as phone.demo -> 200 total 0 |
| records-forms.record-discussion-mentions | 5 | new comment on feeds-disabled parent -> 403 FEEDS_DISABLED, 0 rows | pass | qa-feeds-disabled recipe replayed; POST sys_comment thread qa_nofeeds:gehNATIV87z6Mhk7 -> 403 code FEEDS_DISABLED; thread read total 0; control POST on Initech thread -> 201 |
| records-forms.record-discussion-mentions | 6 | re-thread onto feeds-disabled parent refused, thread_id unchanged | pass | PATCH sys_comment/1261609c thread_id qa_nofeeds:... -> 403 FEEDS_DISABLED; re-read thread_id showcase_account:1tE_vGstZ_g7MAvn; control re-thread onto enabled 200; body-edit control 200 |
| records-forms.record-discussion-mentions | 7 | 4 FeedFilterMode values show exact subsets | pass | All Activity=comments+changes; Comments Only=2 comments; Field Changes=2 change rows only; Tasks Only=empty (record has no task rows); back to All restores every row |
| records-forms.record-discussion-mentions | 8 | author-extended activity type rendered in all feed, not dropped | pass | scratch qa_ov activityMilestones type qa_custom_escalation; sys_activity row type qa_custom_escalation "QA escalated: ov1"; all-feed renders it with generic (lightning) presentation |
| records-forms.grid-personalization | 0 | column resize persists across reload (reorder/pin n/a on stock) | pass | Assignee 240->373px, kept after reload (fresh context too); stored in localStorage grid-columns-showcase_task + pref ui.list.showcase_task; reorder/pin n/a on stock |
| records-forms.grid-personalization | 1 | row-height persists across reload | pass | Density set Compact, row height 45->41px; after reload Density=Compact, rows 41px; pref perView.showcase_task.grid.rowHeight=compact |
| records-forms.grid-personalization | 2 | group-by headers + totals match API + clears cleanly | pass | group Status: Backlog 3 / To Do 59 / In Progress 2 / In Review 2 / Done 2 = API counts over 68 rows (per-bucket server finds); Clear -> 0 group headers, 1 thead, 50 rows |
| records-forms.grid-personalization | 3 | row-color rules paint rows and survive reload | pass | grid view rowColor priority: after reload Medium rows bg oklch(0.932 0.032 255.6) (blue-100), Low row oklch(0.968 0.007 247.9) (slate-100) |
| records-forms.grid-personalization | 4 | column-state key not user-scoped (by design) | pass | localStorage key grid-columns-showcase_task carries no user id (but see accuracy: widths/rowHeight also land in user-scoped sys_user_preference) |
| records-forms.saved-view-management | 0 | create kanban stages draft, Publish makes it live | pass | PUT /meta/view/showcase_task.qa_kanban_g16?mode=draft (kanban, groupByField status) 200 -> ?preview=draft; POST .../publish 200; plain reload shows the 5 status columns |
| records-forms.saved-view-management | 1 | rename/default/pin/delete each write and survive reload; default unsets prior | pass | rename PUT 200; default: 2 PUTs, kanban isDefault true, showcase_task.default false; pin PUT isPinned true; delete DELETE x2 200, meta 404; each survives reload |
| records-forms.saved-view-management | 2 | non-admin lacks mutation affordances | pass | phone.demo on showcase_task: Add View 0, View actions buttons 0, More menu has no Manage all views |
| records-forms.saved-view-management | 3 | forged non-admin PUT refused server-side | pass | phone.demo PUT /meta/view/showcase_task.qa_forged_g16 -> 403 FORBIDDEN requires manage_metadata; ?mode=draft -> 403; admin GET live and ?state=draft -> 404 |
| records-forms.bulk-select-all-matching | 0 | cross-page banner reports server total | pass | 55 scratch "QA bulk NN" tasks; search find total 55, page renders 50; banner "All 50 on this page are selected. Select all 55 matching" -> "All 55 matching records are selected" |
| records-forms.bulk-select-all-matching | 1 | select-all-matching resolves every matching id | pass | resolve find GET ...?top=500&skip=0&search=QA+bulk (no page top/skip); aggregate POST carried 55 distinct ids = API $search total 55 |
| records-forms.bulk-select-all-matching | 2 | aggregate = 1 dispatch with all ids; per-record fans out | pass | aggregate: 1 POST /api/v1/apps/showcase/recalc with 55 _selectedIds (flow acted=55); Mark Done: 55 POSTs /actions/showcase_task/showcase_mark_done, API done 55/55 |
| records-forms.bulk-select-all-matching | 3 | clear resets both selection sources | pass | 50 row boxes checked before Clear; after Clear 0 checked boxes, bulk bar gone, no banner |
| records-forms.bulk-select-all-matching | 4 | fan-out bounded at HARD_CAP 5000 | pass | collected 55 lt = cap; HARD_CAP=5000 read at objectui ObjectGrid.tsx:5252-5264 (slice to cap); weak: set far below the cap |
| records-forms.lookup-picker-create-new | 0 | zero-hit user-facing lookup offers create-new opening full form | pass | BU parent picker zero-hit term: popover "No options found" + "Create new" with the typed term; click opens "Create Business Unit" full form (5 fields) |
| records-forms.lookup-picker-create-new | 1 | picker adopts new id; parent re-read resolves it | pass | child POST 201 iMVTWcsHizaVbfoE; parent field shows qa-bu-quick-c; parent POST 201 rKJbQvkc_pVTtaGL; GET parent.parent = iMVTWcsHizaVbfoE |
| records-forms.lookup-picker-create-new | 2 | typed search is a scoped server find | pass | GET /data/showcase_business_unit?populate=parent&top=50&search=QaZeroHitc8f7j (debounced: one request per settled term) |
| records-forms.lookup-picker-create-new | 3 | system reference offers no quick-create | pass | field zoo f_user (sys_user) picker, term zzQaNoUser: GET /data/sys_user?...search=zzQaNoUser, popover "No records found", 0 Create new |
| records-forms.import-job-undo-cancel | 0 | undoable job reaches terminal state with per-row results | pass | gated console spec 1 passed twice (pinRun); API job succeeded 30/30, results[] 30 per-row entries, list entry undoable true, no revertedAt key |
| records-forms.import-job-undo-cancel | 1 | undo removes exactly the imported rows | pass | marker count pre 0 / post-import 30 / post-undo 0; qa_imp total 2 -> 32 -> 2 (pre-existing rows untouched); undo deleted 30 |
| records-forms.import-job-undo-cancel | 2 | cancel mid-job leaves coherent partial | pass | 3000-row job cancelled: status cancelled processed 400 created 400; 400 committed rows, 0 torn (every row name/a pair consistent) |
| records-forms.import-job-undo-cancel | 3 | job list distinguishes undoable/reverted | pass | before undo {undoable:true} no revertedAt key; after undo {undoable:false, revertedAt:2026-10-16T12:49:52.179Z} |
| records-forms.object-hook-lifecycle | 0 | beforeInsert stamps source=web | pass | POST showcase_inquiry without status/source -> 201; GET source=web, status=new |
| records-forms.object-hook-lifecycle | 1 | one multi-event hook trims insert and update | pass | POST title " os-qa-g16mvayqg8l " -> "os-qa-g16mvayqg8l"; PATCH padded -> "os-qa-g16mvayqg8l-upd" |
| records-forms.object-hook-lifecycle | 2 | afterUpdate audit fires on completing update | pass | OS_LOG_LEVEL=info boot: PATCH Content backlog done false->true -> log "INFO [hook showcase_audit_task_completion] task completed: Content backlog" |
| records-forms.object-hook-lifecycle | 3 | audit does not fire on non-transition edit | pass | priority-only PATCH on Audit current IA and on App wireframes (done:true): 0 new "task completed" lines after 4s settle each |
| records-forms.object-hook-lifecycle | 4 | over-budget warn fires | pass | PATCH Data Platform spent 650000 -> "WARN [hook showcase_warn_over_budget] project over budget: Data Platform (650000 / 600000)" |
| records-forms.object-hook-lifecycle | 5 | record = stored+payload; under-budget no warn | pass | spent-only PATCH fired with stored budget 600000; Website Relaunch spent 70000 -> 0 warn lines |
| records-forms.object-hook-lifecycle | 6 | onError abort on throwing beforeInsert rolls back | pass | scratch qa_h_abort on qa_hk_abort: POST -> 400 {"error":"qa scratch abort hook refused"}; count 0 -> 0 |
| records-forms.object-hook-lifecycle | 7 | onError log async throw does not roll back | pass | scratch qa_h_log: PATCH val 2 -> 200, re-read val 2; log ERROR "[hook] handler failed (onError=log; suppressing)" hook qa_h_log |
| records-forms.object-hook-lifecycle | 8 | unevaluable condition fails closed | pass | scratch qa_h_cond condition int(record.name)>0, name abc: POST -> 500 INTERNAL_ERROR, count 0 -> 0; log "HookConditionError: ... could not evaluate its condition ... operation aborted" |
| records-forms.object-hook-lifecycle | 9 | same object+event runs lowest priority first | pass | scratch afterInsert qa_h_p10 / qa_h_p90: log p10 at 12:49:28.135Z then p90 at 12:49:28.178Z |
| records-forms.field-type-constraints | 0 | length is reject-not-truncate | pass | 201-char name -> 400 max_length {maxLength:200,actual:201}, count 2->2; 200-char -> 201, re-read length 200 |
| records-forms.field-type-constraints | 1 | number range both sides | pass | f_number -1 min_value {min:0}; 1001 max_value {max:1000}; abc invalid_number; 500 re-reads 500 |
| records-forms.field-type-constraints | 2 | KNOWN GAP scale not enforced (recorded) | pass | f_currency 1234.567 (scale 2) -> 201, GET 1234.567 unrounded - gap holds as recorded, no new enforcement |
| records-forms.field-type-constraints | 3 | closed-set rejects on 2 carriers | pass | f_select not-a-value -> invalid_option options[low,medium,high]; invoice status archived -> invalid_option options[draft,sent,paid,void]; counts zoo 5->5 invoice 12->12 |
| records-forms.field-type-constraints | 4 | multiselect array shape | pass | "red" -> ["red"]; {x:1} -> invalid_type; ["red","purple"] -> invalid_option naming purple; ["red","blue"] -> ["red","blue"] |
| records-forms.field-type-constraints | 5 | dangling reference rejected; existing id verbatim | pass | f_lookup nonexistent -> reference_not_found; real account id re-reads equal (out-of-scope acceptance shown in cascading clause 4) |
| records-forms.field-type-constraints | 6 | autonumber server-assigned, client value ignored | pass | POST f_autonumber HACK-9999 -> stored 0009, header x-objectstack-dropped-fields f_autonumber;reason=readonly; PATCH HACK-1 -> 200, still 0009 |
| records-forms.field-type-constraints | 7 | formula read-only; summary maintained cache | pass | f_number 42 f_percent 75 f_formula 999 -> f_formula 31.5; new invoice total 777 kept, self-healed to 0 then 35 = line sum (2x10+3x5); seeded invoices 4/4 total = line sums |
| records-forms.cascading-multilevel-and-clear | 0 | select client clear-on-change | pass | New Cascading Select: cn -> province options Zhejiang,Guangdong, pick Zhejiang; switch us -> province "Select an option", options California,Texas; submit body province null |
| records-forms.cascading-multilevel-and-clear | 1 | select server written-value gate | pass | POST {cn,ca} -> 400 invalid_option province "option ca is not available"; {cn,zj} -> 201 |
| records-forms.cascading-multilevel-and-clear | 2 | KNOWN GAP parent-only PATCH keeps stale child (recorded) | pass | create {cn,zj}; PATCH {country:us} -> 200; GET country us province zj - gap holds as recorded |
| records-forms.cascading-multilevel-and-clear | 3 | lookup twin re-queries scoped and clears | pass | contact picker GET filter ["account","=",Northwind id] -> 26 options; pick Nora West; switch account Contoso -> contact field empty, GET filter Contoso id -> 2 options (Cara Ito, Carl Fox) |
| records-forms.cascading-multilevel-and-clear | 4 | KNOWN GAP lookup scope not server-enforced (recorded) | pass | POST invoice account Contoso + Northwind contact -> 201 stored; fabricated contact -> 400 reference_not_found target showcase_contact |
| records-forms.cascading-multilevel-and-clear | 5 | legal cascade persists consistently | pass | {cn,zj} re-reads cn/zj; invoice Northwind + Northwind contact re-reads both ids |
| records-forms.encrypted-field-behavior | 0 | secret write stores ciphertext | pass | POST f_secret topsecret-value -> 201 resp mask; SQLite column f_secret = secret:sec_7107e0f1...; sys_secret row alg aes-256-gcm kms local:v1 ciphertext v2:...; 0 sys_secret rows containing plaintext |
| records-forms.encrypted-field-behavior | 1 | reads mask for every reader | pass | GET f_secret and f_password both "••••••••"; f_password at rest plaintext (column p@ssw0rd!), masked on read |
| records-forms.encrypted-field-behavior | 2 | echoed mask is a no-op | pass | PATCH f_secret=mask -> 200; column still secret:sec_7107e0f1...; sys_secret row version 1, updated_at == created_at |
| records-forms.encrypted-field-behavior | 3 | fail-closed without provider (pin-only) | pass | packages/objectql/src/secret-fields.test.ts green in batch (fail-closed case) - no stock boot runs unwired (knownGap) |
| records-forms.encrypted-field-behavior | 4 | credential column not aggregable | pass | POST /data/showcase_field_zoo/query groupBy f_secret -> 400 INVALID_FIELD "Cannot aggregate credential field(s)"; max(f_password) -> 400 same; control groupBy f_select -> 200 |
| records-forms.field-group-visible-when | 0 | both spellings accepted; served as authored | pass | PUT qa_group_gate bare "record.kind == pro" -> 200, GET same bare string; PUT envelope {dialect:cel,source} -> 200, GET envelope; batch20 normalization test green |
| records-forms.field-group-visible-when | 1 | visibleOn refused with prescription | pass | PUT visibleOn -> 422 INVALID_METADATA unrecognized_keys naming visibleWhen + "FALSE hides the whole group, header included"; re-read still the envelope |
| records-forms.field-group-visible-when | 2 | derivation carries predicate verbatim | pass | spec field-group-layout.test.ts "visibleWhen passthrough" 5 cases green (bare + envelope) |
| records-forms.field-group-visible-when | 3 | console gates section on form and detail | pass | form: kind basic -> no "Pro details"; flip pro -> section appears live; back basic -> gone. detail (highlightFields [kind]): basic record no Pro details, pro record shows Pro details card |
| records-forms.field-group-visible-when | 4 | display-tier: hidden values survive save | pass | basic record pro_a PREFILL-A / pro_b PREFILL-B; form edit of note only -> PATCH {"note":"display-tier-probe-a"}; re-read pro_a/pro_b unchanged |
| records-forms.field-unique-enforcement | 0 | colliding create refused 409 UNIQUE_VIOLATION, count unchanged | pass | POST sku A-1 twice -> 409 UNIQUE_VIOLATION, developerMessage "Duplicate record refused on qa_unique: a unique constraint already holds these values. No record was written."; no driver text; count 1->1 |
| records-forms.field-unique-enforcement | 1 | NULL-distinct | pass | two POSTs without sku -> 201, 201 |
| records-forms.field-unique-enforcement | 2 | update onto taken value refused; no self-collision | pass | PATCH r2 sku A-1 -> 409, re-read B-1; PATCH r1 nick only -> 200 |
| records-forms.field-unique-enforcement | 3 | updateMany refuses whole batch before mutating | pass | POST /data/qa_unique/updateMany atomic stamping C-1 on 2 rows -> failed 2 (ROLLED_BACK + UNIQUE_VIOLATION), both rows unchanged M-1/M-2; predicate driver path from memory pin |
| records-forms.field-unique-enforcement | 4 | scope matrix both families | pass | memory-unique-constraint.test.ts 26 passed + sql 8577/respelling 65 passed; live global gsku dup -> 409; two-org live probe not provisioned |
| records-forms.field-unique-enforcement | 5 | autonumber collision converges | pass | memory-unique-constraint.test.ts resync case green (pin verdict) |
| records-forms.delete-behavior-matrix | 0 | set_null clears scalar FK | pass | contact -> account; DELETE account 200; contact re-read 200 account null |
| records-forms.delete-behavior-matrix | 1 | multi-value removes member, writes remainder | pass | f_lookups [a2,a3]; DELETE a2 200; f_lookups [a3] |
| records-forms.delete-behavior-matrix | 2 | master_detail cascades | pass | invoice with 2 lines; DELETE invoice 200; line list total 0 |
| records-forms.delete-behavior-matrix | 3 | restrict 409 DELETE_RESTRICTED envelope | pass | scratch restrict child: 409 DELETE_RESTRICTED, dependentObject qa_restrict_child, dependentCount 1, developerMessage names deleteBehavior cascade; parent kept (admin shape) |
| records-forms.delete-behavior-matrix | 4 | required FK escalates to restrict | pass | account referenced by invoice: DELETE -> 409 DELETE_RESTRICTED dependentObject showcase_invoice dependentCount 1 "account is required"; control unreferenced account DELETE 200 |
| records-forms.delete-behavior-matrix | 5 | master_detail + set_null closed at both seams | pass | PUT qa_md_setnull master_detail deleteBehavior set_null -> 422 INVALID_METADATA (not stored, GET 404); seam coercion+log from engine-cascade-delete pins |
| records-forms.record-clone-contract | 0 | enable.clone gate both ways | pass | qa_noclone clone -> 403 CLONE_DISABLED "Cloning is disabled for object qa_noclone", count 1->1; showcase_account clone -> 201 |
| records-forms.record-clone-contract | 1 | overrides win but cannot forge protected columns | pass | override name lands; forged created_by/created_at re-derived (caller, now); readonly approval override "approved" -> clone holds default "pending" |
| records-forms.record-clone-contract | 2 | 201 body bare per CloneDataResponseSchema | pass | top-level keys object,id,sourceId,record; id != sourceId; sourceId = source; no wrapper |
| records-forms.record-clone-contract | 3 | internal fields never ride the 201 (pin) | pass | metadata-protocol search-clone-schema-conformance 8 passed + protocol.write-response-internal-fields.tripwire 18 passed; rest search-clone-route conformance 6 passed (no internal-field object live) |
| records-forms.record-clone-contract | 4 | not-found refusals are the shared ones | pass | unknown id -> 404 RECORD_NOT_FOUND; unregistered object -> 404 OBJECT_NOT_FOUND, identical to list on the same name |
| records-forms.field-history-tracking | 0 | tracked edit lands old->new summary with labels | pass | PATCH Contoso industry technology->finance: sys_activity updated "Industry: Technology -> Finance" (labels, not keys); History tab renders the same |
| records-forms.field-history-tracking | 1 | untracked edit has no field diff | pass | PATCH website only: sys_activity "Updated Account Contoso" (generic, no website diff) |
| records-forms.field-history-tracking | 2 | History tab gates on object-level flag both ways | pass | account detail tabs Details/Invoices/Projects/Related/History; task detail renders no tab strip, no History |
| records-forms.field-history-tracking | 3 | derived history op follows get+trackHistory | pass | /auth/me/permissions apiOperations: showcase_account includes history; showcase_task lacks it |
| records-forms.field-history-tracking | 4 | tracked reference renders titles (pin) | pass | plugin-audit audit-lookup-summary.test.ts green (3-file batch 44 passed); no stock tracked lookup |
| records-forms.import-transform-matrix | 0 | each supported transform does its documented conversion | pass | none: Full Name/E-mail copied; map: Webform -> website, "Carrier Pigeon" passthrough; constant: a=CONST (source SRC_IGNORED dropped); split " left , right " -> b=left c=right; join x1,"",z1 -> x1-z1 |
| records-forms.import-transform-matrix | 1 | javascript refused loudly before any row | pass | qa_imp_js import -> 400 UNSUPPORTED_TRANSFORM "...no server-side sandbox..."; qa_imp count 1->1 |
| records-forms.import-transform-matrix | 2 | lookup copies raw value, resolves downstream | pass | Account=Northwind -> acct = Northwind id wc8Pg3BYCnfyC2a_; NoSuchAccountQA row failed reference_not_found (row 2), not landed |
| records-forms.import-transform-matrix | 3 | format guard | pass | JSON rows via csv mapping -> 400 MAPPING_FORMAT_MISMATCH naming csv and json; xlsxBase64 via same mapping -> 200 created 1 with every transform applied |
| records-forms.predicate-relationship-traversal | 0 | write passes when parent does not trip | pass | POST invoice account Initech (active) -> 201, readable; no fault |
| records-forms.predicate-relationship-traversal | 1 | refused with authored message, not a fault | pass | churned account -> 400 VALIDATION_FAILED rule_violation "This account has churned - reactivate it before invoicing." (no could-not-be-evaluated) |
| records-forms.predicate-relationship-traversal | 2 | repoint on update judged on merged record | pass | PATCH existing invoice account -> churned id -> 400 same authored message; account unchanged |
| records-forms.predicate-relationship-traversal | 3 | unreadable parent still enforces (pin) | pass | objectql engine-predicate-relationship.test.ts green (system-authority read, accept/refuse, prior-row FK) |
| records-forms.script-action-hook-refusal-toast | 0 | refusal is 4xx carrying the hook sentence | pass | POST /api/v1/actions/showcase_task/showcase_reopen_task -> 400 VALIDATION_ERROR message = TASK_REOPEN_REFUSAL verbatim (2 fresh runs) |
| records-forms.script-action-hook-refusal-toast | 1 | exactly one new error toast with the sentence | pass | baseline 0 live toasts; within 1.2s 1 node data-type=error text = TASK_REOPEN_REFUSAL; delta 1 (both runs) |
| records-forms.script-action-hook-refusal-toast | 2 | toast belongs to this request | pass | only non-GET in window: POST /api/v1/actions/showcase_task/showcase_reopen_task; toast text == error.message |
| records-forms.script-action-hook-refusal-toast | 3 | record left unchanged | pass | Audit current IA before done true/progress 100/updated_at 12:49:18.931Z; after identical (both runs) |
Reproduction rules (fails)
None: no clause failed.
Derived item verdicts
39 pass · 1 partial · 0 fail · 0 blocked · 0 not-run (of 40).
Pin runs (Tier 1, RUNNER rule 6):
records-forms.crud-roundtrip: packages/metadata-protocol: vitest run src/protocol.readonly-insert.test.ts → 11 passed; packages/objectql: vitest run src/engine-insert-static-readonly-strip.test.ts → 21 passedrecords-forms.field-type-matrix: cd packages/qa/dogfood && pnpm exec vitest run test/field-zoo-roundtrip.dogfood.test.ts test/field-zoo-value-shape.test.ts test/action-params-contract.dogfood.test.ts test/showcase-search.dogfood.test.ts --maxWorkers=1 → 4 files, 101 passed / 0 failedrecords-forms.view-type-gallery: examples/app-showcase: OS_TEST_CHROMIUM_EXECUTABLE_PATH=/opt/pw-browsers/chromium SMOKE_API_URL=http://localhost:41280 pnpm exec playwright test --grep "<7 view surfaces>" → 7 passed / 0 failedrecords-forms.action-param-widgets: cd packages/qa/dogfood && pnpm exec vitest run test/field-zoo-roundtrip.dogfood.test.ts test/field-zoo-value-shape.test.ts test/action-params-contract.dogfood.test.ts test/showcase-search.dogfood.test.ts --maxWorkers=1 → 4 files, 101 passed / 0 failedrecords-forms.import-wizard-encoding-and-hints: objectui checkout daeb77fc (repo root): pnpm exec vitest run <10 files> --maxWorkers=1 → 124 passed / 0 failed (import files: importParsers 57, importMissingRequiredHint 2, importLegacyReferenceGuard 3)records-forms.gantt-interactions: objectui checkout daeb77fc (repo root): pnpm exec vitest run <10 files> --maxWorkers=1 → 124 passed / 0 failed (gantt: autoscheduledlg 4, summaryedit 8, deptypes 2, selfextent 8, countinterp 5, dnd 21)records-forms.form-dirty-guard: objectui checkout daeb77fc (repo root): pnpm exec vitest run <10 files> --maxWorkers=1 → 124 passed / 0 failed (discardGuard.test.tsx 14)records-forms.calendar-interactions: objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): pnpm exec vitest run --maxWorkers=1 packages/plugin-calendar/src/CalendarView.dnd.test.tsx -> 8 passed (in an 8-file batch, 56/56)records-forms.concurrent-edit-conflict: objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): vitest run packages/plugin-form/src/occSave.test.tsx -> 8 passed. e2e/live/inline-edit-polish-2572.spec.ts not run (driven by hand instead)records-forms.record-discussion-mentions: framework: plugin-audit vitest capability-gate-update-verb.test.ts (+2 audit files) 44 passed; qa/dogfood comments-permission-matrix.dogfood.test.ts (4-file batch 64 passed). objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): recordActivityFeed.test.ts 61 passedrecords-forms.grid-personalization: objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): vitest groupedPagination/groupedBooleanLabel/inlineEditPersistence.test.tsx -> 7+2+1 passedrecords-forms.bulk-select-all-matching: objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): vitest objectBulkActionDispatch/BulkActionBar/bulkActionRefresh.test.tsx -> 12+7+11 passedrecords-forms.lookup-picker-create-new: objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): vitest LookupField.dependsOn.test.tsx 6 passed + RecordPickerDialog.filterOptions.test.tsx 12 passedrecords-forms.import-job-undo-cancel: objectui daeb77fc e2e/import-console/import-console-undo.spec.ts (scratch copy in GROUPDIR/pw, playwright 1.62.1, executablePath /opt/pw-browsers/chromium, IMPORT_CONSOLE_LIVE=1, LIVE_IMPORT_APP=showcase_app LIVE_IMPORT_OBJECT=qa_lead) -> 1 passed (21.2s) and 1 passed (10.2s); gate-off control -> 1 skippedrecords-forms.object-hook-lifecycle: framework: qa/dogfood showcase-public-form + hook-error-format (in 4-file batch 64 passed); objectql hook-binder.test.ts 26 passedrecords-forms.field-type-constraints: framework: objectql engine-lookup-referential-integrity + engine-autonumber-runtime-owned (in 8-file batch 242 passed); qa/dogfood field-zoo-roundtrip (4-file batch 64 passed)records-forms.cascading-multilevel-and-clear: framework: objectql rule-validator.option-visibility.test.ts (in 8-file batch 242 passed). objectui e2e/live/cascading-options.spec.ts not run (driven by hand)records-forms.encrypted-field-behavior: framework: objectql secret-fields.test.ts (in 8-file batch 242 passed); qa/dogfood field-zoo-roundtrip (64 passed)records-forms.field-group-visible-when: framework: spec vitest object-strictness-batch20 + field-group-layout -> 70 passed; objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): record-details.groupVisibleWhen-11630.test.tsx 12 passed, sectionVisibleWhen-6111.test.tsx 25 passedrecords-forms.field-unique-enforcement: framework: driver-memory memory-unique-constraint.test.ts 26 passed; driver-sql 8577-tenant-scoped + organization-respelling 65 passedrecords-forms.delete-behavior-matrix: framework: objectql engine-cascade-delete + -atomic + -multivalue-probe (in 8-file batch 242 passed)records-forms.record-clone-contract: framework: metadata-protocol search-clone-schema-conformance 8 + write-response tripwire 18; rest search-clone-route-schema-conformance 6 passedrecords-forms.field-history-tracking: framework: plugin-audit audit-lookup-summary + audit-option-label-summary (44 passed in 3-file batch)records-forms.predicate-relationship-traversal: framework: objectql engine-predicate-relationship.test.ts (in 8-file batch 242 passed)records-forms.script-action-hook-refusal-toast: framework: examples/app-showcase test/task-reopen-hook-refusal.test.ts 5 passed; runtime nested-hook-refusal-is-a-rejection.test.ts 5 passed; objectui daeb77fc (checkout /home/user/objectstack-qa-console/.cache/objectui-daeb77fc84dd, run from repo root): consoleServerAction.errorToast.test.ts 6 passed
Fixture gaps
records-forms.gantt-interactionsclause 3: no veto path on stock: the task gantt has no host onBeforeTaskUpdate and showcase_task has no date rule the server would refuse; no objectui pin covers acceptance[3]records-forms.gantt-interactionsclause 2: direct drag of a locked LEAF bar still unreachable live (stock view declares no lockField) — sub-leg blocked(fixture); clause scored from pinsrecords-forms.action-location-matrixclause 4: half (a) needs a PACKAGED action with no locations key; none ships on stock (knownGaps) — scored on half (b)records-forms.field-unique-enforcementclause 4: live two-organization collision probe not provisioned (needs 2 orgs); scored from memory + SQL pinsrecords-forms.delete-behavior-matrixclause 3: lesser-persona DELETE_RESTRICTED shape (count may be omitted) not driven; admin shape observedrecords-forms.cascading-multilevel-and-clearclause null: no 3-level dependsOn fixture (knownGap, no clause)
Observation (not a clause fail, not filed)
records-forms.conditional-rules-grid: on a grid save blocked client-side (quantity 120 with an empty required description), the client still sent the batch. The server refused it with 400 and wrote nothing, so the authority held. The objectui pin asserts only the inline required flag. This is noted for the next author of the item, not extracted: no user-visible wrong result.
Checklist-accuracy findings from this area close out on the wave anchor #23878; they are not extracted.
Activity
objectstack-fleet commentedon Oct 16, 2026
Close-out for this record (checklist-test §5), 2026-10-16T18:19Z. 39 pass · 1 partial · 0 fail of 40. There is no
failrow. The one partial's gap is parked on the wave anchor:The record's one observation (conditional-rules-grid: a client-blocked grid save still sends the batch, which the server refuses with 400 and writes nothing) was rechecked on #23878 (6218243967, item 13). It is by design: there is no user-visible wrong result. The other fixture gaps and the checklist-accuracy findings close out on #23878. Closed by the
domain:devxseat 1 PM (seat post #6023) ·os-sales·session_014eFvbvGK4FjXvscggDNTD4, as part of the paced close-out of the #23878 wave.Generated by Claude Code