Skip to content

QA run · release-18.3.0-w5-studio (38/38) · 589049b0 · 2026-11-03 · 37 PASS / 0 PARTIAL / 1 FAIL / 0 BLOCKED / 0 NOT-RUN #29893

Open

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3
Part of #27377 (18.3.0 sweep anchor; wave 5, card W5-STUDIO)

QA run record for the 18.3.0 pre-cut re-check, wave 5 card W5-STUDIO: the studio-authoring area minus the seven items the W5-FAIL card re-judges and the P0 card's first-run-loop, 38 items, plus one targeted off-clause check of the zh-CN Studio save (#29790, breaking). Every clause whose oracle is a live door was driven live on a booted stack (HTTP, the boot's own database, logs, and Playwright Chromium against the Console built from the subject); each item's automated.ref was run from this checkout at the subject and cited only as corroboration. 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 chose to dispatch all four wave-5 cards on 2026-11-03 08:21Z.

Result: 38/38 judged · 37 PASS / 0 PARTIAL / 1 FAIL / 0 BLOCKED / 0 NOT-RUN. No item is planned. Clause rows: 240. Three are test-oracle clauses scored on their pins (acceptance[0] of meta-save-preconditions-and-draft-rows, multi-package-item-rows and save-door-runs-authoring-rules, each naming its layer); the other 237 were driven live. 239 pass, 1 fails (studio-authoring.validation-rules-authoring acceptance[1]). The zh-CN save check passes all five of its clauses. No security finding.

Environment fingerprint

  • Framework: objectstack-ai/objectstack @ 589049b023d82c94508bf9fce752ea19069279e5 (main, the merge of fix(metadata-protocol)!: an object saved back from a translated /meta read stores its source labels, and one the registry would refuse is refused at save and at publish #29790, 2026-11-03 07:44Z), checked out detached. It never followed main or the Version PR chore: version packages #26517. ui/ is the same commit.
  • CI at the subject: push run 41600797636 (CI) read again before posting: status: completed, conclusion: success (updated 08:27:38Z).
  • Build: pnpm install --frozen-lockfile (37 s), pnpm exec turbo run build --concurrency=2 (119/119 tasks, exit 0, 23m04s), pnpm console:build --concurrency=2 (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 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_HOME=RUN/home OS_STORAGE_LOCAL_ROOT=RUN/uploads setsid node packages/cli/bin/run.js dev --ui --seed-admin --no-watch -p PORT -d file:RUN/data.db. A canary boot answered /api/v1/health 200 after 18 s, served /_console/ 200 and signed the seeded admin in before any runner started.
  • Other boots, each on its own port and file DB: OS_PAGE_REACT_ENABLED=false (custom-page-source-tiers, the react gate's off side); OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1 (flow-status-matches-runtime, the schedule half: banner 41 flow(s) 23 bound); a restart on the same DB (picklist-bound-select, api-written-keys); a second fresh boot for the zh-CN check; one orchestrator boot that re-measured three of the probe findings below before they were filed.
  • Ports 42000–42090, a block of 10 per runner. Seed: --seed-admin (admin@objectos.ai), the stock showcase seed and demo personas. Members were minted by admin create-user or invite then sign-up, and each persona's identity was proved with GET /api/v1/auth/get-session before its cells. zh-CN: a Playwright context with locale: 'zh-CN' (the Console's /meta reads follow Accept-Language, filled from <html lang>).
  • Execution: 8 runner subagents (opus, effort high), each on its own boots, with at most 3 Chromium instances on the box at once (a shared slot lock). The orchestrator re-derived the one FAIL against QA run · studio-authoring-42-plus-2 (44/44) · 3482f3d8 · 2026-11-03 · 33 PASS / 3 PARTIAL / 7 FAIL / 1 BLOCKED / 0 NOT-RUN #29672's reproduction (it had the runner drive that exact shape on two fresh loads) and re-measured three of the seven probe findings itself over the API on its own boot (the report filter, the validation type mismatch and the base export); one more (the publish sheet's hook count) it confirmed in the source.
  • Hygiene: after the run, git status is clean, examples/app-showcase/.objectstack does not exist, and no listener remains on any run port. No item needed install-local.

Scope

Selector release-18.3.0-w5-studio: the item list of card W5-STUDIO in the wave-5 brief, computed from the ledger at main 55dca466, whose ledger equals the subject's. Every revision below was re-read at the subject and matches. No planned items.

studio-authoring.object-designer-roundtrip r12 · studio-authoring.view-authoring-live r5 · studio-authoring.record-page-roundtrip r14 · studio-authoring.authoring-validation-not-persisted r6 · studio-authoring.org-override-registry-gate r9 · studio-authoring.permission-matrix-editor-ux r10 · studio-authoring.custom-page-render-and-blocks r2 · studio-authoring.page-variables-and-actions r2 · studio-authoring.custom-page-source-tiers r5 · studio-authoring.metadata-diagnostics-sweep r10 · studio-authoring.doc-on-app-menu r9 · studio-authoring.picklist-bound-select r4 · studio-authoring.meta-save-preconditions-and-draft-rows r2 · studio-authoring.multi-package-item-rows r4 · studio-authoring.save-door-runs-authoring-rules r4 · studio-authoring.held-edit-leave-guard r5 · studio-authoring.held-edit-publish-refusal r3 · studio-authoring.automations-rail-and-new-flow r2 · studio-authoring.flow-edge-type-authoring r4 · studio-authoring.org-scope-flows r3 · studio-authoring.flow-status-matches-runtime r2 · studio-authoring.validation-rules-authoring r3 · studio-authoring.hooks-tab-authoring r3 · studio-authoring.actions-tab-presets r3 · studio-authoring.object-api-tab-curl r2 · studio-authoring.summary-field-authoring r5 · studio-authoring.api-written-keys-survive-studio-save r4 · studio-authoring.interfaces-create-dialogs r2 · studio-authoring.report-authoring-options r3 · studio-authoring.app-and-nav-properties r5 · studio-authoring.authored-page-slots-and-variables r3 · studio-authoring.access-facets-runtime r4 · studio-authoring.access-owd-and-suggestions r5 · studio-authoring.removed-key-save-door-refusal r4 · studio-authoring.uninstalled-package-refused r3 · studio-authoring.package-customize-restore-display r2 · studio-authoring.package-extension-field-c r2 · studio-authoring.package-customization-counts-and-marks r2

Plus the off-clause targeted check zh-CN Studio save (#29790), recorded below as offclause.zh-cn-studio-save-29790 with clauses Z1–Z5. It scores no item and is not in the title's counts.

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

Derived item verdicts

37 PASS / 0 PARTIAL / 1 FAIL / 0 BLOCKED / 0 NOT-RUN (38 of 38 judged).

zh-CN Studio save check (#29790) — off-clause, PASS 5 of 5

On a showcase boot (it declares en and zh-CN), in a runtime package whose object carries published zh-CN translations, as admin in a zh-CN browser context:

Delta against wave 4 (#29172 W4-L3, #29002 W4-C3b1, #28942 W4-C3a, #28965 W4-C2b pins; subject 7fa9873f)

item wave 4 here why
studio-authoring.validation-rules-authoring PASS (#29172) FAIL A1: wave 4 drove only the parse-error shape. The shape that fails is a condition that parses but the server refuses (record.qty > 1000 on a nullable number): Studio autosaves it 200 and does not hold. #29672 (at 3482f3d8, 00:53Z today) measured it FAIL first and parked it on #27482. Not a regression of the subject.
7 items new to the set: custom-page-render-and-blocks, page-variables-and-actions, custom-page-source-tiers, picklist-bound-select, package-customize-restore-display, package-extension-field-c, package-customization-counts-and-marks not run PASS first wave-run; picklist-bound-select was blocked(fixture) at #26715 and #29672 and is driven here with the picklist declared in a scratch copy
the other 30 PASS PASS unchanged. Revisions moved since wave 4 on seven of them, each judged at the new revision: api-written-keys-survive-studio-save r3→r4, authoring-validation-not-persisted r5→r6, object-designer-roundtrip r10→r12, org-override-registry-gate r7→r9, org-scope-flows r2→r3, permission-matrix-editor-ux r7→r10, record-page-roundtrip r11→r14

No PASS→FAIL caused by a subject change, no FAIL→PASS (wave 4 had no FAIL in this set), no PARTIAL→PASS (none was partial).

Against the most recent Studio record, #29672 (at 3482f3d8, 2026-11-03): record-page-roundtrip FAIL→PASS (re-pointed to r14 by #29679), api-written-keys-survive-studio-save FAIL→PASS (r4), picklist-bound-select blocked→PASS, held-edit-leave-guard, custom-page-source-tiers and flow-status-matches-runtime partial→PASS (driven in headless Chromium and with the scheduled-work boot), validation-rules-authoring FAIL→FAIL (same clause, same card).

The Studio changes the brief named were met as shipped: #29784 / #29841 one word per state ("Switched on" / "Switched off"; zh matrix columns 对象 … 批量), #29843 ("Saved to ObjectStack Showcase’s customizations"), #29847 (the ~org rail lists no packaged or .custom flow, greyed or not). Wording that clauses still quote in the older form is listed for the anchor, not scored.

Reproduction rule for every fail

R-1 · studio-authoring.validation-rules-authoring · acceptance[1] · product defect, existing card #27482

  1. Stock showcase boot; form sign-in as admin@objectos.ai. In a writable runtime package, publish an object OBJ with a nullable number field qty (no required, no defaultValue).
  2. Studio → OBJ → Validations → New → "Reject the save when…" → Expression; type record.qty > 1000 and blur.
  3. Expected (clause): an error at the input and no object PUT while it stands; the fix sends one PUT, 200.
  4. Actual (2 of 2 fresh loads, objects gtwo_qty and gtwo_qty2): PUT /api/v1/meta/object/OBJ?mode=draft 200, then the red cel-server-error "applies > to record.qty, which 'OBJ' declares as nullable…", aria-invalid=true, no held line, status "Saved". Editing the message, then the condition (> 10000), each sends another PUT 200 carrying the refused condition. POST /api/v1/packages/PKG/publish-drafts → 200 outcome: refused, INVALID_METADATA expression-invalid at objects.OBJ.validations[0].condition.
  5. In load 2, adding a second rule raised the pillar line "Not saved yet: an expression on the rule … has an error to fix" and held the object, so the hold engages only after the author leaves the offending rule.
    Self-check: ref-targeted fills with a real blur; the same shape reproduces QA run · studio-authoring-42-plus-2 (44/44) · 3482f3d8 · 2026-11-03 · 33 PASS / 3 PARTIAL / 7 FAIL / 1 BLOCKED / 0 NOT-RUN #29672's reading at an earlier subject. The parse-error half of the clause holds (… && → inline "Unexpected token: EOF", 0 PUTs, the fix sends one PUT 200).

Off-clause probes (rule 8)

About 95 runner-minutes in all (9–15 per runner), after the clauses had verdicts, as admin and as members without manage_metadata. Doors: the object / view / report / dashboard / page / hook / flow / dataset save doors (draft and live), publish-drafts and single-item publish, _drafts, /meta/diagnostics, ensure-customization, /customizations, /packages/:id/export, the clone door, _migrate-stored, the action door, the public contact form, Studio's publish sheet, inspectors and Source tab. Boundary classes: the empty string and whitespace, unknown and mismatched names, a missing id, an empty ?package=, garbage or empty If-Match, bodies the registry or the lint gate would refuse, the opposite severity (warning twin), readers outside the asserted scope (members, a second persona). Seven product findings passed the filing gate and are extracted in the close-out; the orchestrator re-measured three of them itself and confirmed a fourth in the source. No security finding. Not filed (observations, intended or unconfirmed): an empty ?package= saves package-less (200); If-Match on an item with no row of that lifecycle answers 409 "…(current is null)"; a lookup with reference:'nope_object' saves and publishes (not checked against a contract); os migrate meta --stored repairs stored injected-column echoes but not an author field's stored echo (inherent: the row is its own only source); turning a field required in Studio on an object with zero rows answered 409 DESTRUCTIVE_CHANGE (not chased); a label-only base-field patch counts nowhere in /customizations.

Fixture gaps

None open. picklist-bound-select needs a package picklist the stock showcase does not declare; this run added showcase_tier to a scratch copy (the item's fixtures.requires[0]), so the stock-tree gap stands for the next runner.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3

    Per-clause verdicts, part 1 of 3

    One row per acceptance (A) and negative (N) clause. Layer names what the evidence reached: HTTP-live (a request to the booted server), browser-live (Playwright Chromium on the Console built from the subject, screenshot first, then text), pin-jsdom / pin-unit (a test run from this checkout at the subject, cited only as corroboration). Evidence is text from the oracle; nothing here is a screenshot.

    studio-authoring.object-designer-roundtrip · r12 · PASS (runner G1)

    clause verdict layer evidence
    A0 pass HTTP-live Studio Data pillar door, com.qa.gone. After Studio Publish->Publish all (POST /packages/com.qa.gone/publish-drafts 200 published 1) GET /meta/object/gone_repair_asset authored order name,asset_code,condition(good=Good,worn=Worn,broken=Broken); after option rename Worn->Used + dnd-kit pointer drag of Condition onto Owner (autosave PUT 200) and publish 2: owner_id,condition(good=Good,worn=Used,broken=Broken),name,asset_code.
    A1 pass browser-live + HTTP-live Add field popover: label 'Asset Code' typed + Tab -> preview 'API name: asset_code'; inspector field-apiname-input value asset_code. Records tab Add record -> POST /data/gone_repair_asset {name:'Pump 7',asset_code:'AC-007'} 201; GET /data/gone_repair_asset/NbPkVJluCkxvdH5W carries asset_code:'AC-007', no field_N key.
    A2 pass browser-live + HTTP-live New object dialog (screenshot confirmed): Label 'Repair Asset' then identifier cleared and typed char-by-char (80 ms/key) -> input value 'repair_asset', create-item-stored-name 'Saved as gone_repair_asset', re-checked before Save as draft; PUT /meta/object/gone_repair_asset?mode=draft 200 body name gone_repair_asset; meta read item.name gone_repair_asset.
    A3 pass browser-live + HTTP-live With 3 real options Good/Worn/Broken present, Add value added an empty 4th row; after 4.5 s: 0 [data-testid=metadata-validation-banner], 0 'System identifier must be' text, no 'Not saved yet' line (screenshot: no banner); published meta read condition.options = exactly good/worn/broken.
    N0 pass browser-live + HTTP-live API name derived to asset_code on label blur (not field_N) and the stored record column key is asset_code (A1). Console built from this tree (stale-bundle trap n/a).

    Notes: Before any option existed the select-needs-options hold line showed ('Not saved yet: the field "Condition" needs at least one option') - the legitimate banner steps[3] warns about, not a FAIL. automated.ref is ui/e2e/live only; not run.

    studio-authoring.view-authoring-live · r5 · PASS (runner G3)

    clause verdict layer evidence
    A0 pass HTTP-live PUT /api/v1/meta/view/gthreev_asset_views?mode=draft&package=com.qa.gthreev {object:gthreev_asset, list grid [serial,name,status], form 2 sections} -> 200 {success:true, version:'hmac-sha256:f670296f…', seq:13, state:'draft'}
    A1 pass browser-live after the draft, list (screenshot confirmed) headers [Name,Serial,Status,Cost,Notes,Actions] = pre-draft baseline; New/Edit dialogs show the flat default field list; GET /meta/view/gthreev_asset_views -> 404 RESOURCE_NOT_FOUND; GET /meta/view?object=gthreev_asset -> 200 items:[]
    A2 pass browser-live POST …/gthreev_asset_views/publish -> 200 seq 14; client reload only: list headers [Serial,Name,Status,Actions]; New dialog 'Create GV Asset / QA Identity Block / Serial / Name / QA Money Block / Cost' (screenshot: two titled sections, Serial+Name side by side); row-menu Edit dialog same sections; boot.log one 'Server is ready', --no-watch, no restart line
    A3 pass HTTP-live GET /api/v1/meta/view?object=gthreev_asset -> 200 items gthreev_asset.default (viewKind list, columns [serial,name,status]) and gthreev_asset.form (viewKind form, sections QA Identity Block / QA Money Block), _packageId com.qa.gthreev; container name appears 0 times (by design)
    A4 pass browser-live re-draft columns [status,name,cost,serial] -> 200 seq 15; list before re-publish still [Serial,Name,Status,Actions]; publish -> 200 seq 16; reload -> headers [Status,Name,Cost,Serial,Actions] (screenshot confirms reorder + added Cost)
    N0 pass browser-live both publishes appeared on client reload with the same server process; boot.log carries exactly one 'Server is ready' across 08:58-09:02Z
    N1 pass browser-live neither the first draft nor the re-draft leaked: list headers unchanged in both pre-publish reads; object door items:[] before first publish

    Notes: End-user surfaces scored as the admin (personas[1]: no grant on the runtime object). Surface /_console/apps/showcase_app/gthreev_asset (redirects to /apps/com.example.showcase/gthreev_asset).

    studio-authoring.record-page-roundtrip · r14 · PASS (runner G3)

    clause verdict layer evidence
    A0 pass browser-live /_console/studio/~all/metadata/page/new: Label 'Invoice Page G3a' -> Name invoice_page_g3a; Page type record (Kind flipped to slotted), Kind set full, Object showcase_account; Save -> PUT /api/v1/meta/page/invoice_page_g3a?mode=draft (If-None-Match *) 200 body {type:'record',kind:'full',object:'showcase_account',regions:[main:[page:header, record:highlights{fields:[status,industry,annual_revenue]}, record:path, page:tabs]]}
    A1 pass browser-live editor /_console/studio/~all/metadata/page/invoice_page_g3a (screenshot: seeded canvas + Basics, Data Context, Advanced panels); Add block picker lists Card (page:card), Section (page:section), Record details (record:details) among DATA/LAYOUT/RECORD CONTEXT/ELEMENTS/NAVIGATION/AI. Pin ui/e2e/live/studio-editor.spec.ts not run (live spec, per brief); driven by hand
    A2 pass HTTP-live picker Card -> PUT ?mode=draft 200 (autosave), Title 'G3 Authored Card' -> PUT 200, editor Publish -> POST /api/v1/meta/page/invoice_page_g3a/publish 200 seq 20; GET /api/v1/meta/page/invoice_page_g3a -> 200 regions main [page:header, record:highlights, record:path, page:tabs, page:card(title 'G3 Authored Card')]
    A3 pass browser-live /_console/apps/showcase_app/showcase_account/record/-4398H2RcVDByZo4 (Wayne Enterprises), member phone.demo@example.com (session proven via get-session) and admin each: screenshot shows highlights strip, stage path, Details/Activity tabs and the bordered card 'G3 Authored Card'; 0 role=alert, 0 pageerrors
    N0 pass browser-live explicit Kind=full create: draft regions non-empty (4 blocks incl. record:highlights) and the editor opened on the copied default layout (screenshot), not a blank canvas

    Notes: Member leg driven for the first time (personas[1] said no run had): the everyone baseline lets phone.demo read showcase_account and the authored page is served to it. The Save draft step is an autosave: the Add-block pick and the title edit each PUT the draft, so the toolbar Save stays disabled.

    studio-authoring.authoring-validation-not-persisted · r6 · PASS (runner G2)

    clause verdict layer evidence
    A0 pass HTTP-live PUT /meta/object/qa_invalid_probe?mode=draft (amount label only) -> 422 {error:'object/qa_invalid_probe failed spec validation: 1 issue — fields.amount.type [invalid_value]',code:INVALID_METADATA,issues:[{path:'fields.amount.type',...}]}
    A1 pass HTTP-live container-shaped body bound to gtwo_bare (GET /meta/view?object=gtwo_bare items []): PUT /meta/view/qa_invalid_views -> 422 INVALID_METADATA, issue 'Unrecognized key(s) on this view container: type, columns. • type belongs to a single VIEW, not to the container. Wrap it: defineView({ list: { type, data, columns, … } })...'; flat body answered the names-no-object/viewKind refusal
    A2 pass HTTP-live object: GET qa_invalid_probe 404 RESOURCE_NOT_FOUND, ?state=draft 404 NO_DRAFT; view: GET qa_invalid_views 404, ?state=draft 404 NO_DRAFT, /meta/view?object=gtwo_bare items []; after the browser 422, gtwo_bare ?state=draft 404 NO_DRAFT and live code field {type:text,label:Code}
    A3 pass browser-live /_console/apps/setup/metadata/object/gtwo_bare redirected to /_console/studio/~all/metadata/object/gtwo_bare; Source tab, body with code missing type pasted -> autosave PUT ?mode=draft 422; screenshot: red banners 'Fields → code → type: Invalid option: expected one of ...' and 'This metadata does not match the spec — 1 validation error(s)', 'Unsaved' chip
    A4 pass HTTP-live corrected qa_invalid_probe (amount type number) -> 200 state:draft seq 11; corrected qa_invalid_views {object:gtwo_bare,list:{type:grid,columns:[name,code]}} -> 200 state:draft seq 12
    N0 pass HTTP-live no 2xx on any invalid draft; nothing invalid readable afterwards (A2)
    N1 pass browser-live the Source-tab refusal surfaced the located error banner (A3), not a silent dead save
    N2 pass browser-live note honoured: no _diagnostics banner met on these saves and none scored

    Notes: the editor's status chip keeps reading 'Auto-saving…' after the 422 (probe finding 1)

    studio-authoring.org-override-registry-gate · r9 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass HTTP-live registry re-read: object overlay false/create true, field false/false, job false/false, view+dashboard overlay true. PUT /meta/object/showcase_task (served body, label changed) → 403 NOT_OVERRIDABLE (sealed sentence); GET label still 'Task'
    A1 pass HTTP-live POST /packages/com.example.showcase/ensure-customization 201 created:true (2nd 200 created:false); PUT /meta/view/showcase_task.in_progress?package=com.example.showcase.custom +overrides{package,basedOn:'0.1.0'} → 200 'Saved customization overlay'; pkg-less GET label 'QA Overlaid In Progress' _packageId com.example.showcase. Dashboard variant same (showcase_ops_dashboard)
    A2 pass HTTP-live PUT /meta/job/qa_probe_job → 403 NOT_CREATABLE ('code-only … allowRuntimeCreate=false'); GET → 404. Field variant: PUT /meta/field/showcase_task.qa_newfield → 403 NOT_CREATABLE
    A3 pass HTTP-live same run: PUT /meta/object/qa_org_probe (new, sharingModel private) → 200 state active, GET 200 label 'QA Org Probe', beside showcase_task 403 NOT_OVERRIDABLE
    A4 pass HTTP-live DELETE /meta/view/showcase_task.in_progress?package=com.example.showcase.custom → 200 reset:true 'reset to artifact default'; GET label 'In Progress'; 2nd DELETE → 200 reset:false 'already at artifact default'. Dashboard: reset:true then 'Delivery Operations' served, then reset:false
    N0 pass HTTP-live no 200 on an artifact-backed overlay: object showcase_task 403 NOT_OVERRIDABLE, field showcase_task.title 403 NOT_OVERRIDABLE; label unchanged. Closed door recorded: pkg-less PUT view → 403 NOT_OVERRIDABLE naming com.example.showcase.custom

    Notes: Pin overlay-precedence.test.ts (objectql batch 6 files/101 tests passed, plugin-unit, corroboration only). The A0 refusal text is now the managed-item sealed sentence rather than the registry-flag sentence; code unchanged.

    studio-authoring.permission-matrix-editor-ux · r10 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass browser-live Studio Access set gsev_matrix (writable pkg com.qa.gsev), gsev_ticket expanded: counter 14 / 14; filter 're' -> '5 / 14' and only created_at, created_by, reference, region, revenue boxes in DOM; 'tax' -> 1 / 14 (tax_code); 'notes' -> 1 / 14
    A1 pass browser-live filter->bulk->clear filter, 4 rounds (Write all on 're', Read-only all on 'tax' and 'notes', Clear on 'tax'): hidden fields' readable/editable states diffed before vs after = [] every round
    A2 pass HTTP-live 3 autosave PUT /meta/permission/gsev_matrix?mode=draft&package=com.qa.gsev 200; draft fields map = {gsev_ticket.notes:{readable:true,editable:false}} (subset of the visible set at bulk time; Write all over 're' wrote no keys as R+U equals the object grant)
    A3 pass HTTP-live Clear on 'tax' -> tax_code boxes back to data-field-rule=inherited; saved fields map no longer carries gsev_ticket.tax_code (only .notes)
    A4 pass browser-live viewports 1000 and 820: perm-matrix-pane scrollWidth 1304 > clientWidth 760/580; Bulk header outside at scrollLeft 0, fully inside after scrollLeft 544/724; hovered row R/CRUD/All/None hit by elementFromPoint, enabled
    A5 pass browser-live showcase_contributor: Studio Access -> Save 0, 232 checkboxes 0 enabled, field panel expanded with Read-only all/Write all/Clear absent (0/0/0), hovered row bulk R/CRUD/All/None disabled, header 'Read-only package — switch to or create a writable package to edit.'; metadata admin door (~all and the Setup door, which lands there) -> Save 0, 968 checkboxes 0 enabled, bulk 0/0/0, badge 'Read-only (this set is provided by a code package)'. Wall and type gates not reachable on stock
    A6 pass browser-live panes 1000/820 px, scrollLeft 0/80/200/max: elementFromPoint at the centre of all 28 Read/Edit boxes returns the box and lies inside the container (28/28 each offset); all 14 field names answer at their first characters (14/14)
    N0 pass HTTP-live saved fields map carries only keys of fields visible at bulk time (notes); no filter-hidden field ever written
    N1 pass browser-live Bulk column reached by horizontal scroll at 820 and 1000 px, never clipped
    N2 pass browser-live no field box covered by the name cell or carried out of view at any offset (see A6)

    Notes: Pins: PermissionMatrixEditor.fieldBulk/readonly/artifactTier jsdom (component layer) green inside the 11-file ui run, 136/136 passed. #29841: zh-CN column heads read 对象 记录共享模型 创建 读取 编辑 删除 导出 转移 查看全部 修改全部 读取范围 写入范围 批量.

    studio-authoring.custom-page-render-and-blocks · r2 · PASS (runner G4)

    clause verdict layer evidence
    A0 pass browser-live screenshot then DOM on command-center / my-work / project-workspace / active-projects: 0 'Unknown component type', 0 'failed to render', 0 role=alert, 0 pageerrors; command-center drew 6 KPI tiles, 5 sized recharts SVGs and the work-queue table; my-work 3 metric tiles + grid + 2 page:cards; workspace parent form + Tasks child; active-projects list
    A1 pass browser-live + HTTP-live KPIs 活跃项目 2 / 待办 9 / 待复审 2 / 风险 1 / 客户 14 / 预算 $1,090,000.00 = data plane (status=active 2; status!=done 9 of 11; in_review 2; health=red 1; accounts total 14; budget sum 1,090,000); work-queue grid rows [Design system, Warehouse schema] = GET /data/showcase_task filter status=in_review (2 rows)
    A2 pass browser-live + HTTP-live 5 SVGs sized (368x350 x4, 788x350); 任务状态分布 5 bars heights 157/236/157/157/157 for Backlog/To Do/In Progress/In Review/Done = POST /analytics/dataset/query showcase_task_metrics[status] rows 2/3/2/2/2; priority bars 141/282/212/141 = Low2/Medium4/High3/Urgent2
    A3 pass browser-live + HTTP-live admin: grid GET /data/showcase_task filter [[owner_id,equals,Jg6l…(admin id)]] → 10 rows = API owner-filtered total 10; planted task 'QA foreign-owned task' (owner usr_showcase_auditor_demo) absent; Leadership View card rendered for admin
    A4 pass browser-live + HTTP-live active-projects: 4 rows Data Platform $600k, Mobile App $200k, Website Relaunch $150k, Compliance Audit $90k (budget desc); request filter status not_equals completed, sort=-budget; Legacy Sunset (completed) exists in data, hidden; Health/Status dropdowns present; Display popover Layout row offers Grid / Kanban
    A5 pass browser-live project-workspace: parent inputs Project Name/Account/Status/Health/Budget/End Date + 'Tasks' child table (Title/Status/Priority/Estimate/Done…) 'No items yet — click “Add task”', Add task button, 'Create Project + Tasks' submit
    A6 pass HTTP-live + browser-live PUT /meta/page/qa_page_probe?mode=draft 200, POST /meta/page/qa_page_probe/publish 200; rendered: intro text, missing-object grid → 'Error loading grid · Metadata not found for object: qa_nonexistent_object_zz', bogus block → role=alert 'Unknown component type: qa_bogus_block_type' + JSON; no blank region
    N0 pass browser-live no data-bound region rendered empty over seeded data; every grid/metric/chart carried rows or values matching the data plane
    N1 pass browser-live every declared block of the four pages found in the DOM (6 KPIs, 5 charts, queue grid; 3 metrics, divider, grid, 2 cards; header + master-detail; list with filters/sort/add)
    N2 pass browser-live no zero-height or single-bucket chart: bar 5 buckets, donut 3 health sectors, priority 4 buckets, area 3 months, budget 3 accounts x2 series, all h=350

    Notes: Console route /_console/apps/showcase_app/page/X redirects to /apps/com.example.showcase/page/X. A 'Set the default currency' modal covers the first page on a fresh org; dismissed with 'Not now' before each screenshot.

    studio-authoring.page-variables-and-actions · r2 · PASS (runner G4)

    clause verdict layer evidence
    A0 pass browser-live first render of showcase_page_variables: hint '↑ Select a project above…' present, '✓ Project selected' heading and gated body absent
    A1 pass browser-live testid record-picker-trigger → option 'Data Platform' (5 projects listed); same page load (window marker kept): hint gone, heading + body shown, trigger reads 'Data Platform'
    A2 pass browser-live DOM after screenshot: before {hint:true, heading:false, body:false} → after {hint:false, heading:true, body:true}
    A3 pass browser-live contact-form: ready_hint '✓ Looks good…' absent before; after filling #field_email it is present, same page load
    A4 pass network captured POST /api/v1/forms/contact-us/submit body {name:'QA Ada r1-…', email:'qa.r1-…@example.com', company:'QA Engines r1-…', message:'QA message r1-…'} = typed values, no unresolved double-brace page. token → 201 {id:f82C1HiE5n7kM-JD}; toast 'Thanks! We received your inquiry.'
    A5 pass HTTP-live GET /data/showcase_inquiry/f82C1HiE5n7kM-JD → name/email/company/message equal the typed values; inquiry total 3 → 4
    N0 pass browser-live neither the detail panel nor ready_hint visible before interaction
    N1 pass browser-live both toggles landed with no reload (window marker survived); reload of page-variables re-initialised empty: hint shown, detail absent
    N2 pass network submit body carried resolved values only
    N3 pass HTTP-live the 201 created a readable showcase_inquiry row with the submitted fields

    studio-authoring.custom-page-source-tiers · r5 · PASS (runner G4)

    clause verdict layer evidence
    A0 pass browser-live capability-map: domain cards (DATA 'Objects, fields & rules', UI …) with → links, no compile panel, 0 <script>; command-center-jsx: 4 KPI cards + Weekly Throughput + Recent Activity; crm-workbench: KPI strip + ListView of 5 projects + form pane
    A1 pass browser-live + HTTP-live command-center-jsx shows Open Tasks 128 / In Progress 47 / Completed 1,902 / Cycle Time 2.4d; GET /data/showcase_task total 11 (≠128); network trace for the page load held 0 /data or /analytics requests
    A2 pass browser-live + HTTP-live crm-workbench TOTAL PROJECTS 5 / ACTIVE 2 = GET /data/showcase_project total 5, status=active 2; network carried GET /api/v1/data/showcase_project?top=200 (the useAdapter find) besides the ListView reads
    A3 pass browser-live click on row 'Website Relaunch' → EDITING 'Website Relaunch', form inputs name=Website Relaunch budget=150000 spent=60000, Update button; '+ New project' → inputs empty (name='', budget=''), Create button
    A4 pass browser-live (two boots) on (42040): page executes as above; off (42041, OS_PAGE_REACT_ENABLED=false): 'React pages are disabled on this deployment' naming OS_PAGE_REACT_ENABLED=true, 0 showcase_project requests; capability-map and command-center-jsx still render on the off boot
    A5 pass HTTP-live + browser-live faulting html: active PUT ?package= → 422 INVALID_METADATA (jsx-forbidden-tag <qa_nosuch_tag_zz>, jsx-forbidden-attr onClick, jsx-unknown-component); per-item publish 422; publish-drafts outcome refused; live GET 404; draft in Studio /studio/com.qa.gfour/interfaces?surface=page:gfour_html_bad → 'HTML page failed to compile (3)' listing the 3 messages; react throw page → 'React page error · Error: QA react boom g4'
    N0 pass HTTP-live + browser-live html pages carry no <script>; save door refuses <script> (jsx-forbidden-tag), onClick (jsx-forbidden-attr) and {expr} children (jsx-expression-child); a saved href='javascript:…' rendered as <a> with no href, click raised no dialog
    N1 pass browser-live off boot: no execution (named notice, no data request), CSP script-src without 'unsafe-eval'
    N2 pass browser-live + HTTP-live Total KPI 5 beside a 5-row ListView, matches live count
    N3 pass browser-live both failure surfaces named (compile panel with 3 diagnostics; 'React page error' with message); neither blank

    Notes: The react error page was published from writable package com.qa.gfour (draft + POST /meta/page/gfour_react_bad/publish 200) and opened in its app.

    studio-authoring.metadata-diagnostics-sweep · r10 · PASS (runner G2)

    clause verdict layer evidence
    A0 pass HTTP-live GET /api/v1/meta/diagnostics 200 bare keys [entries,total,scannedTypes,scannedItems,stats]; total 0 = entries.length 0; scannedTypes 30 = DEFAULT_METADATA_TYPE_REGISTRY entries with getMetadataTypeSchema (30 of 32); scannedItems 428
    A1 pass HTTP-live object/view/flow/dashboard: stats.count = GET /meta/<type> length (122/36/33/4), stats.packages = sorted distinct _packageId (equal x4), stats.locked = count of /layers envelope lock!='none' (117/0/32/0, equal x4)
    A2 pass HTTP-live + browser-live ?package=com.example.showcase: object 122->24, page 35->29, dashboard 4->3, flow 33->32, scannedItems 428->252; scoped lists object/view/flow/dashboard 24/36/32/3 = stats; screenshot of /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, Capability 2, Permission 9, Position 10, Sharing Rule 2, API 3, Book 1 = scoped stats
    A3 pass HTTP-live ?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...'
    A4 pass HTTP-live clean store: severity=warning and =error both 200 total 0; with planted row both total 1 and entries JSON identical; no warning-only rows
    A5 pass browser-live screenshot /studio/com.example.showcase/metadata/_diagnostics: 'All clear' 'Scanned 252 item(s) across 30 type(s) — no issues found.' = scoped API 252/30 (gtwo scope 10/30 likewise); directory had no diagnostics link (0 a[href*=_diagnostics])
    A6 pass HTTP-live + browser-live planted: valid view gtwo_widget.g2_diag_view saved ?package=com.qa.gtwo, then sys_metadata columns[0].field=123 on own DB; sweep total 1 errors [('',Invalid input),(columns,Invalid input),(columns.0.field,'expected string, received number')]; gtwo directory View tile badge 1 + 'View all issues (1)'; diagnostics page groups under VIEW (1); row link /studio/com.qa.gtwo/metadata/view/gtwo_widget.g2_diag_view landed on /studio/com.qa.gtwo/interfaces?surface=view:gtwo_widget.g2_diag_view (view designer); row restored, sweep total 0
    A7 pass pin-plugin-unit (test oracle) packages/metadata-protocol protocol.diagnostics-store-outage.test.ts: 1 file, 10/10 passed at 589049b (protocol layer over stub store: outage rethrown 503, never total:0)
    N0 pass HTTP-live ?type=viewes answered 400 INVALID_REQUEST, not 200 {scannedTypes:1,stats:{}}
    N1 pass pin-plugin-unit (test oracle) same outage pin 10/10: unreadable store never answered as a clean sweep (outage is pin-only by the item's steps[7])
    N2 pass browser-live clean store rendered the designed All-clear state with counts (screenshot), not a blank region
    N3 pass HTTP-live recording guard honoured: every sweep read was the live REST server on :42020, no dispatcher call

    Notes: one re-read immediately after restoring the planted row still answered total 1; the next read answered 0 (read-after-write lag, not scored)

    studio-authoring.doc-on-app-menu · r9 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass HTTP-live baseline: books [showcase_manual public], 9 docs, 0 doc nav on showcase_app. Fixture com.qa.gsixd (docs published before books; books AAA Empty Book / gsixd_gated {permissionSet:showcase_auditor} / gsixd_org org; app gsixd_app 6 doc entries). reader nav [n_doc_g,n_book_g,n_bookdoc_g,n_doc_o,n_book_o,n_bookdoc_o]; outsider [n_doc_o,n_book_o,n_bookdoc_o]; outsider /meta/doc/gsixd_gated_doc & /book/gsixd_gated/tree 403 PERMISSION_DENIED, reader 200
    A1 pass browser-live after screenshot, rendered menu reader = [Gated Page, Gated Book Entry, Gated Page In Book, Open Page, Org Book Entry, Open Page Two In Book]; outsider = [Open Page, Org Book Entry, Open Page Two In Book]; 1:1 with each /meta/app (empty diff)
    A2 pass browser-live reader: 3 gated entries → /apps/com.qa.gsixd/docs/gsixd_gated/gsixd_gated_doc h1 'Gated Doc', book nav 'Gated Book · Main · Gated Doc' (= tree), not AAA Empty Book; open entries → …/gsixd_org/gsixd_open_doc(2), nav [Open Doc, Open Doc Two] = tree; flat /docs/gsixd_gated_doc → /docs/gsixd_gated/gsixd_gated_doc, /docs/gsixd_open_doc2 → /docs/gsixd_org/gsixd_open_doc2; outsider on org book: [Open Doc, Open Doc Two], no Gated Doc
    A3 pass browser-live outsider, screenshot each: /docs/gsixd_gated_doc, /docs/gsixd_gated, /docs/gsixd_gated/gsixd_gated_doc and the 3 under /apps/com.qa.gsixd/docs/… → lock + 'You do not have access to this documentation' + 'This documentation is limited to holders of a permission set you do not have' (server 403); control /docs/gsixd_nosuch_name (+app route) → 'Documentation not found … No book or document named gsixd_nosuch_name is installed.' (server 404)
    N0 pass HTTP-live + browser-live no gated doc entry drawn or served for the outsider (A0/A1)
    N1 pass browser-live no 'Documentation not found' on any of the 6 routes the server answers 403
    N2 pass browser-live all 8 direct routes render a heading and a reason; no blank region or crash
    N3 pass browser-live every page entry opened in its claiming book (A2)

    Notes: Teardown: DELETE app/3 books/3 docs ?package=com.qa.gsixd 200 each, assignment row deleted 200; corpus re-reads baseline (books [showcase_manual], 9 docs, no doc nav), reader GET gated doc 404. Personas g6reader/g6outsider via admin create-user mustChangePassword:false.

    studio-authoring.picklist-bound-select · r4 · PASS (runner G1)

    clause verdict layer evidence
    A0 pass HTTP-live (captured PUT) metadata admin door (Setup metadata URL redirects to /studio/~all/metadata/object/gfour_obj_a): label Notes->Notes Renamed autosave PUT /meta/object/gfour_obj_a?mode=draft 200; tier sent {type:select,label:Tier,picklist:showcase_tier} no options; every other field incl. inline select status byte-identical to the served /layers effective read.
    A1 pass browser-live (page text after screenshot) No 'cannot both be declared' text on load and after save at /studio/~all/metadata/object/gfour_obj_a, nor on the Studio Data pillar Form tab of gfour_obj_a (served tier carries picklist + resolved options).
    A2 pass browser-live gfour_obj_b.level inspector 'Options from' choices ["This field's own options","Service Tier (showcase_tier)"] = GET /meta/picklist items [showcase_tier 'Service Tier'].
    A3 pass HTTP-live (captured PUT) Choosing Service Tier -> PUT /meta/object/gfour_obj_b?mode=draft 200 with level {type:select,label:Level,picklist:showcase_tier} and no options key.
    A4 pass browser-live Bound tier inspector: Options from 'Service Tier (showcase_tier)'; 'Options come from the picklist "Service Tier". A field bound to a picklist has no options of its own...'; Bronze/Silver/Gold listed as text; 0 'Add value' buttons, 0 option-label inputs.
    A5 pass HTTP-live (captured PUT) Studio Data pillar gfour_obj_a Add field 'Extra Note' -> PUT ?mode=draft&package=com.qa.gfour&surface=studio 200; tier {type:select,label:Tier,picklist:showcase_tier} without options.
    A6 pass browser-live After Studio Publish all (publish-drafts 200 published 2), Records tab Add record form: gfour_obj_a tier options [Bronze,Silver,Gold,Platinum]; gfour_obj_b level options [Bronze,Silver,Gold,Platinum] (extension's Platinum included).
    A7 pass browser-live + network List /studio/~all/metadata/picklist: showcase_tier 'Service Tier', 0 New/Create buttons. Detail: label, name, 'Owning package com.example.showcase', Bronze/bronze..Gold/gold, Extensions 'Added by ObjectStack Showcase (com.example.showcase)' Platinum/platinum; all inputs/combos/Add row/Remove row disabled or readonly; no meta write (only a sys_user_preference PATCH).
    N0 pass HTTP-live All four PUTs of objects with a bound field answered 200 (A0, A3, A5 and the publish); none 422 INVALID_METADATA at fields.*.options.
    N1 pass browser-live Bound field inspector: no option rows, no 'Add value', 'Options from' is a closed select of served lists, no free-text name box (A4/A2).
    N2 pass browser-live Picklist list: no New; detail: no Save/Delete, Add row and Remove row disabled=true, no write request (A7).
    N3 pass browser-live (forced failure) page.route fulfilled GET /api/v1/meta/package with 500: Extensions section read 'The installed packages could not be read, so the extensions of this picklist are unknown. forced by runner' - not 'No package extends this picklist.'

    Notes: Fixture per fixtures.requires[0] made in the SCRATCH copy's objectstack.config.ts (not the shared tree); GET /meta/picklist/showcase_tier served name/label/options/_packageId com.example.showcase; GET /meta/object/gfour_obj_a/layers effective tier {picklist:showcase_tier, options bronze,silver,gold,platinum}. Step 4's 'Setup -> Metadata -> Object' is now /studio/~all/metadata/object/NAME (ADR-0146 redirect); the package-scoped /studio/PKG/metadata/object/NAME redirects to the Data pillar. 'New-record form' = Studio Records tab Add record dialog (objects are in no app nav).


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3

    Per-clause verdicts, part 2 of 3

    One row per acceptance (A) and negative (N) clause. Layer names what the evidence reached: HTTP-live (a request to the booted server), browser-live (Playwright Chromium on the Console built from the subject, screenshot first, then text), pin-jsdom / pin-unit (a test run from this checkout at the subject, cited only as corroboration). Evidence is text from the oracle; nothing here is a screenshot.

    studio-authoring.meta-save-preconditions-and-draft-rows · r2 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass pin (REST handler/dispatcher/engine units) all 5 files ran green: packages/rest batch 5 files/60 tests, packages/runtime batch 4/44, packages/objectql batch 6/101 (each named file in its batch, 0 failed)
    A1 pass HTTP-live PUT view showcase_task.qa_occ_view?mode=draft 200 v=9dcf…; GET ?state=draft serves version 9dcf… (plain read 404, no version); If-Match 9dcf → 200 v=a66f; stale If-Match 9dcf → 409 METADATA_CONFLICT currentVersion a66f…; INM:* on existing active → 409 METADATA_CONFLICT currentVersion; INM:"abc" → 400 VALIDATION_ERROR; IM+INM → 400 VALIDATION_ERROR; read-back active 'QA OCC View', draft 'QA OCC View v2' (refused bodies stored nowhere)
    A2 pass HTTP-live GET /meta/_drafts → {type:view,name:showcase_task.qa_occ_view,label:'QA OCC Draft Label',packageId:null,updatedBy:3fKq…} — draft body's label, not the machine name
    A3 pass HTTP-live GET …/qa_occ_view/history events seq7-10 actor 3fKqX72q… (= signed-in admin id); DB sys_metadata draft+active rows created_by=updated_by=that id

    Notes: scratch view deleted (active + ?state=draft).

    studio-authoring.multi-package-item-rows · r4 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass pin (REST handler/dispatcher/engine units) all 9 files ran green: rest batch 5/60, runtime 4/44, objectql 6/101, metadata-protocol 5/353 tests passed
    A1 pass HTTP-live com.qa.pkga/pkgb each PUT dashboard qa_shared_dash 200; pkg-less PUT → 409 RESOURCE_CONFLICT 'belongs to 2 packages … Nothing was written'; pkg-less DELETE 409; unscoped /layers 409; sys_metadata 2 rows unchanged; DELETE ?package=pkga 200 'row bound to com.qa.pkgb remains'; pkg-less PUT → 200 package=com.qa.pkgb; 1 row (pkgb, new label); /layers 200 effective 'QA Shared NEW'
    A2 pass HTTP-live PUT object pkga_qa_obj?package=com.qa.pkga 200; same ?package=com.qa.pkgb (+extra field) → 422 OBJECT_OVERLAY_PACKAGE_MISMATCH 'owned by package com.qa.pkga'; one row pkga label 'QA Obj A', GET fields lack 'extra'
    A3 pass HTTP-live PUT /meta/object/showcase_project (plain, INM:, ?mode=draft, draft+INM:) → 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." (4/4); hook too
    A4 pass HTTP-live pkga: draft pkga_dash1 → publish-drafts 200 outcome published count 1; draft edit + draft-only pkga_dash2; POST /packages/com.qa.pkga/revert 200 → _drafts empty, pkga_dash1 serves 'Dash1 published body', pkga_dash2 404 (active+draft NO_DRAFT); never-published com.qa.pkgc revert → 409 RESOURCE_CONFLICT 'has never been published', draft kept

    studio-authoring.save-door-runs-authoring-rules · r4 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass pin (protocol/objectql/spec/lint units + RestServer unit) all 8 files ran green: metadata-protocol batch 5/353, objectql 6/101, spec 1/19, rest 5/60, lint 2/38 tests passed
    A1 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 path slots.tabs code invalid_type; 2nd isDefault record page showcase_task → 422 DEFAULT_RECORD_PAGE_CONFLICT naming qa_default2_page + showcase_task_detail; chart view w/o chart → 422 path chart code custom; object (parent visibleWhen + sqrt) → 422 2× rule expression-invalid; protection → 400 VALIDATION_ERROR path protection; _lock → 400 path _lock; javascript: target → 422 path target custom
    A2 pass HTTP-live 422 issue message: 'titel is not a prop page:header declares … Did you mean titel → title?' hint 'Remove titel…'
    A3 pass HTTP-live PUT object qa_pred_obj?mode=draft → 200 state draft; POST /meta/object/qa_pred_obj/publish → 422 INVALID_METADATA same 2 expression-invalid issues; active GET 404 after (page titel draft→publish likewise 422)
    N0 pass HTTP-live every refused body read back absent: page qa_props_page/qa_tabs_page/qa_default2_page, view qa_chart_view (active+draft), object qa_pred_obj active, dashboard qa_prot_dash, action qa_bad_url → 404; _lock save left qa_lock_dash label 'QA Lock', layers lock none editable

    Notes: #29790 moved nothing here: a registry-refused object body (unknown field type) answers 422 INVALID_METADATA at fields.x.type; sealed showcase objects still 403 first.

    studio-authoring.held-edit-leave-guard · r5 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass browser-live (dom after screenshot) confirm 'You have unsaved changes. Leave this page anyway?' fired on Stay and Leave for every held/dirty in-app cell: picklist a/b/c/d, lookup a, step a/b, widget a, Access tick a/d (63-130 ms after edit), nav rename a/d; clean Data a/b/d, Automations a, Interfaces a/d, Access a/d → 0 dialogs
    A1 pass browser-live page.reload() raised a 'beforeunload' dialog on Stay and Leave for picklist, lookup, step, widget, Access tick, nav rename; clean reloads of Data/Automations/Interfaces/Access raised none, also with navigator.userActivation.hasBeenActive=true
    A2 pass network after every Stay URL unchanged and held strip/checkbox/rename still shown; completing saved: picklist PUT object/ui_asset?mode=draft 200 body urgency{select,options[{value:high}]}; lookup body linked_thing{lookup,reference:ui_bare} 200; step PUT flow/ui_flow 200 node_1 notify title; widget PUT dashboard/ui_dash 200 widget_1 dataset ui_ds values[rec_count]; Access/nav Stay → draft carries allowEdit:true / 'Dash Renamed'
    A3 pass HTTP-live every Leave cell: GET /meta/_drafts?packageId=com.qa.ui [] and item ?state=draft&package= 404 NO_DRAFT before and after (picklist a-e, lookup a/e, step a/b/e, widget a/e, Access a/d/e, nav a/d/e); Leave URLs /automations, /data?surface=object:ui_bare, /com.qa.uib/data, /_console/studio, reload with edit gone
    N0 pass browser-live no departure dropped a held edit without a prompt (all 24 held cells prompted)
    N1 pass browser-live no prompt on any clean pillar (17 clean cells incl. 5 with user activation, 0 dialogs)

    Notes: Fixture package com.qa.ui made through POST /api/v1/packages, not the Studio landing New package dialog. Picklist label was 'Urgency' for the leave matrix (a field 'Priority' already existed from the publish item). The nav rename still shows no held strip; the guard fires anyway (as in w4).

    studio-authoring.held-edit-publish-refusal · r3 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass browser-live (+pin-jsdom 2 files/6 tests) after screenshot, toast after Publish→Publish all: 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, …'; Interfaces 'Not published: Dataset on the widget “New metric (kpi)” needs a value, …'
    A1 pass network + HTTP-live per pillar 0 non-GET requests in the trace window after the refused Publish all (no publish-drafts, no /meta/…/publish); GET /meta/_drafts?packageId=com.qa.ui and live reads of ui_asset/ui_flow/ui_dash identical to baseline (3/3 pillars)
    A2 pass network + HTTP-live after finishing: autosave PUT …?mode=draft&package=com.qa.ui&surface=studio 200, held strip gone; Publish all → POST /packages/com.qa.ui/publish-drafts 200 outcome published publishedCount 2; toast 'Published 2 items in this package'; live reads: priority select options[{value:high}], node_1 notify title 'QA step title', widget_1 metric dataset ui_ds values [rec_count]
    N0 pass network while each held strip showed, the refused Publish sent no publish request (3 pillars), so the batch never went around the unsent change

    Notes: widget finish needs a measure too: after binding the Dataset the held strip moves to 'Values (measures) on the widget … needs a value' (second held clause), then saves once a measure is picked. Pending draft per pillar seeded via API (ui_bare label) so Publish is enabled.

    studio-authoring.automations-rail-and-new-flow · r2 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass browser-live pkg com.qa.gsix (objects gsix_alpha 'Alpha Widget', gsix_beta 'Beta Gadget'; 5 flows via PUT ?package= 200). Rail screenshot: ALPHA WIDGET [Alpha Welcome Mailer, lightning], BETA GADGET [Beta Sync Hook, lightning], trailing NO OBJECT [Manual Cleanup arrow, Nightly Digest clock, Edge Lab arrow]; dots green x4, grey for the schedule flow
    A1 pass browser-live auto-rail-legend before: 'Record-triggered / Scheduled / Screen / Autolaunched / Switched on / Not running here' (no Off/Refused); header switch off on Beta Sync Hook → PUT …?mode=draft 200, toast 'Switched off — publish to apply', legend unchanged; after Publish all (publish-drafts 200 published 1) legend adds 'Switched off', row reads Switched off
    A2 pass browser-live 'aLPHa wel' → only Alpha Welcome Mailer; 'manual_clean' → only Manual Cleanup (name gsix_manual_cleanup, label lacks '_'); 'zzqq-nothing' → no rows + 'No matches for "zzqq-nothing" / Clear search'; cleared → all 5 back. Pin railSearch 5/5 jsdom corroborates
    A3 pass HTTP-live New automation presets record_created(checked)/updated/deleted/manual + Advanced; Save as draft with no object → 'Choose the object it watches.', dialog stays, 0 writes sent; object Alpha Widget chosen → PUT /meta/flow/gsix_g6_alpha_created?mode=draft&package=com.qa.gsix If-None-Match * 200; switch aria-checked=false 'Switched off' over 'Off by default · review before enabling'; GET ?state=draft → status obsolete, start {triggerType:record-after-create, objectName:gsix_alpha}, _diagnostics valid
    N0 pass browser-live no-match query renders the 'No matches for …' line with Clear search, never a blank rail
    N1 pass HTTP-live new flow created Off (switch false, draft status obsolete) and nothing saved until the trigger object was chosen

    Notes: State words are now 'Switched on' / 'Switched off' (#29784/#29841) where the item says On / Off and 'Enabled switch reads off'; intent holds. pin-jsdom: ui vitest at 589049b — 7 files / 51 tests passed (railSearch-11794 5, flowPresets-11861 8, flowStatusTruth-11779 9, packageLessFlows-11553 16, BuilderLanding.orgScope-11553 2, FlowStatusDot 9, railUnboundReason-11281 2); jsdom component over doubles, corroboration only

    studio-authoring.flow-edge-type-authoring · r4 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass HTTP-live flow gsix_edges Start→Push(http)→Check(decision)→End A/End B, Push→End Err, + draft loop Check→Push. Type options: Normal, Conditional, Fault (error path), Back-edge (revise loop). Autosave PUT ?mode=draft&package=com.qa.gsix&surface=studio 200: e_a conditional, e_err fault, e_loop back (cycle banner cleared), e_start Fault→Normal second PUT has no type key
    A1 pass HTTP-live GET ?state=draft → e_a {type:conditional,label:'Open path',condition:{dialect:cel,source:"record.status == 'open'"}} (inspector shows same), e_b isDefault:true; _diagnostics {valid:true}
    A2 pass HTTP-live Studio Publish all → POST /packages/com.qa.gsix/publish-drafts 200 outcome published 1; GET /meta/flow/gsix_edges edges identical to the draft read (fault/conditional+label+condition/isDefault/back; e_start no type); identical again after the post-fix republish
    A3 pass browser-live + HTTP-live condition 'record.name ==' autosaved; after reload+screenshot inspector flow-edge-server-error 'invalid CEL predicate: Unexpected token: EOF … source: record.name =='; Problems tab count 1 listing it at 'Check → End A · SCHEMA'; draft read (same body via API draft PUT) _diagnostics {valid:false, errors:[{path:edges.2.condition, code:expression-invalid}]}
    A4 pass HTTP-live Publish all → publish-drafts 200 outcome refused, failed[{flow gsix_edges, code INVALID_METADATA, issues[{rule:expression-invalid, path:flows[0].edges[2].condition}]}]; sheet 'Not published: the server refused this draft.'; live edges string-equal before/after; after fix inspector error cleared and publish → published
    N0 pass HTTP-live every inspector choice wrote its member (A0); forged PUT edges[5].type 'loopback' → 422 INVALID_METADATA 'expected one of default|fault|conditional|back', draft read 404 NO_DRAFT

    Notes: The Studio autosave body sends condition as a bare string; the draft read normalizes it to {dialect:cel,source}. A3's _diagnostics read was taken on an API-written draft carrying the identical invalid condition (the Studio-written draft was published-refused before I read its _diagnostics); inspector and Problems halves read on the Studio-written draft.

    studio-authoring.org-scope-flows · r3 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass browser-live Studio landing 'Organization flows' → /_console/studio/~org/automations; screenshot: header only Automations, no Create app; empty state 'No flows outside a package. To change a packaged flow, open its package and use New similar.' with no link/action. After PUT /meta/flow/qa_org_raw (no ?package, no ?mode) 200 'env-wide, state=active': rail lists only QA Org Raw (none of 32 showcase or 7 com.qa.gsix flows, nor the .custom clone); ?package=com.example.showcase list omits it, unscoped includes it; /~org/data landed on /~org/automations
    A1 pass browser-live + HTTP-live opened QA Org Raw: switch enabled-not-disabled, node Label input editable; label 'Notify Admin G6-EDITED' → autosave PUT /meta/flow/qa_org_raw?mode=draft&surface=studio 200 (no ?package=); GET /meta/_drafts?type=flow → {name:qa_org_raw, packageId:null}
    A2 pass network + HTTP-live sheet 'releases the 1 pending draft on its own' → POST /meta/flow/qa_org_raw/publish 200 only, no /packages/…/publish-drafts; toast 'Published flow "QA Org Raw"'; live nodes[1].label 'Notify Admin G6-EDITED'. Second run, 2 drafts: POST …/qa_org_raw2/publish 200 + …/qa_org_raw/publish 200, no batch door, toast 'Published 2 flows QA Org Raw Two, QA Org Raw'
    N0 pass browser-live + HTTP-live ~org deep link ?surface=flow:showcase_urgent_task_alert (and the .custom clone qa_g6_clone) opens no flow: 'The link names flow “…”, which is not here.'; package-less drafts only via single-item publish; POST /packages/com.qa.gsix/publish-drafts with a package-less draft pending → nothing_to_publish, draft stays packageId null

    Notes: Fixture finding asked by PM: at 589049b the clone door still lands its copy in the pair — POST /automation/showcase_urgent_task_alert/clone {name,label} 200, _packageId com.example.showcase.custom (so it cannot make the package-less fixture, as r3 says); cloning the package-less qa_org_raw → 400 'not shipped by a package … name the package the copy lands in with package' (ADR-0146 D3). The raw package-less PUT of a fresh name is still OPEN (200, env-wide, state=active), and the D11 control holds: the same PUT onto shipped showcase_scheduled_digest → 403 'provided by a managed package and is sealed … Declare an override from com.example.showcase.custom'. So r3's fixture is current, not stale; nothing blocked. ~org rail draws no greyed contributed rows (#29847 does not leak packaged flows into this scope). pin-jsdom: ui vitest at 589049b — 7 files / 51 tests passed (railSearch-11794 5,

    studio-authoring.flow-status-matches-runtime · r2 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass browser-live + HTTP-live Boot1 (default) rail data-status/sr · header Status · _status: Alpha Mailer on/'Switched on' · en true b true; G6 Alpha Created (New, published) off/'Switched off' · en false; after enable+publish on · en true b true; Beta off · en false; manual flows 'Switched on' title 'no trigger (run manually)' · en true b false; Nightly not-running/'Not running here' · en true b false deployment_policy. Boot2 (OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1, same DB): Nightly on · en true b true; others unchanged; no row disagrees
    A1 pass browser-live after screenshot: Nightly rail dot title == _status reason string verbatim ('disabled by deployment policy — package-authored scheduled work is off … not about the flow.'); header Status title carries the same string; Problems tab shows a 'Not running here' note with that reason above 'No problems — this flow is structurally valid.', no count on the tab
    A2 pass HTTP-live + browser-live header switch → toast 'Switched on — publish to apply', PUT ?mode=draft 200; Publish all → publish-drafts 200 'Published 1 item in this package'; POST /data/gsix_alpha 201 → GET /automation/gsix_g6_alpha_created/runs: 1 completed, trigger record-after-create recordId M0vR076ifPv-3wuN; disable (toast 'Switched off — publish to apply') + publish 200, _status en false; POST second record 201 → runs still 1
    A3 pass HTTP-live + browser-live boot2 banner '41 flow(s) 23 bound'; _status gsix_nightly_digest en true b true reason null; rail data-status on 'Switched on' title 'Enabled · bound to its trigger', header 'Status: Switched on'; GET /automation/gsix_nightly_digest/runs → completed trigger.type schedule at 09:48:53Z (~60 s after boot)
    N0 pass browser-live on boot1 the unbound schedule flow read 'Not running here' (grey dot, data-status not-running), never green, and Problems listed it as a note with no problem count

    Notes: Words drifted: On/Off now 'Switched on'/'Switched off'. pin-jsdom: ui vitest at 589049b — 7 files / 51 tests passed (railSearch-11794 5, flowPresets-11861 8, flowStatusTruth-11779 9, packageLessFlows-11553 16, BuilderLanding.orgScope-11553 2, FlowStatusDot 9, railUnboundReason-11281 2); jsdom component over doubles, corroboration only

    studio-authoring.validation-rules-authoring · r3 · FAIL (runner G2)

    clause verdict layer evidence
    A0 pass browser-live + pin-jsdom Screenshot of New menu on gtwo_widget: COMMON RULES End date on or after start date / Number can't be negative / Reject the save when..., folded Advanced listing Condition, Compare fields, State machine, Format, JSON schema, Conditional. end_after_start and not_negative each sent one PUT ?mode=draft 200 at once; reject_when sent 0 PUTs, focus on role=group 'When this condition is true...', held 'Not saved yet: the rule validation_3 needs a condition'; typing a condition sent PUT 200. Pins presets-11861 14/14, newRuleWaits-11820 8/8 (jsdom)
    A1 fail browser-live + HTTP-live Parse errors hold (record.name == 'blocked' && -> inline 'Unexpected token: EOF', 0 PUTs, fix -> 1 PUT 200), but an engine-refused shape does not: nullable number qty, Reject the save when... > Expression 'record.qty > 1000' -> PUT ?mode=draft 200, then cel-server-error 'applies > to record.qty, which OBJ declares as nullable', aria-invalid true, no rule-held line; message edit and condition edit each PUT 200 again; publish-drafts refused validations[0].condition expression-invalid. 2/2 fresh loads (gtwo_qty, gtwo_qty2)
    A2 pass HTTP-live All 13 object PUTs of the authoring sessions 200; Publish all -> POST /packages/com.qa.gtwo/publish-drafts 200; GET /meta/object/gtwo_widget validations[] (8 rules) deep-equal to the last authored draft: cross_field(end_date,start_date), script x3 (amount<0; name blocked [warning]; stamp=='OFF' [active:false]), state_machine(status, open->[in_progress]), format(code ^[A-Z]{2}[0-9]{4}$), json_schema(payload qty:number required), conditional(when status=='closed', then script)
    A3 pass HTTP-live POST /data/gtwo_widget: cross_field 400 VALIDATION_FAILED end_date rule_violation; script amount -1 400 _record rule_violation; code bad1 400 code invalid_format; payload {qty:'x'} 400 payload json_schema_violation; closed+stamp '' 400 _record rule_violation; PATCH open->closed 400 status invalid_transition {from:open,to:closed}, read-back open; each violating name stored 0; every twin 201, open->in_progress PATCH 200
    A4 pass HTTP-live POST name 'blocked' -> 201 warnings [{rule:name_blocked_is_not_allowed,severity:warning,code:rule_violation}]; inactive rule: POST stamp 'OFF' -> 201, warnings null
    N0 pass HTTP-live every Studio-authored, published rule enforced at the data write door as authored (A3/A4); none saved-but-inert

    Notes: state_machine rule saves with transitions {} once field+message are given, before a transition row is added; the transition was added next and saved 200 | A1 re-leg 09:55–10:05 on reboot of b1 (boot2.log), per coordinator

    studio-authoring.hooks-tab-authoring · r3 · PASS (runner G2)

    clause verdict layer evidence
    A0 pass browser-live + pin-jsdom New hook first PUT /meta/hook/gtwo_hooked_hook_1?mode=draft carried If-None-Match: * (200) (same for hook_2, hook_3); later autosaves carried If-Match = version the previous PUT answered; after reload the first save of hook_1 (pending draft) sent If-Match hmac-sha256:37ee8cbb03.. = version GET /meta/hook/gtwo_hooked_hook_1?state=draft served. Pin firstSavePinned-11773 5/5 (jsdom)
    A1 pass HTTP-live Name typed to gtwo_hooked_hook_1_renamed with ~2 s pauses: 0 PUTs, line 'Not saved yet: a hook keeps the name it was created with. Change the name back to gtwo_hooked_hook_1 to save your changes.'; GET /meta/_drafts?packageId=com.qa.gtwo&type=hook names only hook_1/2/3; name restored + label edit -> one PUT 200. Pin autosave-11787 4/4
    A2 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.5 s; fixed -> PUT 200. After publish: POST name cond 201 stamp=stamped; POST name other 201 stamp=null; PATCH other->cond 200 stamp=stamped. Pin celGate 3/3
    A3 pass HTTP-live + log hook_2 (retryPolicy {maxRetries:2,backoffMs:50}, onError log): log 'WARN [hook] retrying after failure {attempt:1,maxRetries:2}', '{attempt:2,maxRetries:2}', 'ERROR [hook] handler failed (onError=log; suppressing)'; POST throwlog 201, row stored. hook_3 (onError abort): POST throwabort 400 {error:'g2 abort thrower',code:'HOOK_ABORTED'}; 0 rows named throwabort
    N0 pass HTTP-live paused rename staged no partial-name draft: _drafts lists only gtwo_hooked_hook_1/2/3

    Notes: retry log lines are WARN level, visible at the default warn log level (no OS_LOG_LEVEL=info needed)

    studio-authoring.actions-tab-presets · r3 · PASS (runner G2)

    clause verdict layer evidence
    A0 pass browser-live + pin-jsdom Screenshot of New menu on gtwo_act: 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 {operation:update,params:[{field:status}]}; draft read type script, params status. On gtwo_plain the row is disabled 'Needs a picklist field on this object.', forced click 0 PUTs. Pin presets-11861 26/26
    A1 pass HTTP-live Run a flow / Open a web page / Open a page in a dialog each held 'Not saved yet: the action ... needs "Flow name" / "URL" / "Modal / page name"', 0 PUTs incl. label edits; target given -> one PUT 200 each (flow gtwo_stamp_flow; url http://localhost:42020/api/v1/health?rid=${ctx.recordId}; modal gtwo_dialog_page). Fourth preset deleted while held: 0 requests; draft actions = action_1..4 only
    A2 pass HTTP-live + browser-live member g2.member (form sign-in, session read back): Change Status! prompt -> In progress -> POST /actions/gtwo_act/gtwo_act_action_1 {params:{status:in_progress}} 200, GET status in_progress; Run stamp flow -> POST action_2 200, sys_automation_run gtwo_stamp_flow completed trigger manual, stamp=flowran; Open web page navigated to /api/v1/health?rid=Hr-TMxspqz0G3YBr (real id); More > Open dialog page opened dialog 'G2 Dialog Page' rendering 'G2 dialog page body'
    A3 pass browser-live every object PUT /meta/object/gtwo_act?mode=draft across the Actions sessions answered 200 (13 PUTs: presets, targets, labels, advanced action, script body, AI exposure, later edit); no 4xx
    A4 pass HTTP-live Advanced > New action (written at once, type script) > What it does 'Run a script' > Sandboxed JS (L2) > body: draft carries type:'script', body {language:js,source:...,capabilities:[]}; member More > Script stamp -> POST /actions/gtwo_act/gtwo_act_action_5 {recordId} 200 {success:true,data:{ok:true,ranFor:'Hr-TMxspqz0G3YBr'}}, toast 'Action completed successfully'
    A5 pass HTTP-live Ticking Expose to AI agents on Change Status held until the >=40-char Tool description was typed; then PUT 200 with ai:{exposed:true,description:'Call this to move a G2 act record...'} and no flat aiExposed key; a later label edit PUT 200
    N0 pass HTTP-live no held preset reached the server before its target (A1 trace + draft read) and every published preset did its job for the member (A2, A4)

    studio-authoring.object-api-tab-curl · r2 · PASS (runner G1)

    clause verdict layer evidence
    A0 pass browser-live (DOM after screenshot) + pin-jsdom corroboration gfour_ref API tab lists exactly: GET /api/v1/data/gfour_ref, POST /api/v1/data/gfour_ref, GET /api/v1/data/gfour_ref/{id}, PATCH /api/v1/data/gfour_ref/{id}, DELETE /api/v1/data/gfour_ref/{id}; ObjectApiPanel.exampleBody-11803.test.tsx 13 passed (component-jsdom).
    A1 pass browser-live + HTTP-live Before: {contact:'<id of a showcase_contact record>',name:'string',amount:0,due_on:'2024-01-01',stage:'new'}; Form tab Add field 'Notes Extra' (PUT ?mode=draft 200, header 'Draft · 1 unpublished') -> body adds notes_extra:'string'; after publish the copied create cURL (contact substituted) 201, stored notes_extra 'string'.
    A2 pass HTTP-live All 5 copied cURLs carry -H 'Authorization: Bearer <token>'; token + contact id + {id} substituted and run in bash: list 200, create 201 (QLKCSYGiGXe8m1Md), get 200, update PATCH 200, delete 200; GET after delete 404.
    N0 pass HTTP-live Body leaves out owner_id and the optional lookup buddy ('Optional fields not shown: owner_id, buddy'), required lookup shown as '<id of a showcase_contact record>', other values right-typed; write path accepted it (201) after only the runner's id substitution.
    N1 pass HTTP-live Scoring guard confirmed: pre-publish POST /data/gfour_ref with draft-only notes_extra -> 400 INVALID_FIELD 'Unknown field notes_extra'; after publish the same body 201.

    Notes: Token = the admin session bearer from POST /auth/sign-in/email (same session-token kind the console stores under auth-session-token), not read out of localStorage. cURL text captured by stubbing navigator.clipboard.writeText and clicking each row's 'cURL' button through stable element handles (a role-name locator re-indexes once a clicked button's name turns to 'Copied' - automation artifact, not product).

    studio-authoring.summary-field-authoring · r5 · PASS (runner G1)

    clause verdict layer evidence
    A0 pass network-live (browser) gtwo_parent Form -> Add field 'Child Count' type Rollup -> 0 non-GET requests; line 'Not saved yet: Child object on the field "Child Count" needs a value...'; after Child object=gtwo_child still 0 PUTs, line names 'Aggregation'; after Aggregation=Count one PUT ?mode=draft&package=com.qa.gtwo 200. Sum held ('Child field to aggregate ... needs a value', 0 PUTs) until Amount picked.
    A1 pass browser-live (DOM after screenshot) Open picker (screenshot confirmed): Aggregation=Average -> options [Amount amount number, Price price currency]; Sum -> the same two; text name/memo, datetime due, master_detail parent not offered.
    A2 pass HTTP-live GET /meta/object/gtwo_parent?state=draft: child_count.summaryOperations {object:gtwo_child,function:count} (no field key); child_sum {object:gtwo_child,function:sum,field:amount}; after publish-drafts 200 the live read carries the same two.
    A3 pass HTTP-live POST /data/gtwo_parent + 5 children amount 10..50 -> GET parent child_count:5 child_sum:150; PATCH child 10->110 (200) + DELETE the 20 child (200) -> child_count:4 child_sum:230.
    N0 pass network-live No PUT carried an unconfigured summary (A0 trace: first PUT only after object+function, and +field for sum); publish-drafts success:true, nothing rolled back.

    Notes: Fixture published via API (gtwo_parent public_read_write; gtwo_child controlled_by_parent with name/memo text, due datetime, amount number, price currency, parent master_detail required). Step 'publish' done via POST /packages/com.qa.gtwo/publish-drafts (the door Studio's Publish all calls).


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3

    Per-clause verdicts, part 3 of 3

    One row per acceptance (A) and negative (N) clause. Layer names what the evidence reached: HTTP-live (a request to the booted server), browser-live (Playwright Chromium on the Console built from the subject, screenshot first, then text), pin-jsdom / pin-unit (a test run from this checkout at the subject, cited only as corroboration). Evidence is text from the oracle; nothing here is a screenshot.

    studio-authoring.api-written-keys-survive-studio-save · r4 · PASS (runner G1)

    clause verdict layer evidence
    A0 pass HTTP-live Deep diff kept draft -> post-Studio draft, one change each: object label 'QF Asset'->'QF Asset Renamed'; field notes.inlineHelpText ->'QA help text G1'; flow nodes[1].label 'Set X'->'Set X Renamed'; app navigation[1].label 'QA Dash'->'QA Dash Renamed'; dashboard widgets[0].title ->'Total Tasks Studio'; permission objects.gthree_asset.allowEdit false->true (+ fields:{}).
    A1 pass network-live Every Studio autosave PUT ?mode=draft&package=com.qa.gthree&surface=studio answered 200 and carried every API key equal: object 16/16 (7 object incl. activityMilestones + 9 field keys) on both object PUTs, flow 6/6, app 4/4, dashboard 1/1, permission 3/3.
    A2 pass HTTP-live publish-drafts 200 success:true publishedCount 5; live GETs: object 16/16, flow 6/6, app 4/4, dashboard requiredPermissions [studio.access], permission description/isDefault/packageId (3/3), each Studio edit live too.
    N0 pass HTTP-live No API-written key missing or nulled in any of the six variants' post-save drafts or PUT bodies (A0/A1).

    Notes: Keys: object activityMilestones [{field:status,value:done,summary:'Asset done: {name}'}], indexes [{fields:[serial],unique:'organization'}], userActions {exportCsv:false,import:false}, editMode page, searchableFields [name,serial], publicSharing {enabled:true,maxExpiryDays:7}, imageField photo; field serial.searchable/maskingRule 'name', notes.rows 7/sortable false/widget markdown-lite, cost.step 0.5/useGrouping false, price.currencyConfig {fixed,EUR}, due.dueLike true; flow description/successMessage/errorMessage/variables(+y_api)/errorHandling/requiredPermissions; app description/branding/areas/contextSelectors(active_package); dashboard requiredPermissions [studio.access]; permission description/isDefault/packageId. The first live permission read right after the 5-item publish answered 404 RESOURCE_NOT_FOUND, the next read 200 with all keys; not reproduced in 3 later publish+immediate

    studio-authoring.interfaces-create-dialogs · r2 · PASS (runner G3)

    clause verdict layer evidence
    A0 pass browser-live P1 com.qa.gthreea /interfaces: 'This package has no app yet … Create app', 0 New dashboard/report/page entries; Create app 'Repair Center' -> Identifier repair_center, box on by default; PUT /meta/app/repair_center?mode=draft&package=P1 (INM *) 200; toast 'App “Repair Center” saved as draft — open it after publishing'; lands /studio/P1/interfaces?surface=object:gthreea_ticket; draft nav [object gthreea_ticket, object gthreea_part]; P2 box cleared -> asset_desk draft navigation [] and lands /studio/P2/interfaces
    A1 pass browser-live dashboard ops_board: PUT /meta/dashboard/ops_board?mode=draft&package=P1 INM * 200 + PUT /meta/app/repair_center 200, canvas 'Design … Add widget'; report ticket_report: item PUT 200 + app PUT 200, report canvas 'Tickets 13'; HTML page repair_handbook and React page repair_widget_page: item PUT 200 + app PUT 200 each, source sheet beside live preview; app draft nav types object,object,dashboard,report,page,page
    A2 pass browser-live no dataset -> in-dialog 'Choose the dataset this report reads, and one of its measures.', 0 PUT/POST/PATCH; dataset Ticket Metrics chosen, no measure -> same refusal, 0 writes
    A3 pass browser-live dashboard id showcase_revenue_pulse (published, showcase) and gthreeb_held_dash (draft, P2): 'A dashboard with the identifier “…” already exists. Choose another identifier.', 0 writes each; page id showcase_start_here (published) and gthreeb_held_page (draft, P2): 'A page with the identifier “…” already exists…', 0 writes each
    A4 pass browser-live Publish sheet 'Publish all' -> POST /packages/com.qa.gthreea/publish-drafts 200 publishedCount 5; GET /meta/app names [showcase_app,setup,account,repair_center]; ?id=repair_center nav 6 entries; Home 'Your apps' shows Repair Center (screenshot); each entry rendered, 0 alerts: Repair Tickets list 13 rows, Spare Parts 1 row, Ops Board header + Refresh All, Ticket Report 'Tickets 13', both pages' text; 0 pageerrors
    N0 pass browser-live every successful create sent the item PUT and the app PUT as a pair (4/4 + app create); every refusal (2 report, 4 identifier) sent neither

    Notes: Pins corroborate at the jsdom component layer: interfacesCreate-11823 + interfacesCreatePage-11823 + createAppOpensInterfaces-11794 -> 3 files, 21 tests passed (cd ui && vitest --maxWorkers=1, at 589049b). Identifiers the dialogs derive are not namespace-prefixed (ops_board, ticket_report).

    studio-authoring.report-authoring-options · r3 · PASS (runner G3)

    clause verdict layer evidence
    A0 pass browser-live each created in Studio (New report: dataset gthreea_ticket_metrics + measure), type/rows/columns/blocks set on the Properties panel (every PUT 200), published via Publish all (200, 5 items). In app /apps/com.qa.gthreea/report/*, 0 alerts: g3_tabular -> flat 'Status | Region | Hours' 9 rows, no Total; g3_summary -> grouped rows + Total 68; g3_matrix -> 'Status | North | South | West | Total' grid Open 2/1/2/5 … Total 4/3/6/13 (= API cells); g3_joined -> 2 tables 'Status | Tickets' (Total 13) and 'Region | Hours' (Total 116)
    A1 pass HTTP-live summary stored runtimeFilter {region:west}, order [{by:hours_sum,direction:desc}], chart {bar,xAxis status,yAxis hours_sum}; page query carries runtimeFilter+order; POST /api/v1/analytics/dataset/query same selection -> Open 30, Closed 26, Pending 12 = rendered rows in order (unfiltered 60/32/24); bars heights 198.75/172.25/79.5 = 30:26:12 exactly, axis 0-32, same order
    A2 pass HTTP-live PUT /meta/report/g3_bad_chart?mode=draft&package=P1 (joined+chart) -> 422 INVALID_METADATA path 'chart'; PUT g3_bad_block (block b2 no dataset) -> 422 path 'blocks.1.dataset'; after each GET ?state=draft -> 404 NO_DRAFT and GET -> 404 RESOURCE_NOT_FOUND
    N0 pass browser-live matrix drew a row x column grid, tabular a flat list with no group totals, joined one table per block; the stored runtimeFilter is applied (68 h west subset, not 116 h)

    Notes: Switching Report type to Joined is held locally (no PUT) until a block is added; once one block is filled the type and blocks save together. A pure Studio gesture cannot produce the A2 bodies (the panel offers no chart on joined, and the block Dataset is marked required), so they were PUT directly as the step says.

    studio-authoring.app-and-nav-properties · r5 · PASS (runner G6)

    clause verdict layer evidence
    A0 pass browser-live pkg com.qa.gsixn, app gsixn_app (first app). After screenshot, admin and member: --brand-primary #E11D48 (--primary 347 77% 50%), --brand-accent #0EA5E9; img /_console/vite.svg naturalWidth 150; sidebar 'Area' switcher Main Area / Ops Area, each with its own entries; context selector renders ('Account' admin / 'Select Package' member)
    A1 pass browser-live both personas: Area Closed View → …/gsixn_project/view/closed_projects 'Closed Projects · 2' (2 closed rows); Area Edit Proj 1 → …/record/Tmt4UcC2E2lpouHF/edit 'Edit Nav Project … Cancel Save'; Area Open Filter → /data?filter[status]=open 'Filtered by status = open … 3 records' (3 of 5 open); Area Badged badge 'HOT' class bg-secondary text-secondary-foreground
    A2 pass browser-live Area Ghost (requiresObject gsixn_nosuch_obj) absent from rendered Main Area for admin [Closed View, Owner Only] and member [Closed View] while /meta/app serves a_ghost to both
    A3 pass browser-live 'Area Owner Only' (visible "'org_owner' in current_user.positions") present for admin, absent for member, on two fresh loads each (own contexts, form sign-in, get-session proved g6member@example.com); no visibility-predicate console error
    A4 pass HTTP-live + browser-live member GET /meta/app?id=gsixn_hidden_app 200 hidden:true; App Switcher 'Showcase, G6 Nav App'; Home 'Your apps 2 Showcase G6 Nav App'; avatar menu lists 'G6 Hidden App'; /_console/apps/gsixn_hidden_app renders 'G6 Hidden App … Hidden Projects … 2 records' on two loads
    A5 pass browser-live + HTTP-live requiresObject cell NOT JUDGED per knownGaps[0] (no control; the stored requiresObject survived every save). Studio /interfaces → Edit: each entry inspector has Landing, View, Record ID, Open mode (View/Edit), URL filters, Badge, Badge style (Default/Subtle/Alert/Outline), Order, VISIBLE WHEN ('Valid CEL'). Autosave PUT /meta/app/gsixn_app?mode=draft&package=com.qa.gsixn&surface=studio 200; draft nav: badge NEW2, badgeVariant outline, order 9, visible 'finance' pred, recordId CK6k…, recordMode view, filters {status:closed}, viewName gsixn_project.closed_projects
    N0 pass HTTP-live + browser-live hidden:true kept the app reachable for the member (meta 200, URL renders, avatar lists it) and off switcher/launcher

    Notes: Draft left unpublished on a throwaway DB. The inspector writes viewName qualified ('gsixn_project.closed_projects') where the API-authored entry used the bare 'closed_projects'; runtime effect of the qualified form not driven here (Studio edits only top-level navigation, runtime renders areas).

    studio-authoring.authored-page-slots-and-variables · r3 · PASS (runner G4)

    clause verdict layer evidence
    A0 pass HTTP-live + browser-live slots.tabs save → 422 INVALID_METADATA with the ADR-0085 tombstone; gfour_item_record (record, slotted, isDefault, highlights/details/discussion) published; record Alpha G4 shows SLOT-HIGHLIGHTS where baseline gfour_other shows OWNER/AMOUNT, SLOT-DETAILS where 'More details / NOTES' stood, SLOT-DISCUSSION in the Activity card in place of the composer/feed; header (title, Edit, ⋯) and Overview/Activity tabs as baseline
    A1 pass browser-live gfour_vars first render: VAR-STRING/NUMBER/BOOL/OBJECT/ARRAY/RECID present (name=='' , count>2, flag==true, obj.k=='v', size(items)>0, rec==''); all six CTRL (false-on-default) blocks absent
    A2 pass browser-live same load: typing 'hello' → VAR-STRING gone, CTRL-STRING shown; picking 'Beta G4' → VAR-RECID gone, CTRL-RECID shown; other four unchanged. Two runs (r1, r2)
    N0 pass browser-live all 3 slot overrides render; 12/12 variable-gated blocks match their predicates at each step

    Notes: Fixture com.qa.gfour, all through ?mode=draft&package= then publish-drafts.

    studio-authoring.access-facets-runtime · r4 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass HTTP-live+browser-live baseline M (g7m, get-session proved) POST /actions/gsev_ticket/gsev_restricted_echo 403 'requires capability [showcase.restricted_ops]', record page buttons Edit/Overview/Activity; Studio System capabilities chip 'Restricted Showcase Operations' -> autosave PUT 200, draft systemPermissions ['showcase.restricted_ops']; published -> M action 200 {ok:true}, record page shows 'Restricted Echo'; chip off + publish (live []) -> 403 and button gone
    A1 pass HTTP-live+browser-live draft rowLevelSecurity = [{gsev_open_only, gsev_ticket, select, using "record.status == 'open'", enabled}, {gsev_upd_guard, gsev_ticket, update, using 'record.amount >= 0', check "record.status != 'blocked'", enabled}]; Test dialog {status:open} -> 'Allowed', {status:closed} -> 'Denied'
    A2 pass HTTP-live published: M GET /data/gsev_ticket -> T1,T3 (= admin's 4 rows filtered status=='open'); PATCH T1 {status:blocked} 403 PERMISSION_DENIED (row still open); PATCH T1 {amount:11} 200 (read back 11); RLS read policy switched off in Studio (draft enabled:false) + publish -> M reads T1..T4
    A3 pass HTTP-live Tab visibility: Add tab, typed showcase_app, Hidden -> autosave PUT 200, draft tabPermissions {showcase_app:'hidden'}
    A4 pass HTTP-live+browser-live M GET /me/apps -> [account] (control g7s -> [showcase_app, account]); M /_console/home 'Your apps 0', no Showcase tile; control g7s 'Your apps 1 · Showcase'
    N0 pass HTTP-live every facet moved M's runtime: capability 403->200->403, rows 4->2->4, CHECK 403/200, app hidden from /me/apps and Home

    Notes: Fixture: com.qa.gsev, published gsev_ticket (public_read_write, inline action gsev_restricted_echo requiredPermissions [showcase.restricted_ops]); S=gsev_member_set (R+U) assigned to g7m via sys_user_permission_set; control member g7s.

    studio-authoring.access-owd-and-suggestions · r5 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass browser-live (network) OWD panel lists gsev_doc/gsev_project/gsev_step/gsev_ticket; gsev_doc -> Public read: row 'Unsaved', button 'Save 1 change(s)'; Save -> exactly one PUT /meta/object/gsev_doc?mode=draft&package=com.qa.gsev&surface=studio 200 sharingModel public_read; no draft for gsev_project/gsev_step (404)
    A1 pass HTTP-live+browser-live option 'Controlled by parent — inherited from the master record'; gsev_doc row 'By parent — no master: …' and Save -> PUT 422 (INVALID_METADATA sentence shown in panel), draft stays public_read, undo -> 'No changes'; gsev_step row 'By parent → gsev_project', Save -> 409 -> dialog 'Make the relationship required? … project → gsev_project' -> PUT ?force=true 200 'Saved 1 object draft(s).'; publish -> live gsev_step controlled_by_parent, project {master_detail, reference gsev_project, required:true}
    A2 pass HTTP-live+browser-live suggested-bindings?packageId=com.qa.gsev: gsev_default_set pending; pillar 'This package suggests granting "gsev_default_set" to all signed-in users (Everyone).' Grant/Dismiss; M(g7s) GET /data/gsev_doc 403; Grant -> POST …/confirm 200, status confirmed, M 200 (2 rows); Revoke -> POST …/dismiss 200, status dismissed, '…is not granted: the suggestion was dismissed or revoked.', M 403
    A3 pass HTTP-live+browser-live gsev_default2 published -> pending; Dismiss -> POST …/dismiss 200, toast 'Suggestion for "gsev_default2" dismissed.', status dismissed, pillar not-granted sentence (also after reload); M GET /data/gsev_project 403 before and after
    N0 pass HTTP-live Grant moved M 403->200 and Revoke back to 403 at once, no publish between

    Notes: Guest prompt leg not reachable on stock (knownGap), not scored.

    studio-authoring.removed-key-save-door-refusal · r4 · PASS (runner G5)

    clause verdict layer evidence
    A0 pass HTTP-live 10 baselines saved 200 in com.qa.rone (object, hook, action, list view, form view, app, page, dashboard, flow, permission); 33/33 removed keys → 422 INVALID_METADATA with the variant's marked shape, run twice (33/33 both): 11 tombstones at the key path ('was removed in @objectstack/spec N…'; dashboard.refreshInterval 'renamed to refreshIntervalSeconds'), 22 aged-out unrecognized_keys at the parent path (fields.title / config / root) naming the key + upgrade-guide sentence
    A1 pass HTTP-live for all 33 rows in both runs the ?state=draft&package=com.qa.rone read after the refusal equals the baseline draft byte-for-byte
    A2 pass HTTP-live top-level zzz_junk_key → 422 INVALID_METADATA unrecognized_keys on view ('…on this view item record: zzz_junk_key'), object, page, dashboard; no stored draft contains the key, all equal baseline
    N0 pass HTTP-live no 200 for any removed key in 66 PUTs; nothing stored or stripped

    Notes: variant shape markings (T/A) all still accurate at 589049b.

    studio-authoring.uninstalled-package-refused · r3 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass HTTP-live GET /packages/com.example.nosuch 404; PUT /meta/flow/qa_ghost_flow?mode=draft&package=com.example.nosuch 422 WRITABLE_PACKAGE_REQUIRED 'Cannot save flow … into package com.example.nosuch: no package with that id is installed … Nothing was written.'; active save 422 too; read-backs: draft 404 NO_DRAFT, live+pkg 404, live 404
    A1 pass HTTP-live object (sharingModel private) / dashboard / page, each ?mode=draft and active -> 422 WRITABLE_PACKAGE_REQUIRED naming com.example.nosuch; all 9 read-backs 404; node:sqlite scan of 108 tables for 'qa_ghost_'/'com.example.nosuch' -> 0 rows. Control: same bodies (gsev_ names) into installed com.qa.gsev -> 200 each, then discarded
    A2 pass HTTP-live POST /packages/com.example.nosuch/publish-drafts 404 RESOURCE_NOT_FOUND; all four names live 404; /meta/diagnostics: 30 stats types, com.example.nosuch in none (raw grep 0)
    A3 pass browser-live /_console/studio/com.example.nosuch (-> …/data) and …/data, twice each: 'Package not found. No package with the id "com.example.nosuch" is installed here, so there is nothing to build in.' + Studio home (click -> /_console/studio), 0 New object/Create app; /_console/studio/com.qa.gsev/bogus_pillar: 'This page does not exist. Studio has no "bogus_pillar" section; pick Data, Automations, Interfaces, Access or All metadata above.' (not Interfaces)
    N0 pass HTTP-live 10 saves bound to com.example.nosuch, all 422, none 200

    Notes: No install-local needed or done; shared examples/app-showcase/.objectstack absent before and after the run. Teardown: nothing stored.

    studio-authoring.package-customize-restore-display · r2 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass browser-live (network) dashboard: editors disabled before Customize, Customize sends 0 requests; widget title edit -> POST /packages/com.example.showcase/ensure-customization 201 then PUT /meta/dashboard/showcase_ops_dashboard?mode=draft&package=com.example.showcase.custom 200; stored row overrides {package:com.example.showcase, basedOn:0.1.0}, no _ keys; no draft at com.example.showcase (404). view/report: ensure 200 + PUT at the pair, body keys incl. overrides {com.example.showcase, 0.1.0}, _keys []
    A1 pass HTTP-live after publish (POST …custom/publish-drafts 200): dashboard bare / ?package=base / list all 'Active Projects QA7', bare _packageId com.example.showcase + overrides block; /layers code == step-1 code, overlay = effective = edited, overlayScope env. Same on view (columns without due_date on all four) and report (label 'Hours by Status QA7' on all four)
    A2 pass browser-live end user g7s client reload only: dashboard shows 'Active Projects QA7', task list (view urgent) headers lack 'Due Date', report title 'Hours by Status QA7'; server pid 14260 started 08:52:22 unchanged, boot.log has no restart line
    A3 pass browser-live (dom) studio-override-chip 'Overrides ObjectStack Showcase', title 'This item replaces ObjectStack Showcase’s item of the same name on this deployment. It was taken from ObjectStack Showcase 0.1.0.' (dashboard, view, report)
    A4 pass HTTP-live pair export: dashboards/viewItems/reports carry the item with overrides {com.example.showcase, 0.1.0} and the edit; base export's item byte-identical to step-1 export, no overrides key (checked during customization for dashboard and report)
    A5 pass HTTP-live+browser-live Cancel on studio-restore-original-dialog sends no DELETE; confirm -> DELETE …?state=draft&package=com.example.showcase.custom 200 then DELETE …?package=com.example.showcase.custom 200 (each variant); bare/base/list/layers equal step-1 baseline (minus _ keys), overlay none; end user sees shipped title / Due Date column / '(Summary)' again
    A6 pass HTTP-live+browser-live view (showcase_task.urgent) and report (showcase_hours_by_status) legs: Customize offered on both, A0–A5 each held on their own reads
    N0 pass browser-live before Customize: dashboard label/description inputs disabled, view inspector 'Remove Due Date' absent, report label disabled; no write sent
    N1 pass HTTP-live every served customization carries overrides {package:com.example.showcase}; probe: an overrides block in com.qa.gsev -> 403 NOT_OVERRIDABLE 'only com.example.showcase.custom may declare an override'

    Notes: Pins: InterfacesPillar/StudioCustomizeControls/StudioDesignSurface customize-28562 jsdom (console calls, mocked server) green in the ui run (136/136). ui/e2e/live spec not run (driven by hand instead). #29843: header if-item-saved-to reads 'Saved to ObjectStack Showcase’s customizations' after Customize (zh: '保存到 ObjectStack Showcase 的定制').

    studio-authoring.package-extension-field-c · r2 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass browser-live (network) Form › Add field 'Region': preview 'API name: region__c'; Create -> POST ensure-customization 200 (pair existed) + PUT /meta/object_extension/showcase_task?mode=draft&package=com.example.showcase.custom body {extend:showcase_task, fields:{region__c:{type:text,label:Region}}}; no object PUT; 'Tier' -> same PUT with region__c+tier__c, no second ensure
    A1 pass HTTP-live after publish: GET /meta/object/showcase_task lists region__c and tier__c beside shipped fields; pair object_extension read _packageId com.example.showcase.custom, fields [region__c, tier__c]; POST /data/showcase_task {region__c:EMEA, tier__c:Gold} 201, read back EMEA/Gold
    A2 pass browser-live (network) Assignee label -> 'Assignee QA7': PUT body fieldPatches {assignee:{label:'Assignee QA7'}} only; Required checkbox disabled; field-base-patch-only 'This field belongs to the installed package: you can change only its Label, Description, Help text, Placeholder, Option labels, saved as your customization…'
    A3 pass HTTP-live PUT /meta/object/gsev_qa_c_probe?package=com.qa.gsev with note__c -> 400 VALIDATION_ERROR issues[fields.note__c] 'the __c suffix is reserved to a base's paired .custom package … rename the field'; extension with plain_note -> 400 VALIDATION_ERROR issues[fields.plain_note] 'name it plain_note__c, or change a base field's display properties … fieldPatches'; neither stored (404/NO_DRAFT)
    A4 pass browser-live (dom) reopened Add field, 'Region' (twice, fresh loads): add-field-api-name-hint 'Another field is already named region__c. Type a different API name.', add-field-create aria-disabled=true
    N0 pass HTTP-live no object PUT sent from Studio; probes: PUT object showcase_task into the pair 403 NOT_OVERRIDABLE, into the base 403 ITEM_LOCKED

    Notes: Pins: DataPillar.baseObjectExtension-28575 + StudioCustomizeControls.objectExtension-28575 jsdom green (136/136 run). Teardown: extension row deleted, record deleted, QA pkg deleted, pair deleted.

    studio-authoring.package-customization-counts-and-marks · r2 · PASS (runner G7)

    clause verdict layer evidence
    A0 pass browser-live (dom)+HTTP-live GET /packages/com.example.showcase/customizations counts {overrides:1, extensions:2, switchedOff:1}; landing studio-landing-customizations data-state=read '3 customized, 1 switched off' (twice); pair link href /_console/studio/com.example.showcase.custom/data (lands on the base's Studio by design, ADR-0146 D3), switched-off link -> /_console/studio/com.example.showcase/automations
    A1 pass browser-live (dom) directory-customizations-total '3 customized on ObjectStack Showcase' (= 1+2); tiles object '2 customized', dashboard '1 customized'; dashboard list: showcase_ops_dashboard 'Customized (ObjectStack Showcase)', filter 'Only customizations (1)' leaves only it; permission list: showcase_custom_g7_added 'Added', filter leaves only it; object_extension list: showcase_task 'Added'; base's own items unmarked
    A2 pass network+HTTP-live landing sends GET …/com.example.showcase/customizations only (no request for writable card com.qa.gsev); com.qa.gsev All metadata reads its customizations (counts 0) and draws no total/tile/mark/filter; GET /packages/com.example.showcase.custom/customizations 400 VALIDATION_ERROR naming the base; failed-read state not staged (pins)
    N0 pass HTTP-live+dom every drawn number equals the served counts (3 = overrides 1 + extensions 2; 1 switched off); pins BuilderLanding.customizationCounts-28566 / customizationMarks / metadataAreaHost.customizations-28568 green

    Notes: Fixture: dashboard override (before its restore), region__c+tier__c extension, a pair-added permission set written by API (PUT ?mode=draft&package=com.example.showcase.custom + publish, not Studio's New), flow showcase_task_follow_up switched off through Studio's auto-ledger-switch (POST /automation/…/toggle {enabled:false} 200). packages-customizations.test.ts (runtime) not run.

    offclause.zh-cn-studio-save-29790 · r- · PASS (runner G8)

    clause verdict layer evidence
    Z1 pass browser-live + HTTP-live + DB x3 (zhq8/zhr2/zhr3): zh Studio autosave PUT carried 工单/工单列表/支持工单, owner_id 负责人, queue 队列, title 标题, priority 优先级/紧急程度/低,高; after Studio publish (outcome published, failedCount 0) GET /meta/object/NAME?package=PKG en → Ticket/Tickets, Owner, Queue, Title, Priority|How urgent|Low/High
    Z1-db pass DB sys_metadata active row x3: label Ticket, pluralLabel Tickets, fields only title/priority/notes (no injected columns stored), labels Title/Priority/Notes, help How urgent, options Low/High; save answer carried info advisories translated-label-echo at objects.NAME.label/.pluralLabel/.description/fields.title.label/fields.priority.label …
    Z2 pass HTTP-live x3 after publish: GET /meta/object/NAME?package=PKG Accept-Language zh-CN → 工单/工单列表/支持工单, 标题, 优先级|紧急程度|低/高, 备注, injected 负责人/队列/组织…
    Z3 pass browser-live + HTTP-live + DB x3: field description typed in zh Studio inspector ('Urgency bucket r1' / 'zhr2-s2' / 'zhr3-s2') read back on en and zh GET priority.description and stored in active row; r1 also object description 'Support ticket (edited in zh Studio r1)' via Settings tab landed (en read)
    Z4 pass HTTP-live x2 boots: PUT /meta/object/NAME?mode=draft&package=PKG and live mode with author fields.queue {type:text,label:'Support Queue'|'队列'} → 422 INVALID_METADATA 'declares a field named queue…'; GET /meta/_drafts → []; DB: no draft row. Studio: no gesture produced the body (add-field derives pinyin api name; renaming a field's API name to queue reverts with no PUT sent)
    Z5 pass HTTP-live + browser-live x2: valid draft written then raw sys_metadata draft row rewritten support_queue→queue; POST /packages/PKG/publish-drafts → 200 {success:false,outcome:'refused',publishedCount:0,failedCount:1,failed[0].code INVALID_METADATA}; new object: no active row, GET /data 404; existing object: active row unchanged, data still served; Studio sheet shows 未发布:Object … declares a field named queue

    Notes: Locale: the /meta reads follow Accept-Language, which the console fills from <html lang>; a Playwright context locale:'zh-CN' sets html lang zh-CN and every API call carried Accept-Language: zh-CN (no cookie or user preference involved). Which Studio body is sent depends on draft presence: with no pending draft the designer's PUT carries the untranslated (source) body; once a draft exists, a fresh Studio load edits the translated read and the PUT echoes every zh label incl. injected columns — rounds 2/3 therefore used two fresh-load sessions per object so the echo was actually exercised before publish. Fixture: translation item {name, locale:'zh-CN', objects.OBJ.{label,pluralLabel,description,fields.F.{label,help,options}}} via PUT /meta/translation/NAME?mode=draft&package=PKG + publish-drafts; injected columns read translated in zh before any Studio save. Pin corroboration: packages/


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3

    Close-out

    Every product defect the run met is extracted below, one card each, all through the filing gate and deduped against the wave-4 cards and #29672's. The one clause FAIL rests on an existing open card. No security finding; nothing withheld.

    Parked-on: #27482 · studio-authoring.validation-rules-authoring · acceptance[1]
    Extracted: #29898 · studio-authoring.picklist-bound-select · observed beside the clauses
    Extracted: #29899 · studio-authoring.picklist-bound-select · observed beside the clauses
    Extracted: #29900 · studio-authoring.report-authoring-options · observed beside the clauses
    Extracted: #29901 · studio-authoring.authoring-validation-not-persisted · observed beside the clauses
    Extracted: #29902 · studio-authoring.hooks-tab-authoring · observed beside the clauses
    Extracted: #29903 · studio-authoring.package-extension-field-c · observed beside the clauses
    Extracted: #29904 · studio-authoring.validation-rules-authoring · observed beside the clauses

    Also posted: a re-measurement comment on #27482 and the zh-CN designer readings on #29793 (both pointers, not cards). Checklist-accuracy lines go to the sweep anchor #27377's holder with the run report, not to cards.


    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