Skip to content

QA run · release-18.3.0-w4-l3 (41/41) · 7fa9873f · 2026-11-02 · 37 PASS / 2 PARTIAL / 2 FAIL / 0 BLOCKED / 0 NOT-RUN #29172

Open

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB
Part of #27377 (wave 4, W4-L3)

QA run record for the 18.3.0 release-candidate sweep, wave 4 card W4-L3: the Tier 1 live legs for records-forms and studio-authoring. The wave-4 Tier 1 card W4-C2b (record #28965) ran each item's automated.ref only, so it left these 41 items PARTIAL. This card drove every clause #28965 marked partial, skipped or blocked on a booted stack (API, DB, logs, and Playwright Chromium against the Console built from the subject). Clauses #28965 scored pass keep that verdict and are cited, not re-run. Item verdicts are derived from the clause verdicts, never hand-assigned. Run under .claude/skills/checklist-test/SKILL.md and docs/qa/platform-checklist/RUNNER.md. It is a QA run, not a lane card; Warren approved the live-leg cards on 2026-11-02 07:06Z.

Result: 41/41 judged · 37 PASS / 2 PARTIAL / 2 FAIL / 0 BLOCKED / 0 NOT-RUN. No item is planned. Clause rows: 304 in all. 60 are pins passes cited from #28965. Of the 244 judged live, 237 pass, 3 fail, 3 partial and 1 is blocked.

Environment fingerprint

  • Framework: objectstack-ai/objectstack @ 7fa9873f38f7a0158e3ce17338dcc0f446b113e3 (main, 2026-11-02 02:44Z), detached in its own worktree. It never followed main or the Version PR chore: version packages #26517. ui/ is the same commit.
  • Build: npm_config_nodedir=/opt/node22 pnpm install --frozen-lockfile (26 s), then pnpm exec turbo run build --concurrency=2 (118/118 tasks, exit 0, 21m58s), then pnpm console:build (71/72 cached, plus the copy into packages/console/dist). check-dev-prereqs: 106 build artifacts present. Node v22.23.4, pnpm 10.31.0, Playwright Chromium build 1243 from /opt/pw-browsers, 4 CPUs, about 15 GiB.
  • Boot, one per runner and more where an item needed them: a scratch copy of examples/app-showcase (tar without node_modules and .objectstack, node_modules symlinked to the tree), started from the copy with OS_PORT=PORT OS_STORAGE_LOCAL_ROOT=RUN/uploads OS_HOME=RUN/home setsid node packages/cli/bin/run.js dev --ui --no-watch --seed-admin -p PORT -d file:RUN/data.db. A canary boot answered /api/v1/health 200 after 22 s, served /_console/ 200 and listed showcase_app before any runner started.
  • Other boots, each on its own port and file DB: OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1 (flow-status-matches-runtime, schedule half); a walled group boot with OS_TENANCY_POSTURE=group OS_PLATFORM_OWNER_EMAIL=admin@objectos.ai OS_AUTH_MEMBERSHIP_POLICY=invite-only (record-discussion-mentions A9 cross-org leg); an examples/app-crm scratch copy (import-job-undo-cancel, recipe qa-crm-import-console).
  • Ports 41910–41964, a block of 5 per runner. Seed: --seed-admin (admin@objectos.ai), the stock showcase seed and demo personas. Members were minted by invite then sign-up, or by admin create-user, and each persona's identity was proved with GET /api/v1/auth/get-session before its cells.
  • Execution: 6 runner subagents (opus, effort high). At most 3 browser-heavy runners ran at once, and each drove its items one after another with one browser. The orchestrator checked every FAIL against its runner's reproduction rule and the source. It reproduced R-1 itself over the API on a fresh boot (400 on the action door, 200 on the target).
  • Hygiene: after the run, git status is clean, examples/app-showcase/.objectstack and examples/app-crm/.objectstack do not exist, and no listener remains on any run port.

Scope

Selector release-18.3.0-w4-l3: the item list of card W4-L3 in the wave-4 live-leg brief, computed from the ledger at the subject. The per-item target was every clause the pins record #28965 left partial, skipped or blocked. No planned items. Revisions were pinned from the ledger at the subject, and all match the item file and #28965:

records-forms.action-param-widgets r6 · records-forms.adhoc-filter-sort-builder r8 · records-forms.bulk-select-all-matching r6 · records-forms.cascading-multilevel-and-clear r9 · records-forms.cascading-options r7 · records-forms.concurrent-edit-conflict r5 · records-forms.conditional-rules-grid r11 · records-forms.conditional-rules-header r8 · records-forms.delete-behavior-matrix r5 · records-forms.field-history-tracking r11 · records-forms.field-type-constraints r6 · records-forms.field-type-matrix r8 · records-forms.field-unique-enforcement r13 · records-forms.form-dirty-guard r7 · records-forms.gantt-interactions r8 · records-forms.import-job-undo-cancel r8 · records-forms.import-wizard-encoding-and-hints r9 · records-forms.list-view-capabilities r8 · records-forms.master-detail-atomic-save r9 · records-forms.record-clone-contract r3 · records-forms.record-discussion-mentions r13 · records-forms.related-list-server-pagination r11 · records-forms.v18-semantic-defaults r6 · records-forms.view-type-gallery r3 · studio-authoring.actions-tab-presets r3 · studio-authoring.automations-rail-and-new-flow r2 · studio-authoring.flow-status-matches-runtime r2 · studio-authoring.held-edit-publish-refusal r3 · studio-authoring.hooks-tab-authoring r3 · studio-authoring.interfaces-create-dialogs r2 · studio-authoring.meta-save-preconditions-and-draft-rows r2 · studio-authoring.metadata-diagnostics-sweep r10 · studio-authoring.multi-package-item-rows r4 · studio-authoring.object-api-tab-curl r2 · studio-authoring.object-designer-roundtrip r10 · studio-authoring.org-override-registry-gate r7 · studio-authoring.org-scope-flows r2 · studio-authoring.permission-matrix-editor-ux r7 · studio-authoring.record-page-roundtrip r11 · studio-authoring.save-door-runs-authoring-rules r4 · studio-authoring.validation-rules-authoring r3

The per-clause verdict table is in the three comments below this body.

Derived item verdicts

37 PASS / 2 PARTIAL / 2 FAIL / 0 BLOCKED / 0 NOT-RUN (41 of 41 judged).

  • FAIL (2): records-forms.list-view-capabilities (A8, R-1), records-forms.adhoc-filter-sort-builder (A3, N2, R-2)
  • PARTIAL (2):
    • records-forms.field-unique-enforcement: A3 and N2 are partial. The atomic REST updateMany half was driven live (refused ROLLED_BACK + UNIQUE_VIOLATION, both rows re-read byte-identical). The predicate-update half is blocked(fixture): no REST door takes a predicate update at the subject (batch updateMany is per id), so that half rests on the memory-driver pin.
    • records-forms.gantt-interactions: A1 is blocked(fixture), because auto-schedule needs a dependenciesField that stock showcase_task does not declare and a field overlay is refused on stock (item knownGaps[0]). A2 is partial: the direct drag on a locked bar was proven live on a lockField: 'done' view overlay (no PATCH, row unchanged; the control drag lands PATCH 200), and its "survives auto-schedule" half is blocked for the same reason.
  • PASS (37): every other item.

Delta against the pins record #28965 (W4-C2b, same subject)

PARTIAL→PASS: 37 · PARTIAL→PARTIAL: 2 (field-unique-enforcement, gantt-interactions) · PARTIAL→FAIL: 2 (list-view-capabilities, adhoc-filter-sort-builder). Every one of the 41 items was PARTIAL there because no live leg ran. The two FAILs sit on clauses #28965 recorded skipped, so the pins themselves did not regress.

Delta against wave 3 (#27438, at c4c515ad)

item #27438 here why
records-forms.field-history-tracking FAIL PASS A2: wave 3 looked for the History tab that record page v3 removed (#27055), so the clause was re-pointed to a network oracle (#27492, rev 11). Live here on 2 fresh loads: the account detail made exactly 1 sys_audit_log read and its Changes chip read "Industry Finance → Technology"; the task detail made 0. Scratch objects isolate the flag half: per-field flags alone gave 0 reads, enable.trackHistory gave 1
studio-authoring.object-api-tab-curl FAIL PASS A1/A2/N0: wave 3 met related_record_id in every reference and in owner_id, refused 400 reference_not_found (#27490). Live here: a draft-only field appears in the example body; the body leaves out owner_id and the optional lookup and shows the required lookup as <id of a showcase_contact record>; after substitution the five copied cURLs answered list 200, create 201, get 200, PATCH 200, DELETE 200. #27613's fix holds
records-forms.list-view-capabilities PASS FAIL A8: the console now sends the aggregate api action through the action door (#27611), and that door refuses a record-less dispatch 400 (R-1). Wave 3 scored the request against the direct target URL the clause still names
records-forms.adhoc-filter-sort-builder PASS FAIL A3/N2: driven this time through the realistic same-browser path (account-menu sign-out, then sign-in on the login page that opens), which carries the previous user's uf__* URL state (R-2). The per-user storage keys themselves are clean
records-forms.field-unique-enforcement PASS PARTIAL A3/N2 predicate half: no REST predicate-update door at the subject; scored partial with the half named (RUNNER rule 6), not pass
records-forms.gantt-interactions PARTIAL PARTIAL auto-schedule still needs a dependenciesField fixture
the other 35 PASS PASS unchanged

Wave-3 FAIL delta lines for the anchor:

Reproduction rule for every fail

R-1 · records-forms.list-view-capabilities · A8 (product defect; card in the close-out)

  1. Stock showcase boot; form sign-in as admin@objectos.ai.
  2. Open /_console/apps/com.example.showcase/showcase_task/view/bulk_actions; tick three rows; click getByTestId('bulk-action-showcase_recalc_selection'); in the dialog click the "Run" button.
  3. Network: POST /api/v1/actions/showcase_task/showcase_recalc_selection {"params":{"_selectedIds":[ID1,ID2,ID3]}} → 400 VALIDATION_ERROR "Action "Recalculate Selection" identifies its record by "id", but that field is not present on the record — running without it would send a request that names no record." The dialog reads "Succeeded 0 / 3 · Failed 3"; the target is never called.
  4. Expected: ONE aggregate dispatch carrying every selected id reaches the recalc target and succeeds.
  5. Controls: the same body plus recordId → 200; the target POST /api/v1/apps/showcase/recalc {"_selectedIds":[…]} called directly → 200 (selected: 2, acted: 2); the per-record bulk action (Mark Done) on the same bar succeeds.
    Reproduced on 2 fresh browser loads (3-row and 2-row selections), by a forged API call, and by the orchestrator on a fresh boot. Mechanism (read at the subject): packages/runtime/src/action-execution.ts — a record-less dispatch reaches buildApiActionRequest with input.record = {}; the recordIdParam branch (around L1428–1458) treats any object as a row, finds no id and refuses, although its own comment says a record-less dispatch should go out without the param.

R-2 · records-forms.adhoc-filter-sort-builder · A3, N2 (product defect; card in the close-out)

  1. Stock showcase boot. Mint a second member B (invite, then sign-up).
  2. In a fresh browser context, account A (admin) signs in through the form and opens /_console/apps/com.example.showcase/showcase_task/view/in_progress. A adds two toolbar conditions in the filter builder (toolbar-filter-trigger: Estimate (h) greater than 10, Progress less than 80), searches "pipeline" and sorts Due Date newest first.
  3. A opens the account menu and clicks "Sign out". The login page opens at /_console/login?redirect= + the list URL, with its uf__filter, uf__sort and uf__search parameters.
  4. On that page B signs in. GET /api/v1/auth/get-session reads B.
  5. Expected: B's view opens with no toolbar filter, search or sort authored by A. Actual: B lands on A's list URL. B's toolbar shows A's 2 conditions, A's sort and A's search term. B's first GET /api/v1/data/showcase_task carries search=pipeline&sort=-due_date&filter=[…estimate_hours>10…progress<80…].
    Reproduced 2/2 in fresh browser contexts. The user-keyed storage is clean: A's list-state:<A id> key and sys_user_preference row are A's, B has neither, and B gets 404 on A's preference row by id. So the vehicle is the redirect URL, not a cache. The rows B sees are still read under B's own permissions. Mechanism: ui/packages/app-shell/src/console/auth/LoginPage.tsx follows ?redirect= verbatim (L95), and the signed-out guard composes that redirect from the full path and query, which carries the list's URL-mirrored state (ui/packages/app-shell/src/views/userFilterUrlState.ts).

Fixture gaps (for the anchor, not cards)

  • records-forms.gantt-interactions: auto-schedule (A1, and the second half of A2) needs a dependenciesField on the task gantt; stock showcase_task declares none and no overlay path adds one.
  • records-forms.field-unique-enforcement: no REST predicate-update door, so the predicate half of A3/N2 is memory-pin only.
  • studio-authoring.org-scope-flows: the clone fixture is stale. The clone door writes into com.example.showcase.custom (ADR-0146 D4). The package-less flow was minted with a raw package-less PUT /meta/flow, which ADR-0146 D11 schedules to close; after that this item has no fixture.
  • records-forms.conditional-rules-grid: stock INV-1002 ships one line, so the second line is added per run (as fixtures say).
  • records-forms.cascading-multilevel-and-clear: no 3-level dependsOn fixture (knownGaps, unchanged).
  • records-forms.bulk-select-all-matching: staging the >5000-row cap fixture costs about 15 minutes on 4 CPUs (createMany of 200 tasks takes 23–43 s because flows fire per row).
  • records-forms.field-type-matrix: f_user / f_users / f_owner hold no value on "Specimen — Full", so their pickers render an empty "Select…" (the widget type is still observable).
  • studio-authoring.record-page-roundtrip: no stock non-admin persona reads showcase_contact, so A3's end-user leg ran as admin (as personas[1] allows).

The checklist-accuracy findings (stale steps, routes and labels; the stale knownGap of bulk-select-all-matching) are listed in the anchor comment on #27377. They are not cards.

Off-clause probes (RUNNER rule 8)

About 51 minutes summed across the six runners (7 + 8 + 9 + 12 + 6 + 9), on each runner's own boot.

  • Doors: clone, OCC PATCH/DELETE, unique writes, updateMany, field-zoo constraint writes, the action door (unknown action/object/recordId, missing and undeclared params), /meta draft PUT with If-Match boundary values, /meta/_drafts, package revert, /meta/diagnostics, sys_comment create/patch/read, notifications read, sync import and import jobs (undo, cancel, results), the permission fields map, flow and app meta PUTs, the page aggregate's related rows, the export door, sys_user_preference, invoice line writes under a paid invoice, /api/v1/batch, and console flows (form clear, sort reset, Display popover, sign-out redirect, page publish).
  • Personas: the seeded admin, plain members, contributor-bound and manager-bound members, a non-authoring demo persona (phone.demo), a member holding only a package permission set, and a notification bystander.
  • Boundary classes: empty string and whitespace, missing id, unknown id, object, type or package, malformed tokens, undeclared extra input, out-of-range values, empty selections, readers and writers outside the asserted scope, and the opposite posture (doors the UI hides).
  • Found: seven product defects that pass the filing gate, extracted (numbers in the close-out). Plus one security finding:
    records-forms.record-discussion-mentions · observed beside the clauses · security finding — detail withheld, reported to PM
    A probe scores no clause, so no item verdict moves on it.
  • Observed, not extracted: the Sort popover's "Reset to view default" stayed disabled while a user sort differed from the view's empty declared sort. Its mechanism is untraced, and List personalization: 「恢复视图默认」 clears the reader's stored setting instead of copying the default into it — today the reader stays on the old value after the view changes #28948 reworked that reset after the subject. An api-forwarded action on a record the caller cannot see answers 400 FLOW_FAILED, where the declarative door answers 404. Exporting an unregistered object answers 403 rather than 404. Cancelling a finished import job answers 200 and changes nothing. An empty CSV creates a "succeeded" job with 0 rows. A permission fields-map key naming a missing field saves with no warning. The Project Detail header reads "Discussion (1)" beside two rows. A paid invoice is deletable over the API with its lines cascading, because userActions predicates are advisory by spec (packages/spec/src/data/object.zod.ts); the conditional-rules-header clauses check only the UI menu.

Time: environment prep vs testing

  • Environment prep: orchestrator 25 min (install 26 s, full build 21m58s, console build and canary boot 2 min), runners about 30 min summed (scratch boots, personas, fixtures).
  • Testing: about 190 min summed across the six runners, including 51 min of probes. Prep to testing is about 1 : 3.5.
  • Wall clock 07:13Z to 08:45Z.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB

    Per-clause verdicts (1/3): records-forms. #28965 is the pins-only verdict of W4-C2b; here is this card's verdict. A clause the pins scored pass is cited, not re-run; every other clause was driven live on a booted stack at 7fa9873f.

    item · rev clause #28965 here layer · text evidence
    records-forms.action-param-widgets · r6 A0 skipped pass browser-live · Field Zoo > Specimen — Full row menu > Action Param Gallery dialog (screenshot first). Table: p_text=input[type=text] required; p_richtext=textarea 'Enter text…' (RichTextField); p_priority=combobox select-trigger-p_priority (Normal); p_date=data-slot=date-picker text input placeholder MM/DD/YYYY + date-picker-trigger button (not type=date); p_account=lookup-trigger-p_account record picker; p_assignee=lookup-trigger-p_assignee record picker (opened: Dev Admin, r5-member, ... names, no UUID box); p_color=input[type=color] + hex text #7C3AED; p_reference=read-only empty-value '—' (no input); p_cover=input[type=file] + 'Upload image'; p_attachments=input[type=file] dropzone
    records-forms.action-param-widgets · r6 A1 skipped pass browser-live · DOM attributes post-render: p_cover input[type=file] accept='image/' multiple=false; p_attachments input[type=file] accept='application/pdf,image/' multiple=true
    records-forms.action-param-widgets · r6 A2 pass pass pins · cited from #28965: HTTP-in-process: action-param-gallery-dispatch 'acceptance[2]: the conformant bag passes and the body echoes the keys it received' and 'acceptance[2], negative[1]: a malformed bag is refused 400 and the body never ran'; action-params-contract 'default: a malformed param bag is rejected 400 before the handler runs'
    records-forms.action-param-widgets · r6 A3 partial pass browser-live · Dialog Confirm with p_text empty: no POST to /api/v1/actions (network log empty), dialog stays open, p_text aria-invalid=true with 'Title is required' (screenshot). Server: direct dispatch without p_text -> 400 VALIDATION_ERROR 'Action param "p_text" is required', p_text '' -> same 400, no received echo
    records-forms.action-param-widgets · r6 N0 skipped pass browser-live · Scope pointer honored: neither upload input carries a max-size attribute, and maxSize was not claimed from it; a 6 MB file set on p_cover renders '"big-6mb.png" exceeds max size (5.0 MB)' with no storage request sent (JS enforcement). Full guard scoring stays with records-forms.upload-guard-blocks-confirm
    records-forms.action-param-widgets · r6 N1 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[2], negative[1]: a malformed bag is refused 400 and the body never ran': 400 names p_priority and bogus and carries no received echo
    records-forms.adhoc-filter-sort-builder · r8 A0 skipped pass HTTP-live · in_progress + FilterBuilder Estimate (h) > 10 AND Progress < 80 (keeps both rows): GET .../showcase_task?...&sort=-due_date&filter=["and",[["status","equals","in_progress"]],["and",["estimate_hours",">",10],["progress","<",80]]] -> 200 total 2; both rows are in_progress with estimate>10 and progress 55/45
    records-forms.adhoc-filter-sort-builder · r8 A1 skipped pass HTTP-live · SortBuilder Due Date, Newest -> Oldest: request carries sort=-due_date; response order lyjRksAo9l_hZw2P (Ingest pipeline, due 11-22) then KNK_1cKwb9OI5OOJ (Build homepage, 11-12) = server descending order over 2 rows
    records-forms.adhoc-filter-sort-builder · r8 A2 skipped pass browser-live · sidebar link Accounts then Tasks (URL drops query) then view switcher In Progress: screenshot shows filter badge 2 and sort 1; FilterBuilder lists Estimate (h) Greater than + Progress Less than, SortBuilder Due Date Newest -> Oldest; re-issued request carries the same merged filter + sort=-due_date; with a toolbar search "pipeline" the return request also carries search=pipeline
    records-forms.adhoc-filter-sort-builder · r8 A3 skipped fail browser-live · storage half holds: the one sys_user_preference ui.list.showcase_task row has user_id = admin id; LS key list-state::showcase_task; B has no row and no LS key; B opening the view fresh from /login gets only the stored status filter. But after A signs out from the account menu and B signs in on the login page it lands on, B lands on /view/in_progress?uf__filter=&uf__sort=...&uf__search=pipeline and its first request carries A's two conditions, sort and search (B toolbar: filter 2, sort 1, search "pipeline"); reproduced 2/2
    records-forms.adhoc-filter-sort-builder · r8 A4 partial pass HTTP-live · Task Triage page: Urgent tab -> URL ?uf__tab=urgent, request filter=["priority","equals","urgent"] total 2 ids [0JjFA4V05s-PcpwU, lyjRksAo9l_hZw2P]; same URL in a fresh browser context issues the identical request and ids. All Projects Status chips Planned+Active -> URL ?uf_status=planned%2Cactive, request filter ["or",status=planned,status=active] total 3; fresh context reproduces the identical request and 3 ids
    records-forms.adhoc-filter-sort-builder · r8 N0 skipped pass HTTP-live · every toolbar-filtered request kept the view node [["status","equals","in_progress"]] inside an "and" with the toolbar conditions; no row outside in_progress returned
    records-forms.adhoc-filter-sort-builder · r8 N1 skipped pass browser-live · staged overlay giving showcase_task.urgent userFilters element "tabs" (see list-view-capabilities N1): no tab chip on the object list, the ADR-0047 suppression warning logged; overlay removed. Object list URLs carried only the reserved uf__filter/uf__sort/uf__search list-state params, never uf__tab
    records-forms.adhoc-filter-sort-builder · r8 N2 skipped fail browser-live · B sees A's toolbar filter on the same browser: sign-out redirect /login?redirect=<A list URL incl. uf__filter, uf__sort, uf__search> sends B to A's list state after sign-in (see A3); the user-keyed storage itself does not leak
    records-forms.adhoc-filter-sort-builder · r8 N3 skipped pass HTTP-live · added a FilterBuilder row Priority / Equals with no value: requests issued meanwhile carried only the 2 complete conditions, no ["priority","=",""] node
    records-forms.bulk-select-all-matching · r6 A0 partial pass HTTP-live · 55 scratch tasks "QA bulk NN" (createMany 201); Bulk Actions view search "QA bulk": GET ...?top=50&search=QA+bulk -> total 55, 50 rows rendered; header checkbox -> banner "All 50 on this page are selected. / Select all 55 matching" (55 = response total); not shown before the whole page is ticked
    records-forms.bulk-select-all-matching · r6 A1 partial pass HTTP-live · after Select all 55 matching: fan-out GET .../showcase_task?top=500&skip=0&search=QA+bulk (top/skip of the page dropped); the dispatched set held 55 distinct ids = GET showcase_task?search=QA bulk&top=0 total 55
    records-forms.bulk-select-all-matching · r6 A2 partial pass HTTP-live · aggregate Recalculate Selection: ONE POST /api/v1/actions/showcase_task/showcase_recalc_selection with params._selectedIds of 55 distinct ids (the door, since #27611, forwards to /api/v1/apps/showcase/recalc; the door answered 400 - the list-view-capabilities A8 defect, so the target never ran); per-record Recalculate Estimate: 55 POSTs .../showcase_recalc_estimate, 55 distinct recordIds, 55 x 200, dialog "Succeeded 55 / 55"
    records-forms.bulk-select-all-matching · r6 A3 partial pass browser-live · Clear on the bulk bar after an all-matching selection: screenshot shows no ticked rows, header checkbox unchecked, bulk bar and cross-page banner gone (DOM: 0 checked row boxes, header data-state unchecked)
    records-forms.bulk-select-all-matching · r6 A4 partial pass HTTP-live · 5200 scratch tasks "QA cap NNNN" (26 x createMany 200, 201 each); find total 5200; after the cap select the fan-out issued 10 pages GET ...?top=500&skip=0..4500&search=QA+cap and stopped; the one aggregate dispatch carried 5000 distinct ids (< server total 5200); confirm dialog "This will apply to 5000 records"
    records-forms.bulk-select-all-matching · r6 A5 partial pass browser-live · screenshot + DOM with 5200 matches: before Select all the banner reads "All 50 on this page are selected. / Select the first 5000 of 5200 matching"; after it reads "Only the first 5000 of 5200 matching records will be processed." and the count "5000 selected (of 5200 matches)" - each names the 5000 cap; the uncapped 5200 is never presented as the processed set
    records-forms.bulk-select-all-matching · r6 N0 partial pass HTTP-live · select-all-matching dispatched 55 ids (the server match set), not the 50 rendered page rows
    records-forms.bulk-select-all-matching · r6 N1 partial pass HTTP-live · banner total 55 = filtered API count 55
    records-forms.bulk-select-all-matching · r6 N2 partial pass browser-live · after Clear both the toolbar selection and the row/header checkboxes are empty (A3)
    records-forms.cascading-multilevel-and-clear · r9 A0 partial pass browser-live · New Cascading Select: cn -> [Zhejiang, Guangdong], picked zj; switch to us -> [California, Texas], province shows 'Select an option' (screenshot); Create sent POST /api/v1/data/showcase_cascade body {tier:standard, name, country:'us', province:null} -> 201: the stale zj was not carried into the submit
    records-forms.cascading-multilevel-and-clear · r9 A1 partial pass HTTP-live · POST {country:'cn', province:'ca'} -> 400 VALIDATION_FAILED fields[{field:province, code:invalid_option, message:"Province / State: option 'ca' is not available"}] (the option_unavailable template, spec validation-message.ts:151; the key itself is not on the wire), 0 rows stored; POST {cn, zj} -> 201
    records-forms.cascading-multilevel-and-clear · r9 A2 pass pass pins · cited from #28965: HTTP-in-process: cascade-server-boundary 'acceptance[2], negative[2]: a parent-only PATCH is accepted and the stale child is kept (recorded gap)' — create {cn,zj}, PATCH {country:us} 200, re-read province zj.
    records-forms.cascading-multilevel-and-clear · r9 A3 skipped pass browser-live · New Invoice: Northwind -> GET showcase_contact filter [account = gkjxchdg3aTc_bZZ] 26 rows; picked 'Prospect 11' (field showed it); switched account to Contoso -> contact field back to 'Select…' (screenshot), re-issued GET filter [account = WAGgcOGkoP-hy_Gu] 2 rows (Carl Fox, Cara Ito)
    records-forms.cascading-multilevel-and-clear · r9 A4 pass pass pins · cited from #28965: HTTP-in-process: cascade-server-boundary 'acceptance[4], negative[2]: a cross-account contact that exists is stored…; a fabricated one is reference_not_found' — 201 stored as sent; 400 reference_not_found, nothing stored.
    records-forms.cascading-multilevel-and-clear · r9 A5 pass pass pins · cited from #28965: HTTP-in-process: cascade-server-boundary 'acceptance[5]: a legal selection on each cascade re-reads verbatim' — cn/zj and Northwind + Northwind contact.
    records-forms.cascading-multilevel-and-clear · r9 N0 pass pass pins · cited from #28965: HTTP-in-process: cascade-server-boundary control in the acceptance[2] case: PATCH {us, zj} with the child in payload → 400 [['province','invalid_option']].
    records-forms.cascading-multilevel-and-clear · r9 N1 pass pass pins · cited from #28965: browser-live + HTTP-in-process: cascading-options 'the server rejects an out-of-set option value…' ({us,zj} 400) + the dogfood in-payload control; never 200.
    records-forms.cascading-multilevel-and-clear · r9 N2 pass pass pins · cited from #28965: HTTP-in-process: cascade-server-boundary pins both recorded gaps as they stand (parent-only PATCH keeps zj; cross-account contact 201), so neither can be ticked as server-enforced.
    records-forms.cascading-options · r7 A0 partial pass browser-live · New Cascading Select dialog (screenshot): before any country, province renders 'Select country first' with no combobox; country cn -> province options [Zhejiang, Guangdong]; picked Zhejiang (hidden select value zj); country -> United States: province trigger back to 'Select an option', select value '', options [California, Texas]
    records-forms.cascading-options · r7 A1 pass pass pins · cited from #28965: HTTP-live: cascading-options.spec.ts 'the server rejects an out-of-set option value, and accepts an in-set one': Playwright request against the booted showcase, 400 VALIDATION_FAILED province invalid_option, in-set 201
    records-forms.cascading-options · r7 A2 partial pass browser-live · Personas minted by invite->sign-up(->accept): sys_member r5-orgadmin=admin, r5-member=member. Tier enumeration: org_admin persona [Standard, 'Restricted (admin only)'], plain member [Standard], seeded admin (owner) [Standard]. org_admin form create sent tier 'restricted' -> 201, re-read tier restricted; plain member direct POST tier restricted -> 400 fields[{field:tier, code:invalid_option}], 0 stored (standard twin 201). Seeded owner POST restricted also 400: the predicate as authored
    records-forms.cascading-options · r7 A3 skipped pass browser-live · New Invoice: contact is lookup-trigger-gated, disabled, 'Select Account first' before account (no contact request). Account Northwind -> picker GET /api/v1/data/showcase_contact?top=50&filter=["account","=","gkjxchdg3aTc_bZZ"] 200, 26 rows (26 options); switch to Contoso -> GET ...filter=["account","=","WAGgcOGkoP-hy_Gu"] 200, 2 rows (Carl Fox, Cara Ito)
    records-forms.cascading-options · r7 A4 pass pass pins · cited from #28965: HTTP-in-process: cascade-role-gated-option 'acceptance[4]: a legal cascade selection re-reads verbatim' (GET by id returns country cn, province zj)
    records-forms.cascading-options · r7 N0 pass pass pins · cited from #28965: HTTP-live: cascading-options.spec.ts server case: us/zj answers 400 invalid_option, not 200
    records-forms.cascading-options · r7 N1 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[2], negative[1]: a plain member's direct tier restricted POST is refused invalid_option, and nothing is stored' (standard is the 201 control)
    records-forms.concurrent-edit-conflict · r5 A0 skipped pass HTTP-live+browser-live · API: A PATCH If-Match v0 → 200; B PATCH same stale If-Match → 409 code CONCURRENT_UPDATE, currentVersion + currentRecord in body; re-read website = A's value. Browser (two form-signed-in contexts on run account): A's modal Update PATCH If-Match 07:47:55.146Z → 200; B's modal Update with the same stale If-Match → 409 CONCURRENT_UPDATE; server kept A's value
    records-forms.concurrent-edit-conflict · r5 A1 partial pass browser-live · screenshot: modal form → 'Save conflict' dialog ('This record was changed by someone else…', 'Their save: 11/2/2026, 7:49:36 AM') with Keep editing / Overwrite (Keep editing → 0 further writes); inline stale save → 'This record was modified by someone else' dialog showing YOUR EDIT (B value) vs CURRENT VALUE (A value, 'Updated at 7:50:47 AM') with Cancel / Overwrite anyway / Reload latest
    records-forms.concurrent-edit-conflict · r5 A2 skipped pass browser-live+HTTP-live · inline: B double-clicks Website, types B value; A's write lands (200); B Save → 409; 'Reload latest' → 0 further PATCH/PUT from B, page shows A's value (https://A-race-2.example), save bar gone; server website = A's value
    records-forms.concurrent-edit-conflict · r5 A3 partial pass browser-live+HTTP-live · inline: B stale save → 409 currentVersion 07:51:00.979Z (= A's write's updated_at); 'Overwrite anyway' → PATCH If-Match 07:51:00.979Z body {website:B} → 200; server re-read website = https://B-inline-over.example (one winner)
    records-forms.concurrent-edit-conflict · r5 A4 skipped pass browser-live · network: one inline session — dblclick Website (details body) then header-highlight Annual Revenue edited 1000→2500 (screenshot: both inputs in one session, one Save bar); Save → exactly ONE PATCH, body {website:'https://B-two-surface.example', annual_revenue:2500} (exactly two keys), header If-Match 07:51:04.135Z (the read updated_at) → 200; server shows both
    records-forms.concurrent-edit-conflict · r5 A5 skipped pass browser-live · stale inline PATCH (If-Match 07:49:36.995Z after A's 07:50:47.908Z write) → 409 CONCURRENT_UPDATE and the ConcurrentUpdateDialog rendered (screenshot); same on the overwrite leg
    records-forms.concurrent-edit-conflict · r5 N0 partial pass HTTP-live+browser-live · every stale save (API, modal, inline ×2) answered 409; no silent overwrite — server kept A's value until B explicitly chose Overwrite
    records-forms.concurrent-edit-conflict · r5 N1 skipped pass browser-live · two-surface inline edit issued 1 PATCH with only the 2 edited keys (no computed/readonly/untouched keys)
    records-forms.concurrent-edit-conflict · r5 N2 partial pass browser-live · every captured console write carried If-Match: modal (A and B), inline stale, inline overwrite, inline two-surface
    records-forms.conditional-rules-grid · r11 A0 partial pass browser-live · INV-1002 Edit dialog, line 1 description cleared + Qty 120: cell [data-cell=0-1] aria-invalid=true, td title 'Description is required', red cell (screenshot); seeded line 2 (Qty 1, position 1) unflagged. Update -> dialog stays open with banner 'Description is required'; the one POST /api/v1/batch (atomic) answered 400 VALIDATION_FAILED fields[{field:description,code:required}]; API re-read of both lines byte-identical to before
    records-forms.conditional-rules-grid · r11 A1 skipped pass HTTP-live · POST /api/v1/batch atomic [create valid line, create line qty 120 no description] -> 400 VALIDATION_FAILED description/required; batch update qty 150 description '' -> 400 same; POST /data/showcase_invoice_line qty 120 -> 400 same; line count 2 -> 2, rows unchanged (no partial row from the valid sibling op)
    records-forms.conditional-rules-grid · r11 A2 partial pass browser-live · INV-1002 Edit dialog: at Draft both lines' Product lookup-trigger, Qty, Unit Price enabled; header Status set Paid (unsaved) -> all three disabled on both rows (screenshot shows greyed cells; fill on Qty refused, value kept 120); back to Draft -> all three enabled again; Description stayed editable throughout; Cancel, zero writes
    records-forms.conditional-rules-grid · r11 A3 skipped pass HTTP-live · Upload receipt-r5.txt in line 2 receipt cell -> presigned/PUT/complete 200 fileId fee185ed...; batch update {receipt:'fee185ed...'} 200; GET line -> receipt {id:fee185ed..., name:receipt-r5.txt, size 42, mimeType text/plain}; DB column holds the id string; sys_file row status committed; GET /storage/files//url -> raw URL -> 200 body matches the uploaded text
    records-forms.conditional-rules-grid · r11 A4 skipped pass HTTP-live · Lines at distinct positions 0/1. Saved batch carried line-1 update {description, quantity 120, amount} only; re-read: line 1 description 'R5 bulk order note', qty 120, amount 11880; line 2 every field incl. updated_at 07:44:10.762Z identical before/after; invoice total re-read 11909.99 = 11880 + 29.99
    records-forms.conditional-rules-grid · r11 N0 skipped pass HTTP-live · Client batch with qty 120 + empty description refused 400 and nothing written; direct batch/POST twins refused 400 (A0/A1)
    records-forms.conditional-rules-grid · r11 N1 partial pass browser-live · Product, Qty, Unit Price all disabled at unsaved Paid and all re-enabled at Draft (A2); no cell left editable at Paid or locked after revert
    records-forms.conditional-rules-header · r8 A0 pass pass pins · cited from #28965: browser-live: required-when-submit.spec.ts 'requiredWhen blocks submit with a field error, and relaxes when FALSE' (Issued On is required, zero batch POSTs) + field-conditional-rules.spec.ts required marker; Playwright visibility waits stand in for the screenshot step
    records-forms.conditional-rules-header · r8 A1 pass pass pins · cited from #28965: HTTP-in-process: invoice-conditional-rules-server 'acceptance[1], negative[0]: a sent invoice with no issued_on is refused, naming issued_on, and nothing is stored' (400 VALIDATION_FAILED, exactly issued_on required; draft and dated twins 201)
    records-forms.conditional-rules-header · r8 A2 partial pass browser-live · Client: INV-1001 (sent) Edit dialog, Status set to Paid unsaved -> [data-testid=field:showcase_invoice.tax_rate] loses its and renders read-only text '8' (screenshot confirmed; at Sent it is an enabled number input); Cancel wrote nothing (no non-GET data request). Server: GET INV-1003 tax_rate 8 -> PATCH {tax_rate:99} 200 -> GET tax_rate 8, status paid (draft control INV-1004 PATCH tax_rate 7 persisted 7)
    records-forms.conditional-rules-header · r8 A3 pass pass pins · cited from #28965: browser-live: field-conditional-rules.spec.ts 'header fields react to Status via CEL visibleWhen / readonlyWhen / requiredWhen': paid_on count 0 at draft/sent, 1 and required at paid, on the New Invoice dialog
    records-forms.conditional-rules-header · r8 A4 skipped pass browser-live · Invoice list row menus (screenshots confirmed): INV-1003 (paid) = Edit [aria-disabled=true], Submit for Sign-off, Generate PDF - no Delete; INV-1002 (draft) = Edit, Delete, Submit for Sign-off, Generate PDF all enabled; INV-1001 (sent) = Edit, Delete, Generate PDF. INV-1003 record page: Edit button disabled, no Delete button
    records-forms.conditional-rules-header · r8 A5 partial pass HTTP-live · readonlyWhen one-rule cross-check: client locked tax_rate when the unsaved Status became Paid on a sent invoice; server mirror PATCH INV-1012 (sent) {status:'paid', paid_on:'2026-11-01', tax_rate:55} -> 200, re-read status paid, tax_rate 8 (stripped on the merged record), and the stored-paid PATCH above discarded 99. requiredWhen half: pins A0/A1. No case where one side locks and the other accepts
    records-forms.conditional-rules-header · r8 N0 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[1], negative[0]: a sent invoice with no issued_on is refused...' answers 400, nothing stored
    records-forms.conditional-rules-header · r8 N1 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[2], negative[1]: a tax_rate PATCH on the paid invoice answers 200 and is discarded': tax_rate 99 does not persist, status stays paid
    records-forms.conditional-rules-header · r8 N2 skipped pass browser-live · INV-1003 (paid) row menu has no Delete item (screenshot + menuitem read); draft INV-1002 row menu does carry Delete
    records-forms.delete-behavior-matrix · r5 A0 partial pass HTTP-live · run-created account+contact(account=acc); DELETE /data/showcase_account/acc → 200 {success:true}; GET contact → 200, record.account null (key present, DB column NULL)
    records-forms.delete-behavior-matrix · r5 A1 partial pass HTTP-live · zoo row f_lookups [A,B] (f_master_detail→run project on account C); DELETE account A → 200; re-read f_lookups = [B] exactly (member removed, remainder written, not [] / not [A,B])
    records-forms.delete-behavior-matrix · r5 A2 partial pass HTTP-live · invoice + 2 lines (filtered list = 2); DELETE invoice → 200; filtered line list 0, each line GET 404 RECORD_NOT_FOUND, 0 rows in DB
    records-forms.delete-behavior-matrix · r5 A3 partial pass HTTP-live · admin: stock escalation arm DELETE account referenced by an invoice → 409 code DELETE_RESTRICTED, dependentObject 'showcase_invoice', dependentCount 1, developerMessage ends "or set deleteBehavior:'cascade' on showcase_invoice.account"; authored arm (scratch qa_restrict_child.account deleteBehavior:'restrict') → 409 DELETE_RESTRICTED dependentObject 'qa_restrict_child', dependentCount 1, same cascade remedy; parent survives (GET 200). Shape observed: admin, count disclosed; lesser persona not driven
    records-forms.delete-behavior-matrix · r5 A4 partial pass HTTP-live · DELETE account referenced by required invoice.account → 409 DELETE_RESTRICTED 'This Account is still referenced by 1 Invoice record(s) through “Account”, which is required and cannot be cleared' (not a 400 required); account+invoice intact; control DELETE of an unreferenced run account → 200
    records-forms.delete-behavior-matrix · r5 A5 pass pass pins · cited from #28965: engine + spec-schema: field.test.ts 'deleteBehavior: set_null on master_detail is a parse-time rejection' + engine-cascade-delete '[#9689] … logs via logger.error when the parent delete coerces an authored set_null to cascade' green; the PUT refusal has no pin.
    records-forms.delete-behavior-matrix · r5 N0 partial pass HTTP-live · the escalation refusal is 409 DELETE_RESTRICTED about the account the caller deleted — no 400 'account is required' surfaced
    records-forms.delete-behavior-matrix · r5 N1 partial pass HTTP-live · multi-value removal left ['B'] — neither cleared slot nor dead id A
    records-forms.delete-behavior-matrix · r5 N2 partial pass HTTP-live+db · no orphan: both lines 404 + 0 DB rows; telemetry sys_audit_log shows per-line 'delete' rows for both lines (07:41:22.939/.014) before the invoice's delete row — the children's own audit hooks fired (public delete path)
    records-forms.delete-behavior-matrix · r5 N3 partial pass HTTP-live · both refusals carry dependentCount 1 (never 0)
    records-forms.field-history-tracking · r11 A0 pass pass pins · cited from #28965: HTTP-in-process: field-history-tracking-activity 'acceptance[0], negative[2]: a tracked edit lands the old → new summary in labels, never raw values' — sys_activity summary 'Industry: Retail → Finance', key fieldChanges.
    records-forms.field-history-tracking · r11 A1 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[1], negative[0]: a website-only edit lands the generic frame, never an old → new diff' — summary names the Website label, no arrow, no URLs.
    records-forms.field-history-tracking · r11 A2 partial pass browser-live · network log, built /_console at 1440x900, form sign-in, 2 fresh loads: seeded account record page → exactly 1 GET /api/v1/data/sys_audit_log?…filter=[record_id=ID, object_name=showcase_account] at load (both loads), none extra after the chip; Activity tab → Changes chip aria-pressed=true, screenshot shows 'Industry Finance → Technology · Dev Admin · just now' (option labels); showcase_task detail (authored showcase_task_detail page, screenshot confirmed render) → 0 sys_audit_log reads of 31 /api requests (both loads). Flag half isolated on scratch objects with synthesized pages: qa_trkfield (per-field trackHistory, no enable block; screenshot shows its Activity stream with 'Stage: …
    records-forms.field-history-tracking · r11 A3 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[3]: the derived history operation is offered on showcase_account and not on showcase_task' — GET /auth/me/permissions apiOperations.
    records-forms.field-history-tracking · r11 A4 pass pass pins · cited from #28965: plugin-unit (test oracle): audit-lookup-summary '[#7230] renders BOTH sides of a reference change as titles' + 'a tracked change with NO reference field pays ZERO extra reads' / 'ONE tracked reference change costs exactly ONE read' green.
    records-forms.field-history-tracking · r11 N0 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[1], negative[0]…' — the untracked website edit's rendered summary carries no old→new diff.
    records-forms.field-history-tracking · r11 N1 skipped pass browser-live · task detail made 0 sys_audit_log reads on both fresh loads (network log); and the gate's flag half on a synthesized page: per-field-only qa_trkfield 0 reads vs enable.trackHistory qa_trkobj 1 read — the verdict rests on the flag gate, not only the authored-page gate
    records-forms.field-history-tracking · r11 N2 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[0], negative[2]…' — option labels Retail/Finance, never raw values; audit-option-label-summary '[#7289] localizes the option label on BOTH sides' beside it.
    records-forms.field-type-constraints · r6 A0 skipped pass HTTP-live · POST zoo name×201 → 400 VALIDATION_FAILED fields[{field:'name',code:'max_length',constraint:{maxLength:200,actual:201}}]; zoo count 4→4; name×200 → 201, re-read length 200
    records-forms.field-type-constraints · r6 A1 partial pass HTTP-live · f_number -1 → 400 min_value {min:0}; 1001 → max_value {max:1000}; 'abc' → invalid_number; count 5→5; 500 → 201 re-read 500. Also f_currency -5 → min_value, f_percent 101 → max_value
    records-forms.field-type-constraints · r6 A2 skipped pass HTTP-live · scratch qa_scale.amount (number, scale 2): 1234.567 → 400 max_scale {scale:2,actual:3}, qa_scale count 0→0; 1234.56 → 201 re-read 1234.56; PUT of currency field with scale:2 (qa_curscale) → 422 INVALID_METADATA 'scale is not valid on a currency field'; zoo f_currency 1234.567 → 201 re-read 1234.567 (unrounded)
    records-forms.field-type-constraints · r6 A3 skipped pass HTTP-live · zoo f_select 'not-a-value' → 400 invalid_option options:['low','medium','high'], count 7→7; showcase_invoice status 'archived' → 400 invalid_option options:['draft','sent','paid','void'], invoice count 13→13
    records-forms.field-type-constraints · r6 A4 partial pass HTTP-live · f_multiselect 'red' → 201 re-read ['red']; {x:1} → 400 invalid_type; ['red','purple'] → 400 invalid_option value:'purple'; ['red','blue'] → 201 re-read ['red','blue']
    records-forms.field-type-constraints · r6 A5 partial pass HTTP-live · f_lookup 'FABRICATED_r1_xyz' → 400 reference_not_found constraint{target:'showcase_account'}, count 9→9; real account id → 201 re-read verbatim; showcase_invoice{account:A, contact: contact of account B} → 201, re-read contact = B's contact (existence-only, dependsOn unscoped server-side)
    records-forms.field-type-constraints · r6 A6 partial pass HTTP-live · POST zoo f_autonumber 'HACK-9999' → 201, re-read '0011' (sequence); PATCH f_autonumber 'HACK-PATCH' → 200, re-read still '0011'
    records-forms.field-type-constraints · r6 A7 partial pass HTTP-live · POST f_number 42, f_percent 75, f_formula 999 → re-read f_formula 31.5; run invoice + lines (30, 7.5) → total 37.5; invoice POSTed with total 777 → kept 777, then one line (8) → total self-heals to 8
    records-forms.field-type-constraints · r6 N0 skipped pass HTTP-live · 201-char name refused max_length, not truncated (count unchanged)
    records-forms.field-type-constraints · r6 N1 skipped pass HTTP-live · out-of-set f_select and invoice status both 400, neither count grew
    records-forms.field-type-constraints · r6 N2 partial pass HTTP-live · forged f_autonumber not persisted on POST or PATCH ('0011')
    records-forms.field-type-constraints · r6 N3 skipped pass HTTP-live · scale enforced by refusal (max_scale), the conforming value stored verbatim; f_currency 1234.567 stored unrounded (currency declares no scale)
    records-forms.field-type-constraints · r6 N4 partial pass HTTP-live · dangling f_lookup 400 reference_not_found, nothing stored
    records-forms.field-type-matrix · r8 A0 pass pass pins · cited from #28965: HTTP-in-process: field-zoo-roundtrip.dogfood.test.ts green: every MATRIX vector's case '‹type› (‹field›) round-trips' (setEqual for arrays, equal for objects) over bootShowcase apiAs POST then GET; MATRIX carries no xfail rows at the subject
    records-forms.field-type-matrix · r8 A1 skipped pass browser-live · 47 types matched to dedicated widgets on the Specimen — Full edit form (table in notes; no generic text input stands in for richtext/markdown/html/code); f_autonumber read-only '0001' on the record detail page (screenshot, AUTO NUMBER 0001, not on the form); summary carriers: EXP-2001 detail shows TOTAL 1,500.50 / APPROVED 960.00 as read-only values, showcase_invoice Edit form draws Total as read-only text '8' (no input)
    records-forms.field-type-matrix · r8 A2 pass pass pins · cited from #28965: HTTP-in-process: field-type-matrix-server-refusals: 'acceptance[2], negative[0]: an out-of-set select is refused invalid_option, and the row count does not grow' and 'acceptance[2]: a create with no name is refused naming name with required...' (400 VALIDATION_FAILED, exact fields[])
    records-forms.field-type-matrix · r8 A3 pass pass pins · cited from #28965: HTTP-in-process: field-zoo-roundtrip: 'secret (f_secret) round-trips' and 'password (f_password) round-trips' are kind masked: the REST GET is not the written value and equals SECRET_MASK
    records-forms.field-type-matrix · r8 A4 pass pass pins · cited from #28965: HTTP-in-process: field-zoo-roundtrip: 'autonumber (f_autonumber) round-trips' (present, non-null; the create body never writes it, so server-assigned) and 'formula (f_formula) round-trips' (computed, 31.5)
    records-forms.field-type-matrix · r8 A5 pass pass pins · cited from #28965: HTTP-in-process: round-trip half: 'lookup (f_lookup)/master_detail (f_master_detail)/tree (f_tree) round-trips' equal the REFERENCE_TARGETS ids; refusal half: it.each 'acceptance[5], negative[1]: a dangling %s is refused reference_not_found, and nothing is stored'
    records-forms.field-type-matrix · r8 A6 pass pass pins · cited from #28965: HTTP-in-process: field-type-matrix-server-refusals: 'acceptance[6]: every seeded invoice total is the server-side sum of its lines' and 'acceptance[6]: EXP-2001 reads total 1500.5 and the filtered approved roll-up 960'
    records-forms.field-type-matrix · r8 N0 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[2], negative[0]: an out-of-set select is refused invalid_option...': not_an_option answers 400, row count unchanged; the in-set 'high' is the 201 control
    records-forms.field-type-matrix · r8 N1 pass pass pins · cited from #28965: HTTP-in-process: it.each 'acceptance[5], negative[1]: a dangling %s is refused reference_not_found, and nothing is stored' for f_lookup, f_master_detail, f_tree: 400 naming the field, zero rows by name
    records-forms.field-type-matrix · r8 N2 pass pass pins · cited from #28965: HTTP-in-process: field-zoo-roundtrip masked checks: the GET of the created row returns SECRET_MASK, not the plaintext, for f_secret and f_password
    records-forms.field-unique-enforcement · r13 A0 pass pass pins · cited from #28965: HTTP-in-process (SQLite): field-unique-rest-refusal 'acceptance[0], negative[0], negative[1]: a colliding create on %s is 409 UNIQUE_VIOLATION, driver-anonymous, and nothing lands' — sku: generic error + label developerMessage; gsku: 'Global SKU' named, field gsku.
    records-forms.field-unique-enforcement · r13 A1 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[1]: two rows with no sku both land (NULL-distinct)'.
    records-forms.field-unique-enforcement · r13 A2 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[2]: an update onto a taken value is refused and the row keeps its old value; a self-update passes' — 409 UNIQUE_VIOLATION, sku kept; nick-only and same-value PATCH 200.
    records-forms.field-unique-enforcement · r13 A3 partial partial HTTP-live · HTTP-live half: scratch qauniq_item (pkg com.objectstack.qa.unique, ns qauniq), POST /data/qauniq_item/updateMany {records:[{id:r1,data:{sku:Z}},{id:r2,data:{sku:Z}}],options:{atomic:true}} → 200 success:false, succeeded 0, r1 ROLLED_BACK, r2 UNIQUE_VIOLATION 409; both rows re-read byte-identical to before. Missing half: a PREDICATE update — REST updateMany takes per-id records only (UpdateManyRecordSchema {id,data}; protocol runs per-row engine.update), no door reaches the engine's predicate updateMany; the atomic arm mutates-then-rolls-back rather than checking before mutating · blocked by {by: fixture, ref: no REST predicate-update door at 7fa9873 (batch-routes.ts updateMany = …
    records-forms.field-unique-enforcement · r13 A4 pass pass pins · cited from #28965: driver (test oracle): memory-unique-constraint "[#13197] scoping matches driver-sql's ADR-0120 D1/D3 rule" cases + sql-driver-8577 and declared-index-organization-respelling ('AFTER: the same cross-organization create is accepted', plain organization key) green.
    records-forms.field-unique-enforcement · r13 A5 pass pass pins · cited from #28965: driver (test oracle): memory-unique-constraint 'the refusal is recognisable to the ENGINE, so the autonumber resync converges' green.
    records-forms.field-unique-enforcement · r13 N0 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[0], negative[0], negative[1]…' — the second create is 409 and only one row holds the value.
    records-forms.field-unique-enforcement · r13 N1 pass pass pins · cited from #28965: HTTP-in-process: same case's expectDriverAnonymous: no [driver- prefix, no 'UNIQUE constraint failed', no bound value in the body (PATCH refusal too).
    records-forms.field-unique-enforcement · r13 N2 partial partial HTTP-live · atomic per-id REST updateMany: 0 rows changed (ROLLED_BACK + UNIQUE_VIOLATION). Observed beside: the DEFAULT non-atomic updateMany (no options) stamped row 1 (sku C→Y, success:true) and refused row 2 — half-applied by its documented non-atomic contract (BatchOperationResultSchema: rows before the failure stay written). Predicate half not REST-reachable (see A3) · blocked by {by: fixture, ref: no REST predicate-update door}
    records-forms.field-unique-enforcement · r13 N3 pass pass pins · cited from #28965: driver (test oracle): memory 'unique: true on an object WITH a tenant column scopes per organization' + sql-driver-8577 'AFTER: the same cross-organization create is accepted — 409 flips to 201'; live two-org probe not provisioned (knownGaps[2]).
    records-forms.form-dirty-guard · r7 A0 partial pass browser-live · Products > Widget B Edit dialog, description changed: X, Escape and an overlay (backdrop) click each raised alertdialog 'Discard changes? ... Keep editing / Discard' (screenshots); footer Cancel on a dirty form closed at once with no prompt and no write. Pristine form: X, Escape, backdrop, Cancel each closed with no prompt (alertdialog count 0)
    records-forms.form-dirty-guard · r7 A1 skipped pass HTTP-live · Discard exit: GET /data/showcase_product/ JSON (incl. updated_at) identical to pre-edit read, zero non-GET requests; reopened form shows stored 'Deluxe widget'. Dirty Cancel: same identical read. Save exit: Update sent PATCH {description:'R5 saved description'} and the re-read carries it
    records-forms.form-dirty-guard · r7 A2 pass pass pins · cited from #28965: component-jsdom: discardGuard.test.tsx ran green in the ui package run (48 files / 534 tests, 0 failed); per-file count not broken out in ui.log
    records-forms.form-dirty-guard · r7 N0 partial pass browser-live · No accidental close (X/Escape/backdrop) lost a dirty edit without a prompt: 'Keep editing' returned to the dialog with 'R5 dirty edit' intact on all three; no pristine path prompted
    records-forms.gantt-interactions · r8 A0 partial pass HTTP-live · Schedule (Gantt), search "Build homepage": body drag of bar KNK_1cKwb9OI5OOJ -> PATCH /api/v1/data/showcase_task/KNK_1cKwb9OI5OOJ {"start_date":"2026-11-02","end_date":"2026-11-18"} 200; API re-read 10-27/11-12 -> 11-02/11-18 (both shifted 6 days); after reload the row reads 11/2 - 11/18 and the bar starts at Nov 2 (screenshot)
    records-forms.gantt-interactions · r8 A1 partial blocked pins · component-jsdom evidence only (GanttView.autoscheduledlg / countinterp); no live row-set diff: auto-schedule needs a dependenciesField and showcase_task declares no predecessor field, and an object/field overlay is refused on stock (only view-type overlays are allowed) · blocked by {by: fixture, ref: records-forms.gantt-interactions fixtures.knownGaps[0] (no dependenciesField on the stock task gantt)}
    records-forms.gantt-interactions · r8 A2 partial partial HTTP-live · direct-drag half proven live on a view overlay (PUT /api/v1/meta/view/showcase_task.gantt + lockField:"done", 200): drag of the done bar App wireframes -> no PATCH, re-read 10-23/10-27 with updated_at unchanged, hover shows "No edit permission"; control drag of not-done Design system -> PATCH 200 (10-21/11-04 -> 10-22/11-05); overlay removed with DELETE (gantt config back to stock). Missing half: survives auto-schedule, blocked(fixture) like A1
    records-forms.gantt-interactions · r8 A3 skipped pass HTTP-live · member bound to contributor (task_own_rows): admin set Warehouse schema assignee to the member, the member's gantt lists only that task; admin reassigned it away; member drag -> PATCH {"start_date":"2026-10-12","end_date":"2026-11-06"} -> 404 RECORD_NOT_FOUND; toast "Save failed — the change was rolled back"; bar style before/after identical (left 770px); re-read 10-11/11-05 unchanged
    records-forms.gantt-interactions · r8 N0 skipped pass HTTP-live · every repainted drag landed a PATCH (A0 200, control 200); the refused PATCH (404) left the bar at its old position and the row unchanged (A3)
    records-forms.import-job-undo-cancel · r8 A0 partial pass HTTP-live · POST /api/v1/data/crm_lead/import/jobs {format:csv, 3 rows, marker in notes} -> 201 pending; GET /data/import/jobs/:id -> succeeded, undoable:true, created 3; GET .../results -> 200 results[] per row {row,ok:true,action:"created",id}; jobs list entry {jobId, undoable:true, createdAt} with NO revertedAt key. Console undoable-job half rests on the #28965 gated-spec run (import-console-undo.spec.ts passed 2/2), not re-run
    records-forms.import-job-undo-cancel · r8 A1 partial pass HTTP-live · marker-filtered GET /data/crm_lead?notes=MARKER total: pre 0 -> post-import 3 -> post-undo 0 (undo 200 {deleted:3, failed:0}); unfiltered total 5 -> 8 -> 5, the 5 seeded leads untouched
    records-forms.import-job-undo-cancel · r8 A2 skipped pass HTTP-live · 4000-row job, cancel once 800 marker rows were counted: POST /data/import/jobs/:id/cancel 200; job terminal status "cancelled", processed 1400, created 1400, errors 0; marker read = 1400 rows, all 1400 complete (name, email, status, notes present) - 0 torn rows; CSV carries no lookup column, so no FK to dangle
    records-forms.import-job-undo-cancel · r8 A3 partial pass HTTP-live · GET /api/v1/data/import/jobs -> {jobs:[...]} each with jobId/undoable/createdAt; before undo the fresh job has undoable:true and no revertedAt key; after undo undoable:false and revertedAt "2026-11-02T08:35:15.321Z"; the cancelled job reads status cancelled, undoable:true
    records-forms.import-job-undo-cancel · r8 N0 partial pass HTTP-live · undo removed exactly the 3 imported rows (marker 3->0, unfiltered 8->5, seeded rows intact)
    records-forms.import-job-undo-cancel · r8 N1 skipped pass HTTP-live · post-cancel row set coherent: 1400/1400 complete rows, 0 torn
    records-forms.import-job-undo-cancel · r8 N2 pass pass pins · cited from #28965: test-oracle: the console spec ran with the gate set and reported ✓ passed (impcrm1/impcrm2), not skipped; import-harness/import-undo.spec.ts SKIPPED (its vite.live.config.ts is absent) and is counted as no evidence.
    records-forms.import-wizard-encoding-and-hints · r9 A0 pass pass pins · cited from #28965: component-jsdom: importParsers.test.ts green: 'decodes a GBK/GB18030 .csv (zh-CN Excel default) via fallback' yields exactly 名称 *, 编号 *, 测试岛2, TEST-001
    records-forms.import-wizard-encoding-and-hints · r9 A1 pass pass pins · cited from #28965: component-jsdom: importMissingRequiredHint.test.tsx green: 'names each unmapped required field as label (name) and disables Next' and 'clears the hint and enables Next once the required column is supplied'
    records-forms.import-wizard-encoding-and-hints · r9 A2 pass pass pins · cited from #28965: component-jsdom: importLegacyReferenceGuard.test.tsx green (pin-only by design, knownGaps[1]): 'flags the result degraded and shows a downgrade notice on the scalar path' (import-degraded-notice, 'compatibility fallback')
    records-forms.import-wizard-encoding-and-hints · r9 A3 partial pass browser-live · wizard-completed import of a GBK-encoded CSV (2 CJK rows) on showcase_contact (More actions -> Import, file via setInputFiles): POST /api/v1/data/showcase_contact/import 200, result "Import Complete / 2 created"; filtered GET $filter notes $contains "QA-R6-GBK" -> 200 total 2: 张三测试/测试岛2/"QA-R6-GBK 编号 TEST-001", 李四测试/名称公司/"QA-R6-GBK 备注二"
    records-forms.import-wizard-encoding-and-hints · r9 N0 skipped pass browser-live · wired wizard: mapping step auto-matched name/email/company/notes (high confidence); preview screenshot shows 张三测试, 测试岛2, "QA-R6-GBK 编号 TEST-0..", 李四测试, 名称公司, 备注二 - no mojibake
    records-forms.import-wizard-encoding-and-hints · r9 N1 partial pass browser-live · wizard reported 2 created; filtered API read finds exactly 2 marker rows (contact total 32 -> 34)
    records-forms.list-view-capabilities · r8 A0 partial pass browser-live · All 11 variants driven live with their own trace/screenshot: saved-view-filter, quick-search, sort, server-pagination, saved-view-switcher (direct /view/ routes: in_progress, tabular, grid, bulk_actions, legacy_row_actions, done, urgent), inline-edit, export, visualization-switcher, bulk-actions, row-actions, conditional-formatting (Field Zoo: Specimen - Full row background rgba(37,99,235,0.08), Specimen - Minimal none). Per-variant verdicts are on A1-A9; bulk-actions aggregate fails at A8
    records-forms.list-view-capabilities · r8 A1 partial pass HTTP-live · in_progress view: GET /api/v1/data/showcase_task?...&filter=[["status","equals","in_progress"]] -> 200 total 2 (ids KNK_1cKwb9OI5OOJ, lyjRksAo9l_hZw2P); screenshot header "In Progress · 2" and count bar "2 records" match the response total
    records-forms.list-view-capabilities · r8 A2 skipped pass HTTP-live · Accounts search box "huaning" -> GET /api/v1/data/showcase_account?top=50&search=huaning&select=... 200 total 1, record name 华宁科技 (server-side search param, not client filter); direct GET /api/v1/data/showcase_account?search=huaning reproduces the same row id KwfflkAZ6XHU7wd8
    records-forms.list-view-capabilities · r8 A3 skipped pass HTTP-live · 55 inquiries staged via POST /api/v1/data/showcase_inquiry/createMany (201, total 3->58). Page 1 GET ...?top=50 -> 50 rows total 58; Next page GET ...?top=50&skip=50 -> 8 rows; union 58 ids, 58 distinct, none missing; 0 data requests without top; pager "Page 2 of 2"
    records-forms.list-view-capabilities · r8 A4 skipped pass HTTP-live · tabular view (Task List) GET .../showcase_task?populate=project&top=50&select=...&sort=-estimate_hours -> 200, 10 rows, no pageerror; server order 60,40,30,24,20,18,16,16,12,8 (desc); screenshot renders the same order
    records-forms.list-view-capabilities · r8 A5 partial pass HTTP-live · grid view (inlineEdit): dblclick Estimate cell of "SEO migration plan", 16->17, Save All(1) -> PATCH /api/v1/data/showcase_task/C9VNUoO--d2c77Ek body {"estimate_hours":17} -> 200; full-row API re-read diff before/after = estimate_hours 16->17 plus updated_at/updated_by only
    records-forms.list-view-capabilities · r8 A6 skipped pass HTTP-live · grant provisioned: PUT /api/v1/meta/permission/qa_task_export {objects:{showcase_task:{allowRead,allowExport}}} 200 + POST sys_user_permission_set {user_id: admin, permission_set:"qa_task_export"} 201 (before: export route 403 EXPORT_NOT_PERMITTED). Display popover footer lists exactly Export as CSV / XLSX / JSON; each GET .../showcase_task/export?format=&fields=...&filter=[["status","equals","in_progress"]] 200; csv, json, xlsx files each hold exactly Build homepage + Ingest pipeline
    records-forms.list-view-capabilities · r8 A7 partial pass browser-live · All Tasks Display popover Layout dropdown screenshot: Grid, Kanban, Gallery, Calendar, Timeline, Gantt (exactly six). Switching each: Kanban 10 cards over status columns, Gallery 10, Timeline 10, Gantt 10, Grid 10 seeded task titles rendered; Calendar renders the November 2026 month grid with 8 titles, its date-windowed request answers 9 (the 10th, Oct 15, is outside the window); no pageerror
    records-forms.list-view-capabilities · r8 A8 skipped fail HTTP-live · per-record half holds: Mark Done over 2 not-done rows -> 2 x POST /api/v1/actions/showcase_task/showcase_mark_done {recordId} 200. Aggregate half FAILS: Recalculate Selection over 3 rows -> ONE POST /api/v1/actions/showcase_task/showcase_recalc_selection {"params":{"_selectedIds":[3 ids]}} -> 400 VALIDATION_ERROR "identifies its record by "id", but that field is not present on the record"; dialog "Succeeded 0 / 3 · Failed 3"; reproduced 2/2 fresh loads + forged API
    records-forms.list-view-capabilities · r8 A9 skipped pass browser-live · legacy_row_actions row menu (screenshot): Mark Done, Reassign…, Quick View, Recalculate Estimate - Quick View once; Recalculate Estimate -> POST /api/v1/actions/showcase_task/showcase_recalc_estimate {recordId} 200 (flow ran, estimates returned); Quick View opens its modal target (showcase_component_gallery dialog, its analytics queries 200)
    records-forms.list-view-capabilities · r8 N0 skipped pass HTTP-live · overlay PUT /api/v1/meta/view/showcase_task.done filter done equals "maybe" (200); Done view data GET ...filter=[["done","equals","maybe"]] -> 400 (boolean comparand refusal); screenshot: zero rows, "This view's query was rejected" with Retry, no unfiltered rows. Overlay removed with DELETE (filter back to status=done, provenance package)
    records-forms.list-view-capabilities · r8 N1 skipped pass browser-live · staged overlay on showcase_task.urgent adding userFilters {element:"tabs", tabs:[QA InProg Tab]}; the Urgent object list renders no tab chip (text count 0, screenshot shows the plain 2-row grid), console logs the ADR-0047 suppression warning; overlay removed with DELETE
    records-forms.list-view-capabilities · r8 N2 skipped pass HTTP-live · every export request carried filter=[["status","equals","in_progress"]] and each of the 3 downloaded files holds 2 rows, not the 10-row table
    records-forms.master-detail-atomic-save · r9 A0 partial pass browser-live · New Invoice os-qa-r5-happy-1793606373984 (Northwind, draft) with two grid lines (Widget A qty 3, Consulting Hour qty 1): Create sent exactly ONE non-GET data write, POST /api/v1/batch {atomic:true, operations:[showcase_invoice create, showcase_invoice_line create {invoice:{$ref:0}, position 0}, showcase_invoice_line create {invoice:{$ref:0}, position 1}]} -> 200
    records-forms.master-detail-atomic-save · r9 A1 pass pass pins · cited from #28965: HTTP-in-process: master-detail-batch-atomic 'acceptance[1], acceptance[5], acceptance[2]: one batch lands the invoice and two lines...': REST re-read of invoice + WIDGET-A/SERVICE-HR lines with catalog description and unit_price
    records-forms.master-detail-atomic-save · r9 A2 pass pass pins · cited from #28965: HTTP-in-process: same case: expectTotalIsSum after the atomic create, after a line POST and after a line DELETE (total == sum of line amounts)
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB

    Per-clause verdicts (2/3): records-forms. #28965 is the pins-only verdict of W4-C2b; here is this card's verdict. A clause the pins scored pass is cited, not re-run; every other clause was driven live on a booted stack at 7fa9873f.

    item · rev clause #28965 here layer · text evidence
    records-forms.master-detail-atomic-save · r9 A3 partial pass browser-live · Create with line 2 product empty: the one atomic batch answered 400 VALIDATION_FAILED fields[{field:product, code:required}]; dialog stays open with banner 'Product is required' and the row-2 Product cell red, title 'Product is required' (screenshot). Filtered GET for os-qa-r5-invalid-1793606397693 -> 0 rows; DB: 0 invoices like os-qa-r5-invalid%, 0 lines with the sibling's description, 0 orphan lines
    records-forms.master-detail-atomic-save · r9 A4 pass pass pins · cited from #28965: browser-live: form-view-subforms.spec.ts 'New ‹object› modal renders relationship-derived subforms and submits an atomic batch': stock New Invoice dialog shows the Line Items heading and line grid
    records-forms.master-detail-atomic-save · r9 A5 pass pass pins · cited from #28965: browser-live + HTTP-in-process: client: form-view-subforms reads the read-only data-computed amount 59.98 after Qty 2, before save; persisted: master-detail-batch-atomic stored amount = quantity x unit_price, client-sent amount 1 does not stand
    records-forms.master-detail-atomic-save · r9 N0 pass pass pins · cited from #28965: HTTP-in-process: 'acceptance[3], negative[0]: a failing child aborts the whole batch...': REST filtered reads find no invoice and no sibling line
    records-forms.master-detail-atomic-save · r9 N1 partial pass browser-live · Network log over the Create click counted 1 data write (the batch) for master + 2 lines; no per-row POSTs to /data/showcase_invoice or /data/showcase_invoice_line
    records-forms.record-clone-contract · r3 A0 skipped pass HTTP-live · scratch qa_noclone (enable:{clone:false}, pkg ns 'qa'): POST /data/qa_noclone/ID/clone → 403 {code:'CLONE_DISABLED', error:"Cloning is disabled for object 'qa_noclone'", object:'qa_noclone'}; qa_noclone count 1→1. Control: POST /data/showcase_account/ID/clone {} → 201
    records-forms.record-clone-contract · r3 A1 partial pass HTTP-live · account clone overrides {name:'os-qa-clone-override', created_by/updated_by:'forged-user', created_at:'2000-01-01…'} → 201; re-read name 'os-qa-clone-override', created_by/updated_by = admin id, created_at fresh 2026-11-02T07:43:18Z; organization_id:'org_forged' override re-derived to the caller's org; zoo clone overrides {f_autonumber:'FORGED-0001', f_formula:999} → re-read f_autonumber '0004' (fresh sequence), f_formula 315 (=420*75/100)
    records-forms.record-clone-contract · r3 A2 partial pass HTTP-live · raw 201 body top-level keys exactly [object,id,sourceId,record], id ≠ sourceId (2bKn7SmAYT-emLrk vs 2cNze9FSLYRXuCG0), no data/result wrapper
    records-forms.record-clone-contract · r3 A3 pass pass pins · cited from #28965: test-oracle: protocol.write-response-internal-fields.tripwire '#7823 tripwire: every generic data ingress strips internal: true…' (enumerates cloneData) + search-clone-schema-conformance green.
    records-forms.record-clone-contract · r3 A4 skipped pass HTTP-live · POST /data/showcase_account/NO_SUCH_ID_r1/clone → 404 RECORD_NOT_FOUND; POST /data/no_such_object_r1/abc/clone → 404 {code:'OBJECT_NOT_FOUND', error:"Object 'no_such_object_r1' is not registered"} — identical envelope to GET/POST on the same unregistered object (shared refusal, not bespoke)
    records-forms.record-clone-contract · r3 N0 skipped pass HTTP-live · explicit-false object refused 403 CLONE_DISABLED; default object (showcase_account, no enable.clone) cloned 201
    records-forms.record-clone-contract · r3 N1 skipped pass HTTP-live · no readonly/engine-owned value minted through overrides: forged audit stamps, organization_id, f_autonumber and f_formula all re-derived on the clone re-read
    records-forms.record-clone-contract · r3 N2 partial pass HTTP-live · booted HTTP 201 body is bare {object,id,sourceId,record}
    records-forms.record-clone-contract · r3 N3 skipped pass cli · run-record note re-checked: grep of ui/packages + ui/apps at 7fa9873 finds no caller of the record clone door (only metadata clones: permission-set-clone, FlowItemMoreMenu, and the Studio enable.clone switch in ObjectSettingsPanel) — the surface is still API-only
    records-forms.record-discussion-mentions · r13 A0 partial pass HTTP-live · console composer post on showcase_account bcAq.. -> POST /api/v1/data/sys_comment 201; GET sys_comment $filter {thread_id:"showcase_account:bcAq.."} $orderby created_at asc -> 200, 3 rows in created_at order incl. "QA R6 mention2 hi @qa Member R6" with mentions [""]
    records-forms.record-discussion-mentions · r13 A1 skipped pass browser-live · account record page timeline stream: composer post -> 1 row "QA R6 mention2 hi" at +250ms (optimistic), 1 at +3s, 1 after reload (screenshots post and post-reload each show exactly one row, Activity count 5); single POST 201 in network log
    records-forms.record-discussion-mentions · r13 A2 skipped pass browser-live · surface = authored record:discussion panel (Project Detail, showcase_project Mobile App): rows read oldest-first: comment 08:10:53 -> sys_activity "Updated Project "Mobile App": Budget" 08:11:16 (sys_activity read: type updated) -> comment posted after the edit; one feed, two tables, ascending
    records-forms.record-discussion-mentions · r13 A3 skipped pass browser-live · member (minted, sys_comment read granted) form sign-in: bell badge 2 = API unreadCount 2; popover lists "Dev Admin mentioned you / QA R6 mention2..."; row click -> POST /notifications/read 200, lands on /_console/apps/com.example.showcase/showcase_account/record/bcAq.. (record rendered, h1 "QA R6 account"); badge 1 = API 1; Mark all read -> POST /notifications/read/all 200, badge gone, API 0
    records-forms.record-discussion-mentions · r13 A4 partial pass browser-live · after 3 API comments + 1 console comment: member inbox 2 collab.mention (account only); bystander (also granted sys_comment read, never mentioned) bell shows no badge + "You're all caught up", API unreadCount 0; admin (author, also named in mentions) inbox gained 0 collab.mention
    records-forms.record-discussion-mentions · r13 A5 partial pass HTTP-live · qa-feeds-disabled recipe replayed (pkg 201 ns qafeeds, object PUT 200): POST sys_comment thread qafeeds_nofeeds:5LjF.. -> 403 {"code":"FEEDS_DISABLED","object":"qafeeds_nofeeds"}; GET sys_comment by that thread_id -> 200 0 rows; control POST on showcase_account:bcAq.. -> 201
    records-forms.record-discussion-mentions · r13 A6 partial pass HTTP-live · PATCH /api/v1/data/sys_comment/eguw.. {thread_id:"qafeeds_nofeeds:5LjF.."} -> 403 FEEDS_DISABLED; re-read thread_id still showcase_account:bcAq..; body-only PATCH -> 200 (body edited); control re-thread onto feeds-enabled showcase_account:esY5.. -> 200 and re-read follows
    records-forms.record-discussion-mentions · r13 A7 skipped pass browser-live · Project Detail record:discussion panel, Select offers exactly All Activity / Comments Only / Field Changes / Tasks Only; all = comment + "Updated Project...: Budget"; comments_only = the comment only; changes_only = the update row only; tasks_only = empty ("No comments yet", no task rows exist); back to all restores both (screenshot per mode)
    records-forms.record-discussion-mentions · r13 A8 partial pass browser-live · milestone channel available: scratch object qafeeds_milestone (qa.feeds pkg) with activityMilestones type "qa_custom_shipped"; PATCH status shipped -> sys_activity row type qa_custom_shipped; record page timeline stream (default unfiltered view, not the FeedFilterMode panel) renders "QA custom milestone fired for qa-ms-1" with the generic edit-icon row, Activity count 2 - row not dropped. Named diagnostic not observed in browser console (filter matched nothing); rests on the live render
    records-forms.record-discussion-mentions · r13 A9 partial pass HTTP-live · single boot: member reads note 404 / account 200; note comment by id and by email -> 0 collab.mention, boot log 2x "[audit] mention notification withheld" cannot_read_source userId= commentIds kYTF../Dy7L..; account comment (member+admin) -> member +1 collab.mention actionUrl /showcase_account/bcAq.., admin +0. Walled group boot (41962): user minted in org B, granted sys_comment read (GET 200); control comment on B account -> user +1 collab.mention actionUrl /showcase_account/4g0f..; org A account read by user 404; A comments by id and by email -> user inbox unchanged, admin +0, log 2x withheld cannot_read_source (JbTb.., lxyT..)
    records-forms.record-discussion-mentions · r13 N0 skipped pass browser-live · exactly one row for the posted comment before and after reload; POST response id d0f01707.. matches the single server row in the thread read (no dupe, no phantom)
    records-forms.record-discussion-mentions · r13 N1 partial pass HTTP-live · non-mentioned bystander inbox 0 rows / bell empty; only member gained collab.mention
    records-forms.record-discussion-mentions · r13 N2 partial pass browser-live · server half 403 FEEDS_DISABLED (A5); qafeeds_nofeeds record page: no composer (0 textarea / composer button), timeline shows activity only, network log shows NO sys_comment request on the page load; per objectstack#27055 the record:activity stream stays (activity-only) while comment writes are withheld
    records-forms.record-discussion-mentions · r13 N3 partial pass HTTP-live · refused re-thread left thread_id unchanged (A6 re-read)
    records-forms.record-discussion-mentions · r13 N4 partial pass HTTP-live · both refusals paired with same-fixture 2xx controls (create 201, re-thread to enabled parent 200), admin can read/edit the public_read_write parent
    records-forms.record-discussion-mentions · r13 N5 partial pass browser-live · no mode showed another mode's row type; custom-type milestone row present in the unfiltered feed
    records-forms.record-discussion-mentions · r13 N6 partial pass HTTP-live · withheld from the user who cannot read the record (single: private note; walled: other-organization account), by id and by email; delivered where both reads succeed; author never notified - both live legs
    records-forms.related-list-server-pagination · r11 A0 skipped pass HTTP-live · Northwind detail at 1440x900: POST /api/v1/data/showcase_account/PZdN17H0QUFE5fdk/page related [contact, field_zoo x2, invoice, project] each top:3; GET showcase_invoice?top=10&skip=0&filter=["account","=",NW], showcase_project?top=10&skip=0&filter=...; enlarged Contacts GET showcase_contact?...&top=25&skip=0&filter=["account","=",NW] then top=25&skip=25; no child request without a top bound
    records-forms.related-list-server-pagination · r11 A1 partial pass HTTP-live · enlarged Contacts (?related=showcase_contact.account): "26 rows · Page 1 of 2", page 1 response total 26 / 25 rows; Next -> "Page 2 of 2", total 26 / 1 row; page-1 and page-2 id sets overlap 0, union 26
    records-forms.related-list-server-pagination · r11 A2 skipped pass browser-live · stock detail screenshot: Invoices and Projects body tables in the details column, Contacts 26 card (3 rows) plus two Field Zoo lookup cards in the related column; no hand-built page
    records-forms.related-list-server-pagination · r11 A3 skipped pass browser-live · Invoices body table title "Invoices" with headers Invoice Number, Status, Total, Issued On (= relatedListColumns name, status, total, issued_on; relatedListTitle Invoices); its request selects exactly id,name,status,total,issued_on
    records-forms.related-list-server-pagination · r11 A4 partial pass HTTP-live · clicking "Prospect 12" in the enlarged list opens its record (drawer "Prospect 12", 4 / 25, id BTTIbL2II42egI_N, account Northwind); GET /api/v1/data/showcase_contact/BTTIbL2II42egI_N -> 200 same email prospect12@northwind.example and account PZdN17H0QUFE5fdk
    records-forms.related-list-server-pagination · r11 A5 partial pass HTTP-live · contributor (invited member + sys_user_position contributor, session proven qa-r4-ctb): detail screenshot has no Contacts card or tab, page aggregate omits showcase_contact, forged GET showcase_contact?$filter=[["account","=",NW]] -> 403 PERMISSION_DENIED 0 rows. manager (position manager, session qa-r4-mgr): Contacts 26 tab + card render, same forged query -> 200 total 26, 26 rows
    records-forms.related-list-server-pagination · r11 N0 skipped pass HTTP-live · every child request on the detail and enlarged list carried top (3 / 10 / 25); none unbounded
    records-forms.related-list-server-pagination · r11 N1 partial pass HTTP-live · pager total 26 (Page 2 of 2 at 25) = direct GET showcase_contact?$filter=[["account","=",NW]] total 26
    records-forms.related-list-server-pagination · r11 N2 partial pass browser-live · Contacts section absent for the contributor (screenshot + page aggregate without showcase_contact) and its child query 403 with no rows
    records-forms.v18-semantic-defaults · r6 A0 pass pass pins · cited from #28965: spec-schema (test oracle): field-autonumber-default-unique.test.ts, ui/view.test.ts, record-surface.test.ts green in packages/spec (12 files, 1077 tests, 0 failed) at 7fa9873.
    records-forms.v18-semantic-defaults · r6 A1 skipped pass browser-live · GET /meta/view/showcase_task.default: config has no pagination/pageSize and no userActions; 50 showcase_task rows seeded via createMany (60 total). Admin at 1440x900 /_console/apps/showcase_app/showcase_task/view/showcase_task.default: screenshot shows the grid with 'Rows per page: 50', 'Page 1 of 2', '60 records'; list request GET /data/showcase_task?…&top=50; 50 tbody rows rendered
    records-forms.v18-semantic-defaults · r6 A2 skipped pass browser-live · Display popover (data-testid toolbar-display-trigger) at 1440x900, each persona form-signed-in and verified by get-session: contributor-bound member (admin create-user + change-password + sys_user_position 'contributor'; me/permissions showcase_task allowEdit:true; sees its 3 assigned tasks) → screenshot shows 'Edit inline' switch, off (aria-checked false); plain member (default set, allowEdit:false) → popover shows Layout/Open records/Group/Density/Row numbers and NO Edit inline row
    records-forms.v18-semantic-defaults · r6 A3 skipped pass browser-live · New on showcase_field_zoo (50 fields): 1440x900 → screenshot shows a bounded 'Create Field Zoo' modal, dialog box 1024x810 at (208,45) over the dimmed list; 390x844 mobile → dialog box 0,0 390x844, full-screen sheet with stacked Cancel/Create
    records-forms.view-type-gallery · r3 A0 skipped pass browser-live · one screenshot per type via /showcase_task/view/: grid (row-colored table), kanban (5 status columns of cards), gallery (10 cards, coverless by design per knownGaps), calendar (Nov 2026 month grid with titled chips), timeline (dated vertical timeline, 10 entries), gantt (task list + dated bars with Today line), map (fallback list under the "Map failed to load" banner, the environment fact; 10 rows), chart (bar SVG with 4 priority series over 5 status buckets), tree (Organization Chart expandable nesting). No type fell back to a grid table
    records-forms.view-type-gallery · r3 A1 partial pass browser-live · board screenshot + DOM: columns BACKLOG 2 Σ26, TO DO 2 Σ37, IN PROGRESS 2 Σ584, IN REVIEW 2 Σ54, DONE 2 Σ24; direct GET /api/v1/data/showcase_task?top=100 grouped client-side: backlog 2/26, todo 2/37, in_progress 2/584, in_review 2/54, done 2/24 (equal; estimates reflect this run's edits)
    records-forms.view-type-gallery · r3 A2 partial pass browser-live · sampled vs API reads: calendar places Build homepage on Nov 12 and SEO migration plan on Nov 17 (due_date 2026-11-12 / 11-17); timeline shows Ingest pipeline 9/23/2026 and Warehouse schema 10/8/2026 (created_at 2026-09-23 / 10-08); gantt rows Build homepage 10/27-11/12 and Design system 10/21-11/4 (start_date/end_date match) with the darker progress fill drawn inside bars
    records-forms.view-type-gallery · r3 A3 skipped pass browser-live · map (environment fact: no tiles here): data-testid map-style-fallback present, role=alert "Map failed to load ...", list "Locations" holds 10 rows = 10 of 10 tasks carrying a location, no canvas; search box "Ingest" narrows the list to 1 row (Ingest pipeline). chart: POST /api/v1/analytics/dataset/query 200 after GET /api/v1/meta/dataset/showcase_task_metrics; main svg carries 8 recharts bar rectangles
    records-forms.view-type-gallery · r3 A4 skipped pass browser-live · Organization Chart screenshot (Acme root, two divisions), after expanding: Acme 0 > Product & Engineering 20px > Frontend Guild 40 > Design Systems 60, Platform 40; Go-To-Market 20 > Sales 40 > Enterprise Sales 60, Marketing 40 — identical to the parent field of all 9 rows from GET /api/v1/data/showcase_business_unit; no pageerror
    records-forms.view-type-gallery · r3 A5 pass pass pins · cited from #28965: browser-live: showcase-smoke.spec.ts 50/50 twice: 'surface renders cleanly: All Views / Task Board / Task Calendar / Task Gallery / Team Schedule / Activity Timeline / Work Map' assert no pageerror and non-empty main past the boot splash
    records-forms.view-type-gallery · r3 N0 skipped pass browser-live · none of the 9 views rendered a plain grid in place of its type (screenshots per A0); the chart page and the tree view carry their own structures
    records-forms.view-type-gallery · r3 N1 skipped pass browser-live · chart weakness noted: 8 plotted bars over 5 status buckets and 4 priority series, from 10 task rows (not a single datapoint); values reflect estimates this run rewrote
  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB

    Per-clause verdicts (3/3): studio-authoring. #28965 is the pins-only verdict of W4-C2b; here is this card's verdict. A clause the pins scored pass is cited, not re-run; every other clause was driven live on a booted stack at 7fa9873f.

    item · rev clause #28965 here layer · text evidence
    studio-authoring.actions-tab-presets · r3 A0 partial pass browser-live · Screenshot of the New menu on rthree_widget: COMMON ACTIONS Change a picklist field ('Asks for a new "Status", then saves it.'), Run a flow, Open a web page, Open a page in a dialog, then Advanced > New action. Change a picklist field sent one PUT 200 writing {label:'Change Status',operation:'update',params:[{field:'status'}]} (the author's own select, not a system field). On rthree_plain (no picklist) the row is aria-disabled with 'Needs a picklist field on this object.' and a forced click sends nothing.
    studio-authoring.actions-tab-presets · r3 A1 partial pass HTTP-live · Run a flow / Open a web page / Open a page in a dialog each showed 'Not saved yet: the action ... needs "Flow name" / "URL" / "Modal / page name"' and sent 0 PUTs (label edits included) until the target was given, then one PUT 200 each (type flow target rthree_stamp_flow; type url target http://localhost:41930/api/v1/health?rid=${ctx.recordId}; type modal target rthree_dialog_page). A fourth preset (Run a flow, rthree_widget_action_5) deleted while held: 0 requests; GET /api/v1/meta/object/rthree_widget?state=draft&package=com.qa.rthree actions = [action_1..action_4] only.
    studio-authoring.actions-tab-presets · r3 A2 skipped pass HTTP-live · As member r3.member@example.com (own browser context, form sign-in, session read back as that email) on /_console/apps/rthree_app/rthree_widget/record/QZ4I222fFPyZj3oC: Change Status prompt -> In progress -> POST /api/v1/actions/rthree_widget/rthree_widget_action_1 {params:{status:'in_progress'}} 200, GET record status=in_progress; Run stamp flow -> POST .../rthree_widget_action_2 200, sys_automation_run rthree_stamp_flow status completed (trigger manual) and record stamp='flowran'; Open web page navigated to http://localhost:41930/api/v1/health?rid=QZ4I222fFPyZj3oC (real id); Open dialog page (More actions menu) opened a dialog titled 'R3 Dialog Page' rendering the card 'R3 Dialog …
    studio-authoring.actions-tab-presets · r3 A3 partial pass browser-live · Every object PUT /api/v1/meta/object/rthree_widget?mode=draft of the Actions session answered 200 (presets, targets, label edits, advanced action, script body edits, AI exposure); no 4xx. The AI-exposure step was held client-side ('Tool description (required, >=40 chars) ... needs a value') rather than sent and refused.
    studio-authoring.actions-tab-presets · r3 A4 skipped pass HTTP-live · Advanced > New action > What it does 'Run a script' > Sandboxed JS (L2): PUT body carries type:'script', body {language:'js',source:...}; published. Member click (More actions > Script stamp) sends POST /api/v1/actions/rthree_widget/rthree_widget_action_5 {recordId} -> 200 {success:true,data:{ok:true,ranFor:'QZ4I222fFPyZj3oC'}} and toast 'Action completed successfully'. A body that writes (ctx.api.object(...).update) answered 403 INSUFFICIENT_PRIVILEGES 'capability api.write not granted' (2/2 loads) because the Studio action editor offers no capabilities control - see defect_drafts.
    studio-authoring.actions-tab-presets · r3 A5 skipped pass HTTP-live · Ticking 'Expose to AI agents' on Change Status held the save until the >=40-char tool description was typed; then PUT 200 with action ai:{exposed:true,description:'Call this to move an R3 widget...'} and no flat aiExposed key; a later label edit saved 200.
    studio-authoring.actions-tab-presets · r3 N0 partial pass HTTP-live · No held preset reached the server before its target (A1 trace + draft read) and each published preset did its job when the member clicked it (A2).
    studio-authoring.automations-rail-and-new-flow · r2 A0 skipped pass browser-live · fixture: scratch package com.objectstack.qa.r6auto (ns qaauto) with objects qaauto_alpha "Alpha Widget" / qaauto_beta "Beta Gadget", record flows on each, a manual flow and a schedule flow with config.schedule cron, all via PUT /meta (200). Rail screenshot: headings ALPHA WIDGET (Alpha Welcome Mailer, lightning glyph), BETA GADGET (Beta Sync Hook, lightning), trailing NO OBJECT (Manual Cleanup arrow glyph = autolaunched, Nightly Digest clock glyph = scheduled); dots green On x3, grey Not running here for the schedule flow (default boot, scheduled work off)
    studio-authoring.automations-rail-and-new-flow · r2 A1 skipped pass browser-live · auto-rail-legend before: "Record-triggered / Scheduled / Screen / Autolaunched / On / Not running here" (no Off/Refused); Enabled switch off on Beta Sync Hook -> PUT /meta/flow/qaauto_beta_updated?mode=draft 200 + toast "Disabled - publish to apply", legend unchanged; after Publish all (POST /packages//publish-drafts 200) legend reads "... / Not running here / Off" and the row reads Off
    studio-authoring.automations-rail-and-new-flow · r2 A2 partial pass browser-live · search "aLPHa wel" -> only qaauto_alpha_created (label, case-insensitive); "manual_clean" -> only qaauto_manual_cleanup (machine name, label "Manual Cleanup" lacks the underscore); "zzqq-nothing" -> 0 rows and rail text "No matches for "zzqq-nothing" / Clear search"; cleared -> all 4 rows back
    studio-authoring.automations-rail-and-new-flow · r2 A3 partial pass HTTP-live · New automation: presets record_created/updated/deleted/manual + Advanced; name typed, record-created preset, no object -> "Choose the object it watches." shown, network log empty (no PUT); object Alpha Widget chosen -> PUT /meta/flow/qa_r6_alpha_created_flow?mode=draft&package=... If-None-Match * 200; header switch aria-checked=false "Disabled / Off by default"; GET ?state=draft -> status "obsolete", start config {triggerType:"record-after-create", objectName:"qaauto_alpha"}
    studio-authoring.automations-rail-and-new-flow · r2 N0 partial pass browser-live · no-match query shows the "No matches for ..." line with a Clear search action, never a blank rail
    studio-authoring.automations-rail-and-new-flow · r2 N1 partial pass HTTP-live · new flow created Off (switch false, status obsolete) and refused to save without the trigger object
    studio-authoring.flow-status-matches-runtime · r2 A0 partial pass browser-live + HTTP-live · writable com.qa.pkga with object pkga_qa_obj and 3 flows authored via ?package= (record_change pkga_rec_flow status obsolete, autolaunched pkga_manual_flow, schedule pkga_sched_flow interval 60s). Boot 1 pairing (rail data-status/sr-only words · header title · _status): rec Off/'Disabled — will not run' · enabled false bound false; manual On/'Enabled · no trigger (run manually)' · enabled true bound false; sched 'Not running here' · enabled true bound false unboundCause deployment_policy; after enable+publish rec On/'Enabled · bound to its trigger' · enabled true bound true; after disable+publish rec Off · enabled false status obsolete. Boot 2 (scheduled work on): rec Off·false/fal …
    studio-authoring.flow-status-matches-runtime · r2 A1 partial pass browser-live · after screenshots confirmed the pillar rendered: sched rail dot title == _status reason string verbatim ('disabled by deployment policy — package-authored scheduled work is off …'); header Status title 'Status: Not running here\n'; Problems tab (screenshot) shows a 'Not running here' note carrying the reason and 'No problems — this flow is structurally valid.', no count on the tab
    studio-authoring.flow-status-matches-runtime · r2 A2 skipped pass HTTP-live + browser-live · header switch click → toast 'Enabled — publish to apply', PUT /meta/flow/pkga_rec_flow?mode=draft&package=com.qa.pkga (status active); Publish → sheet → Publish all → POST /packages/com.qa.pkga/publish-drafts 200, toast 'Published 1 item in this package'; POST /data/pkga_qa_obj 201 → GET /automation/pkga_rec_flow/runs: 1 run completed, trigger record-after-write recordId WEOFpU2Be…; disable (toast 'Disabled — publish to apply') + publish 200, _status enabled false; POST second record 201 → runs still the single earlier run
    studio-authoring.flow-status-matches-runtime · r2 A3 skipped pass HTTP-live + browser-live · boot 2 with OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1: _status pkga_sched_flow enabled true bound true reason null; Studio rail data-status 'on' label On and header 'Enabled · bound to its trigger'; GET /automation/pkga_sched_flow/runs → completed run trigger.type 'schedule' at 07:54:51Z (~1 min after boot)
    studio-authoring.flow-status-matches-runtime · r2 N0 partial pass browser-live · on boot 1 the unbound schedule flow read 'Not running here' (grey dot, data-status not-running), never On, and Problems listed it as a note with no problem — the deployment policy is painted as neither health nor error
    studio-authoring.held-edit-publish-refusal · r3 A0 partial pass browser-live · screenshots of the error toast after Publish all, per pillar: Data 'Not published: the field “Priority” needs at least one option, so that change is not saved yet. Finish it, then publish.'; Automations 'Not published: Title on the step “Notify” needs a value, so that change is not saved yet. Finish it, then publish.'; Interfaces 'Not published: Dataset on the widget “New metric (kpi)” needs a value, so that change is not saved yet. Finish it, then publish.'
    studio-authoring.held-edit-publish-refusal · r3 A1 partial pass network + HTTP-live · per pillar the trace window after the refused Publish all held 0 non-GET requests (no /packages/com.qa.pkga/publish-drafts, no /meta/…/publish); GET /meta/_drafts?packageId=com.qa.pkga and the live reads of pkga_qa_obj / pkga_manual_flow / pkga_dash1 were byte-identical to the baseline taken before the edit (3/3)
    studio-authoring.held-edit-publish-refusal · r3 A2 partial pass HTTP-live + network · after finishing each edit: autosave PUT …?mode=draft&package=com.qa.pkga 200 and the held strip gone; Publish all → POST /packages/com.qa.pkga/publish-drafts 200 (flow and dashboard legs: body outcome 'published', publishedCount 2); toasts 'Published 2 items in this package'; live reads carry the finished field (priority select, options [{value:'high'}]), the step (node_1 notify title 'QA step title') and the widget (metric, dataset pkga_qa_ds, values [rec_count])
    studio-authoring.held-edit-publish-refusal · r3 N0 partial pass network · while each held strip showed, the refused Publish sent no publish request, so the batch never went around the unsent change (3 pillars)
    studio-authoring.hooks-tab-authoring · r3 A0 partial pass browser-live · Wire headers: New hook's first PUT /api/v1/meta/hook/rthree_widget_hook_1?mode=draft carried If-None-Match: * (200); each later autosave carried If-Match equal to the version the previous PUT answered (139e6f..->a6d632.., a6d632..->e201ff..); after a page reload, the first save of the hook with a pending draft sent If-Match hmac-sha256:2472b65d.. = the version GET /meta/hook/rthree_widget_hook_1?state=draft served (200).
    studio-authoring.hooks-tab-authoring · r3 A1 partial pass HTTP-live · Name typed to rthree_widget_hook_1_renamed with 1.5-2.5 s pauses between parts: 0 PUTs, line 'Not saved yet: a hook keeps the name it was created with. Change the name back to rthree_widget_hook_1 to save your changes.'; GET /api/v1/meta/_drafts?packageId=com.qa.rthree&type=hook listed only rthree_widget_hook_1; name restored + label edit -> one PUT 200.
    studio-authoring.hooks-tab-authoring · r3 A2 partial pass HTTP-live · Run only when "record.name == 'cond' &&": inline 'Unexpected token: EOF', header 'Fix the CEL syntax errors before saving.', 0 PUTs in 4 s; fixed to "record.name == 'cond'" -> PUT 200. After publish: POST name 'cond' 201 reads stamp='stamped'; POST name 'other' 201 reads stamp=null.
    studio-authoring.hooks-tab-authoring · r3 A3 skipped pass HTTP-live · Server log (OS_LOG_LEVEL=info): hook_2 (retryPolicy {maxRetries:2,backoffMs:50}, onError log) logged 'WARN [hook] retrying after failure {attempt:1,maxRetries:2}' and '{attempt:2,maxRetries:2}' then 'ERROR [hook] handler failed (onError=log; suppressing)'; its POST answered 201 (row stored). hook_3 (onError abort) POST answered 400 {error:'r3 abort thrower',code:'HOOK_ABORTED'}; list read-back finds 0 rows named throwabort.
    studio-authoring.hooks-tab-authoring · r3 N0 partial pass HTTP-live · The paused rename staged no partial-name draft: _drafts lists only the original name (A1).
    studio-authoring.interfaces-create-dialogs · r2 A0 partial pass browser-live · P1 com.objectstack.qa.r6ifa (objects qaifa_ticket, qaifa_part published) /interfaces: "This package has no app yet ... Create app", 0 New dashboard/report/page entries; Create app "Repair Center" -> Identifier repair_center, box on, Save as draft -> PUT /meta/app/repair_center?mode=draft&package=P1 If-None-Match * 200, toast "App “Repair Center” saved as draft — open it after publishing", lands /studio/P1/interfaces?surface=object:qaifa_ticket; draft read navigation [{type:object,objectName:qaifa_ticket},{type:object,objectName:qaifa_part}]; P2 (qaifb_asset) box cleared -> app asset_desk draft navigation [] and lands on /studio/P2/interfaces
    studio-authoring.interfaces-create-dialogs · r2 A1 partial pass browser-live · dashboard: PUT /meta/dashboard/qaifa_ops_board?mode=draft&package=P1 (If-None-Match *) 200 + PUT /meta/app/repair_center?mode=draft 200, opens dashboard canvas "Design ... Add widget"; report: PUT /meta/report/qaifa_ticket_report 200 + app PUT 200, report canvas "Tickets 3"; page HTML qaifa_handbook and page React qaifa_react_page: item PUT 200 + app PUT 200 each, source sheet (flex/HTML resp. function Page()) beside the live preview
    studio-authoring.interfaces-create-dialogs · r2 A2 partial pass browser-live · report with no dataset -> "Choose the dataset this report reads, and one of its measures." in dialog, 0 writes; dataset Ticket Metrics chosen, no measure -> same refusal, 0 writes (network window empty of PUT/POST)
    studio-authoring.interfaces-create-dialogs · r2 A3 partial pass browser-live · dashboard id showcase_revenue_pulse (published, showcase pkg) and qaifb_held_dash (draft in P2): "A dashboard with the identifier “…” already exists. Choose another identifier.", 0 writes each; page id showcase_start_here (published) and qaifb_held_page (draft in P2): "A page with the identifier “…” already exists...", 0 writes each
    studio-authoring.interfaces-create-dialogs · r2 A4 skipped pass browser-live · Publish all -> POST /packages/P1/publish-drafts 200 (5 items); GET /meta/app names [showcase_app, setup, account, repair_center]; ?id=repair_center -> nav object:qaifa_ticket, object:qaifa_part, dashboard:qaifa_ops_board, report:qaifa_ticket_report, page:qaifa_handbook, page:qaifa_react_page; Home shows Repair Center; each entry rendered without an error panel: Repair Ticket list (3 rows), Spare Part empty list, Ops Board (empty dashboard header + Refresh All), Ticket Report (Tickets 3), Repair Handbook and Repair Widget Page (page text), 0 pageerrors
    studio-authoring.interfaces-create-dialogs · r2 N0 partial pass browser-live · every successful create sent the item PUT and the app PUT as a pair (5/5); every refusal sent neither
    studio-authoring.meta-save-preconditions-and-draft-rows · r2 A0 pass pass pins · cited from #28965: test-oracle: all 5 named files ran green at the subject: packages/rest 14/14, packages/runtime 16/16, packages/objectql 29/29 files passed (pins/SUMMARY); each file is in the batch and has cases (e.g. 'two readers save in turn: the second is refused 409').
    studio-authoring.meta-save-preconditions-and-draft-rows · r2 A1 partial pass HTTP-live · PUT ?mode=draft 200 v=c724…; GET ?state=draft serves version c724…; PUT If-Match c724 → 200 (v=1ce9…); PUT again If-Match c724 → 409 METADATA_CONFLICT currentVersion=1ce9…; after active save, PUT If-None-Match:* → 409 METADATA_CONFLICT currentVersion c724… (draft lifecycle likewise 409); If-None-Match:"abc" → 400 VALIDATION_ERROR; If-Match+If-None-Match → 400 VALIDATION_ERROR; read-back: active label 'QA OCC View', draft 'QA OCC View v2' (the refused 'SHOULD NOT LAND' body stored nowhere)
    studio-authoring.meta-save-preconditions-and-draft-rows · r2 A2 partial pass HTTP-live · GET /api/v1/meta/_drafts → {drafts:[{type:'view',name:'showcase_task.qa_occ_view',label:'QA OCC View v2',packageId:null,updatedBy:'uAM08…'}]} — label is the draft body's label, not the machine name
    studio-authoring.meta-save-preconditions-and-draft-rows · r2 A3 partial pass HTTP-live · GET …/qa_occ_view/history → events seq1-3 each actor 'uAM08iEYsdiFl7g4u58W6lEA55MXjZqS' (= sys_user admin@objectos.ai); DB oracle sys_metadata draft+active rows created_by=updated_by=that id
    studio-authoring.metadata-diagnostics-sweep · r10 A0 partial pass HTTP-live · GET /api/v1/meta/diagnostics (live REST, bare) 200 keys [entries,total,scannedTypes,scannedItems,stats]; total 0 = entries.length 0; scannedTypes 30 = registry types with getMetadataTypeSchema (30 of 32, computed from @objectstack/spec/kernel); scannedItems 420
    studio-authoring.metadata-diagnostics-sweep · r10 A1 partial pass HTTP-live · object/view/flow/dashboard: stats.count = GET /meta/ list length (116/36/32/6), stats.packages = sorted distinct _packageId (equal for all 4), stats.locked = count of /layers envelope lock!='none' (115/0/32/0, all equal)
    studio-authoring.metadata-diagnostics-sweep · r10 A2 skipped pass HTTP-live + browser-live · ?package=com.example.showcase: stats narrow (object 116→24, dashboard 6→3, scannedItems 420→252), scoped list parity holds for object/view/flow/dashboard; screenshot of /_console/studio/com.example.showcase/metadata settled: tiles Hook 8, Mapping 1, Object 24, Action 72, App 1, Dashboard 3, Dataset 4, Page 29, Report 4, View 36, Flow 32, Job 1 … all equal the scoped stats counts; page fetched /meta/diagnostics?severity=warning&package=com.example.showcase
    studio-authoring.metadata-diagnostics-sweep · r10 A3 partial pass HTTP-live · GET /meta/diagnostics?type=viewes → 400 INVALID_REQUEST "'viewes' is not a recognised spelling of metadata type 'view'. Address it as 'view' or 'views'…"; ?severity=error&severity=warning → 400 VALIDATION_ERROR 'The "severity" query parameter was supplied 2 times…'
    studio-authoring.metadata-diagnostics-sweep · r10 A4 skipped pass HTTP-live · clean store: severity=warning and severity=error both 200 total 0; with the planted row both total 1 and entries JSON byte-identical; no warning-only rows
    studio-authoring.metadata-diagnostics-sweep · r10 A5 skipped pass browser-live · after removing the planted row: screenshot of /_console/studio/com.example.showcase/metadata/_diagnostics shows the green 'All clear' empty state 'Scanned 420 item(s) across 30 type(s) — no issues found.' (= API body 420/30); directory header has no 'View all issues' link (count 0)
    studio-authoring.metadata-diagnostics-sweep · r10 A6 skipped pass HTTP-live + browser-live · planted row (valid view pkga_qa_obj.qa_diag_view saved ?package=com.qa.pkga, then sys_metadata.metadata config.columns[0].field=123 on the run's own file DB): sweep total 1, entry {type:view,name,diagnostics.errors:[('',Invalid input),(config.columns,…),(config.columns.0,…),(config.columns.0.field,'expected string, received number')]} — located leaf present; pkga directory: View tile red badge 1 + 'View all issues (1)'; diagnostics page groups it under VIEW (1); row link /studio/com.qa.pkga/metadata/view/pkga_qa_obj.qa_diag_view lands on /studio/com.qa.pkga/interfaces?surface=view:pkga_qa_obj.qa_diag_view, the item's view designer (columns shows '123')
    studio-authoring.metadata-diagnostics-sweep · r10 A7 pass pass pins · cited from #28965: test-oracle: protocol.diagnostics-store-outage.test.ts green (metadata-protocol 10/10 files): 'the whole-corpus sweep propagates the 503 instead of answering total: 0, stats: {}' and 'the single-type sweep is held to the same rule' (503 SERVICE_UNAVAILABLE).
    studio-authoring.metadata-diagnostics-sweep · r10 N0 partial pass HTTP-live · ?type=viewes answered 400 INVALID_REQUEST, not 200 {scannedTypes:1,stats:{}}
    studio-authoring.metadata-diagnostics-sweep · r10 N1 pass pass pins · cited from #28965: test-oracle: 'an outage and a first boot are now distinguishable by the caller' and the 503 cases assert an unreadable store is never answered as a clean sweep; outage is pin-only by the item's own step.
    studio-authoring.metadata-diagnostics-sweep · r10 N2 skipped pass browser-live · clean store renders the designed All-clear state with counts (screenshot), not a blank region
    studio-authoring.metadata-diagnostics-sweep · r10 N3 skipped pass HTTP-live · recording guard honoured: every sweep read was the live REST server on :41920, no dispatcher call
    studio-authoring.multi-package-item-rows · r4 A0 pass pass pins · cited from #28965: test-oracle: all 9 named files ran green at the subject: packages/rest 14/14, runtime 16/16, objectql 29/29, metadata-protocol 10/10 files passed (pins/SUMMARY); every file is in its batch with cases.
    studio-authoring.multi-package-item-rows · r4 A1 partial pass HTTP-live · scratch packages com.qa.pkga/com.qa.pkgb (POST /api/v1/packages 201) each PUT dashboard qa_shared_dash 200; package-less PUT → 409 RESOURCE_CONFLICT 'belongs to 2 packages … Nothing was written'; package-less DELETE → 409 same; unscoped /layers → 409; sys_metadata still 2 rows (A 'QA Shared A', B 'QA Shared B'); DELETE ?package=A → 200 'its row bound to com.qa.pkgb remains'; package-less PUT → 200 'package=com.qa.pkgb'; sys_metadata 1 row (pkgb, new label), /layers unscoped 200 effective label = new
    studio-authoring.multi-package-item-rows · r4 A2 partial pass HTTP-live · PUT /meta/object/pkga_qa_obj?package=com.qa.pkga (sharingModel private) 200; PUT same ?package=com.qa.pkgb → 422 OBJECT_OVERLAY_PACKAGE_MISMATCH ('owned by package com.qa.pkga'); read-back: sys_metadata one row package_id=com.qa.pkga label 'QA Obj A', GET fields lack B's 'extra'
    studio-authoring.multi-package-item-rows · r4 A3 partial pass HTTP-live · PUT /meta/object/showcase_project (plain, If-None-Match:*, ?mode=draft) → 403 NOT_OVERRIDABLE, error exactly "'object/showcase_project' is provided by a managed package and is sealed: it cannot be written here. GET /api/v1/meta/object lists the names in use."
    studio-authoring.multi-package-item-rows · r4 A4 partial pass HTTP-live · pkga: draft pkga_dash1 → POST /packages/com.qa.pkga/publish-drafts 200 outcome published count 1; draft edit 'Dash1 DRAFT EDIT' + draft-only pkga_dash2; POST /packages/com.qa.pkga/revert 200 → _drafts empty, pkga_dash1 serves 'Dash1 published body', pkga_dash2 404 (both lifecycles); never-published com.qa.pkgc (draft only) revert → 409 RESOURCE_CONFLICT 'has never been published…', its draft untouched
    studio-authoring.object-api-tab-curl · r2 A0 partial pass browser-live · Screenshot of /_console/studio/com.qa.rthree/data?surface=object:rthree_ref API tab shows exactly five rows: GET /api/v1/data/rthree_ref, POST /api/v1/data/rthree_ref, GET /api/v1/data/rthree_ref/{id}, PATCH /api/v1/data/rthree_ref/{id}, DELETE /api/v1/data/rthree_ref/{id}; the five copied cURLs carry the same methods/paths.
    studio-authoring.object-api-tab-curl · r2 A1 partial pass browser-live · Before: body {name:'string',contact:'',amount:0,due_on:'2024-01-01',stage:'new'}; Form tab Add field 'Notes Extra' (draft PUT ?mode=draft 200, header 'Draft - 1 unpublished', not published) -> re-read body adds notes_extra:'string'; after Publish all (POST /packages/com.qa.rthree/publish-drafts 200) the copied create cURL with that body (contact substituted) answered 201 and stored notes_extra='string'.
    studio-authoring.object-api-tab-curl · r2 A2 partial pass HTTP-live · All five copied cURLs carry -H 'Authorization: Bearer '; with the console's localStorage auth-session-token and a real showcase_contact id substituted and run in bash: list 200, create 201 (id I4BrJhbJSHLvqEcz), get 200, update(PATCH) 200, delete 200; get after delete 404.
    studio-authoring.object-api-tab-curl · r2 N0 partial pass HTTP-live · Live body leaves out owner_id and the optional lookup buddy (panel: 'Optional fields not shown: owner_id, buddy'), shows the required lookup as '', every other value right-typed; write path accepted it (201) after only the runner's id substitution. Second object rthree_widget (no required reference): its copied create body run verbatim with only the token answered 201.
    studio-authoring.object-api-tab-curl · r2 N1 skipped pass HTTP-live · Draft-only field draft_only added (PUT ?mode=draft 200); POST /api/v1/data/rthree_ref with it before publish -> 400 INVALID_FIELD 'Unknown field draft_only'; after publish the same create -> 201 storing draft_only='x' (guard holds as stated: pre-publish 400 is the draft not being live).
    studio-authoring.object-designer-roundtrip · r10 A0 partial pass HTTP-live · Studio Data pillar door (/_console/studio/com.qa.rthree/data). GET /api/v1/meta/object/rthree_repair_asset after publish 1 (POST /packages/com.qa.rthree/publish-drafts 200): fields [owner_id, name, asset_code, condition(select good=Good,worn=Worn,broken=Broken)]; after renaming option 'Worn'->'Used' and a dnd-kit pointer drag of Condition onto Owner (autosave PUT ?mode=draft 200) and publish 2: [owner_id, condition(good=Good,worn=Used,broken=Broken), name, asset_code] - rename and new order both persisted.
    studio-authoring.object-designer-roundtrip · r10 A1 partial pass browser-live · Studio Add field dialog: label 'Asset Code' typed + Tab shows 'API name: asset_code'; after Create the inspector's API name input reads asset_code (screenshot confirmed); record created via Records tab Add record posted {name:'Pump 7',asset_code:'AC-007'} -> 201, GET /api/v1/data/rthree_repair_asset/lYqTjk8bo76uocrE returns column key asset_code='AC-007', no field_N key.
    studio-authoring.object-designer-roundtrip · r10 A2 partial pass browser-live · New object dialog (screenshot confirmed): Label 'Repair Asset' typed, then identifier cleared and typed char-by-char 'repair_asset' (80ms/key); input value read 'repair_asset' before and after, preview 'Saved as rthree_repair_asset'; 'Save as draft' sent PUT /meta/object/rthree_repair_asset ?mode=draft 200; meta read item.name = rthree_repair_asset.
    studio-authoring.object-designer-roundtrip · r10 A3 partial pass HTTP-live · With 3 real options (Good/Worn/Broken) present, Add value added an empty row; after 4 s: 0 [data-testid=metadata-validation-banner], 0 'System identifier must be' text (screenshot shows no banner), no PUT for the empty row; published meta read carries exactly 3 options good/worn/broken.
    studio-authoring.object-designer-roundtrip · r10 N0 partial pass browser-live · Studio Data pillar door: the new field's API name derived to asset_code (not field_N) and the stored record key is asset_code. Console built from this tree (stale-bundle trap n/a).
    studio-authoring.org-override-registry-gate · r7 A0 partial pass HTTP-live · registry re-read (metadata-plugin.zod.ts: object allowEnvironmentOverlay false/allowRuntimeCreate true, job false/false, view true/true — unchanged); PUT /meta/object/showcase_task (full served body, label changed) → 403 code NOT_OVERRIDABLE; GET afterwards label 'Task'
    studio-authoring.org-override-registry-gate · r7 A1 partial pass HTTP-live · PUT /meta/view/showcase_task.in_progress label 'QA Overlaid In Progress' → 200 'Saved customization overlay'; GET serves the overlaid label; /layers?package=com.example.showcase code 'In Progress', overlay/effective 'QA Overlaid In Progress', resettable true
    studio-authoring.org-override-registry-gate · r7 A2 partial pass HTTP-live · PUT /meta/job/qa_probe_job → 403 code NOT_CREATABLE ('Metadata type job is code-only … allowRuntimeCreate=false'); GET afterwards 404
    studio-authoring.org-override-registry-gate · r7 A3 partial pass HTTP-live · same run: PUT /meta/object/qa_org_probe (new, sharingModel private) → 200 state active and GET 200, beside the showcase_task 403 NOT_OVERRIDABLE
    studio-authoring.org-override-registry-gate · r7 A4 skipped pass HTTP-live · DELETE /meta/view/showcase_task.in_progress → 200 {success:true, reset:true, 'reset to artifact default'}; GET serves 'In Progress' again; second DELETE → 200 reset:false 'already at artifact default'
    studio-authoring.org-override-registry-gate · r7 N0 skipped pass HTTP-live · no 200 on an artifact-backed overlay: object showcase_task 403 NOT_OVERRIDABLE and field showcase_task.title 403 NOT_OVERRIDABLE (code-only type sentence); neither stored (label unchanged)
    studio-authoring.org-scope-flows · r2 A0 partial pass browser-live · Studio landing 'Organization flows' → /_console/studio/~org/automations; screenshot: header shows only the Automations pillar, no Create app; empty rail (0) before any package-less flow; after authoring package-less flows the rail lists only qa_org_pl / qa_org_pl2 (TASK 1→2) — none of the 32 showcase flows and not the clone (which landed in com.example.showcase.custom); /_console/studio/~org/data landed on /_console/studio/~org/automations
    studio-authoring.org-scope-flows · r2 A1 partial pass browser-live + HTTP-live · opened qa_org_pl: Enabled switch live (aria-checked false, not disabled), node config inputs editable; Label fill 'Notify Assignee QA-EDITED' → autosave PUT /api/v1/meta/flow/qa_org_pl?mode=draft&surface=studio 200 (no ?package=); GET /meta/_drafts?type=flow → {name:'qa_org_pl', packageId:null}
    studio-authoring.org-scope-flows · r2 A2 partial pass network + HTTP-live · Publish → sheet 'releases the 1 pending draft on its own' → Publish all → POST /api/v1/meta/flow/qa_org_pl/publish 200 only, no /packages/…/publish-drafts; live GET nodes[1].label 'Notify Assignee QA-EDITED'; second run with 2 package-less drafts: POST …/qa_org_pl/publish 200 + POST …/qa_org_pl2/publish 200, no batch door, toast 'Published 2 flows — QA package-less flow, QA package-less flow 2' (1-flow toast names the flow: 'Published flow "QA package-less flow"')
    studio-authoring.org-scope-flows · r2 N0 partial pass browser-live + network · deep link ?surface=flow:showcase_urgent_task_alert in ~org: no flow opened, 'The link names flow “showcase_urgent_task_alert”, which is not here.' (same for qa_org_clone); package-less drafts went through single-item publish only
    studio-authoring.permission-matrix-editor-ux · r7 A0 partial pass browser-live · writable set qa_r6_matrix (PUT ?package=com.objectstack.qamention.single, grants showcase_account R+U) opened at /_console/apps/setup/metadata/permission/qa_r6_matrix (lands /_console/studio/~all/...); showcase_account expanded: counter 20 / 20; filter "re" -> counter 6 / 20 and only annual_revenue, churn_reason, created_at, created_by, csat_score, sales_region checkboxes in the DOM; "tax" -> 1 / 20, "revenue" -> 1 / 20
    studio-authoring.permission-matrix-editor-ux · r7 A1 partial pass browser-live · filter "re" -> Write all; filter cleared -> all 14 hidden fields' read/edit states identical to before (diff empty); same for Read-only all on "tax" and Clear on "revenue" / "tax" and Read-only all on "name" (each diff of the non-visible fields empty)
    studio-authoring.permission-matrix-editor-ux · r7 A2 skipped pass HTTP-live · Save -> PUT /api/v1/meta/permission/qa_r6_matrix 200; GET fields map round 1 = {"showcase_account.tax_id":{readable:true,editable:false}} (subset of visible set ["tax_id"]; Write all over the 6 "re" fields wrote no keys because R+U equals the object grant), round 2 = {"showcase_account.name":{readable:true,editable:false}} (visible set ["name"]) - no filter-hidden field ever appeared
    studio-authoring.permission-matrix-editor-ux · r7 A3 partial pass HTTP-live · round 2: filter "tax" -> Clear -> tax_id boxes revert to data-field-rule=inherited; Save 200; GET fields map no longer carries showcase_account.tax_id (only .name)
    studio-authoring.permission-matrix-editor-ux · r7 A4 skipped pass browser-live · viewport 820px: matrix container data-testid perm-matrix-pane (overflow-auto) scrollWidth 1304 > clientWidth 820; scrollLeft set -> 484; screenshot shows the full Bulk column R / CRUD / All / None on every row
    studio-authoring.permission-matrix-editor-ux · r7 A5 partial pass browser-live · showcase_contributor: metadata admin door -> no Save, 976 checkboxes 0 enabled, field bulk buttons absent (0/0/0), row bulk on hover R/CRUD/All/None all disabled, badge "Read-only (this set is provided by a code package)" (artifact gate); Studio Access /_console/studio/com.example.showcase/access -> no Save, 232 checkboxes 0 enabled, field bulk absent, row bulk disabled, "Read-only package - switch to or create a writable package to edit." (package gate). Wall and type gates not reachable on stock
    studio-authoring.permission-matrix-editor-ux · r7 N0 partial pass HTTP-live · saved fields map carries only keys of fields visible at bulk time (tax_id, then name); hidden fields never written
    studio-authoring.permission-matrix-editor-ux · r7 N1 skipped pass browser-live · at 820px the Bulk column is reachable by horizontal scroll (scrollWidth 1304 / clientWidth 820, scrolled 484), not clipped
    studio-authoring.record-page-roundtrip · r11 A0 skipped pass browser-live · Create form (Label 'Invoice Page R3a' -> Name invoice_page_r3a, Page type record [Kind flipped to slotted on the type change], Kind set to full, Object showcase_contact), Save -> captured PUT /api/v1/meta/page/invoice_page_r3a?mode=draft 200 body {type:'record',kind:'full',object:'showcase_contact',regions:[{name:'main',components:[page:header, record:highlights{fields:[owner_id,phone,email,account,...]}, record:path, page:tabs{details/activity}]}]}.
    studio-authoring.record-page-roundtrip · r11 A1 pass pass pins · cited from #28965: browser-live: 'Add block opens a block-type picker with configurable block kinds, and the added block is stored' (studio-editor.spec.ts, 2/2 green on preview) asserts the dialog offers Card, Section and Record details on an editable page copy.
    studio-authoring.record-page-roundtrip · r11 A2 partial pass HTTP-live · Editor (Basics/Data Context/Advanced panels rendered): Add block -> Card (PUT ?mode=draft 200), Title 'R3 Authored Card' (PUT 200), Publish -> POST /api/v1/meta/page/invoice_page_r3a/publish 200; GET /api/v1/meta/page/invoice_page_r3a (published) regions[main] = [page:header, record:highlights, record:path, page:tabs, page:card(title 'R3 Authored Card')].
    studio-authoring.record-page-roundtrip · r11 A3 skipped pass browser-live · Fresh browser context signed in through the form, /_console/apps/showcase_app/showcase_contact/record/-HIS6g_V394ow6xo (Prospect 05): screenshot shows the authored page - highlights, stage path, Details/Activity tabs and the added bordered card 'R3 Authored Card' below Work/Status. Viewer = the admin (personas[1]: no stock non-admin persona reads showcase_contact).
    studio-authoring.record-page-roundtrip · r11 N0 skipped pass browser-live · Explicit Kind=full create: draft regions non-empty (4 blocks incl. record:highlights), and the editor canvas opened with the copied default layout (screenshot), not a blank canvas.
    studio-authoring.save-door-runs-authoring-rules · r4 A0 pass pass pins · cited from #28965: test-oracle: all 8 named files ran green at the subject: metadata-protocol 10/10, objectql 29/29, spec 12/12, rest 14/14, lint 2/2 files passed (pins/SUMMARY); each file is in its batch with cases.
    studio-authoring.save-door-runs-authoring-rules · r4 A1 partial pass HTTP-live · page titel → 422 INVALID_METADATA path pages[0].regions[0].components[0].properties.titel rule component-props-unknown-key; slots.tabs → 422 INVALID_METADATA path slots.tabs code invalid_type (no rule); 2nd isDefault record page for showcase_task → 422 DEFAULT_RECORD_PAGE_CONFLICT naming qa_default2_page and showcase_task_detail; chart view w/o chart block → 422 path chart code custom; object (option visibleWhen reads parent + validation sqrt(record.amount)) → 422 with 2 issues rule expression-invalid, paths objects.qa_pred_obj.validations[0].condition and "object 'qa_pred_obj' · field 'tier' option 'gold' visibleWhen"; protection → 400 VALIDATION_ERROR path protection; _lock:'full …
    studio-authoring.save-door-runs-authoring-rules · r4 A2 partial pass HTTP-live · 422 body message: 'titel is not a prop page:header declares … Did you mean titel → title?'
    studio-authoring.save-door-runs-authoring-rules · r4 A3 partial pass HTTP-live · PUT /meta/object/qa_pred_obj?mode=draft (same bad predicates) → 200 state draft; POST /meta/object/qa_pred_obj/publish → 422 INVALID_METADATA, same 2 expression-invalid issues; active GET 404 after
    studio-authoring.save-door-runs-authoring-rules · r4 N0 partial pass HTTP-live · every refused body read back absent: GET page qa_props_page/qa_tabs_page/qa_default2_page, view qa_chart_view, object qa_pred_obj (active), action qa_bad_url → 404; protection create → 404; _lock on existing view → stored row unchanged
    studio-authoring.validation-rules-authoring · r3 A0 partial pass browser-live · Screenshot of the New menu on rthree_widget: COMMON RULES 'End date on or after start date', 'Number can't be negative', 'Reject the save when...', then collapsed 'Advanced' listing Condition (script), Compare fields (cross_field), State machine, Format, JSON schema, Conditional. End-date and Not-negative presets each sent one PUT ?mode=draft 200 at once (cross_field on start_date/end_date; script on amount); 'Reject the save when...' sent no PUT, showed 'Not saved - needs a condition', and focus landed on the condition group (role=group); typing a condition sent the PUT (200).
    studio-authoring.validation-rules-authoring · r3 A1 skipped pass browser-live · Expression mode, typed "record.name == 'blocked' &&": inline 'Unexpected token: EOF' with caret and held line 'an expression on the rule ... has an error to fix'; 0 object PUTs in 4 s; completing it to "... && record.amount != null" cleared the error and sent exactly one PUT, answered 200, carrying the fixed condition.
    studio-authoring.validation-rules-authoring · r3 A2 partial pass HTTP-live · All object PUTs in the session answered 200; after Publish all (publish-drafts 200) GET /api/v1/meta/object/rthree_widget validations[] is byte-identical (sorted JSON diff) to the last authored PUT: 8 rules - cross_field(end_date+start_date), script x3 (amount, name=blocked [warning], stamp=='OFF' [active:false]), state_machine(status, open->[in_progress]), format(code, regex ^[A-Z]{2}[0-9]{4}$), json_schema(payload, qty:number), conditional(when status=='closed', then script stamp empty) - each with its message, severity and active flag.
    studio-authoring.validation-rules-authoring · r3 A3 skipped pass HTTP-live · POST/PATCH /api/v1/data/rthree_widget: cross_field 400 VALIDATION_FAILED field end_date rule_violation; script(amount -1) 400 _record rule_violation; format(code bad1) 400 code invalid_format; json_schema(payload {qty:'x'}) 400 payload json_schema_violation; conditional(status closed, stamp '') 400 _record rule_violation; state_machine PATCH open->closed 400 status invalid_transition {from:open,to:closed}, read-back status=open; each violating tag stored=0; every twin 201 and open->in_progress PATCH 200.
    studio-authoring.validation-rules-authoring · r3 A4 skipped pass HTTP-live · Warning rule: POST name='blocked' -> 201 with warnings [{rule:name_blocked_is_not_allowed,severity:warning,code:rule_violation}]; inactive rule (active:false, stamp=='OFF'): POST stamp='OFF' -> 201.
    studio-authoring.validation-rules-authoring · r3 N0 skipped pass HTTP-live · Every rule saved in Studio and published enforced at the data write door as authored (A3/A4 lines); none saved-but-inert.
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB

    Close-out (RUNNER extraction obligation, checklist-test §5). Every FAIL has a disposition, and each probe finding that passed the filing gate is extracted with a QA-source: line.

    Extracted: #29173 · records-forms.list-view-capabilities · A8
    Extracted: #29174 · records-forms.adhoc-filter-sort-builder · A3, N2
    Extracted: #29175 · records-forms.list-view-capabilities · observed beside the clauses
    Extracted: #29176 · records-forms.field-unique-enforcement · observed beside the clauses
    Extracted: #29177 · records-forms.field-type-matrix · observed beside the clauses
    Extracted: #29179 · studio-authoring.metadata-diagnostics-sweep · observed beside the clauses
    Extracted: #29180 · studio-authoring.meta-save-preconditions-and-draft-rows · observed beside the clauses
    Extracted: #29181 · studio-authoring.actions-tab-presets · observed beside the clauses
    Extracted: #29182 · studio-authoring.record-page-roundtrip · observed beside the clauses
    Parked-on: #27377 · checklist-accuracy findings, fixture gaps and the partial gaps of records-forms.field-unique-enforcement and records-forms.gantt-interactions (anchor comment 6573003409)

    Dispositions: R-1 and R-2 are product defects. R-1 is a regression against wave 3: it came when the console started routing api actions through the action door (#27611). A8's step text also names the old direct URL, which is an accuracy line on the anchor. The two PARTIALs are fixture gaps, not defects.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions