Skip to content

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

Closed

Description

@objectstack-fleet

epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848

QA run record, run on the maintainer's word in direct chat with the epic PM seat, 2026-11-03: 「然后 studio 相关的功能再完整的测试一下」; filing: 「发记录并立卡」. The record is text only.

Environment fingerprint

  • Framework: main at 3482f3d8d2. pnpm install --frozen-lockfile, pnpm build, pnpm console:build. The console is this tree's own build, served at /_console.
  • Four runners in parallel, each with its own showcase boot, port (3791–3794), file database and OS_HOME, in tabs of the desktop app's built-in browser pane.
  • Personas: the seeded admin; members made per item.
  • Isolation. Session cookies ignore ports, so runners on localhost overwrote each other's cookie until this was found mid-run. Runners then moved to rN.localhost and re-drove every fail and gate-related reading there. Two artefacts were discarded that way. Some passes ran before isolation with a bearer identity; each runner's detail names which.
  • Not run: the ui/e2e/live specs, which need the console dev server. Those items were driven by hand.

Scope

All 42 active items of area:studio-authoring not judged an hour earlier, plus i18n.studio-follows-app-locale and access-security.readonly-package-locks-studio.

Carried-forward: #29648 · studio-authoring.package-customize-restore-display · FAIL
Carried-forward: #29648 · studio-authoring.package-extension-field-c · PASS
Carried-forward: #29648 · studio-authoring.package-customization-counts-and-marks · PASS

Item verdicts

item rev verdict failed or open clauses disposition
first-run-loop 10 FAIL [2], zh-CN only product defect
object-designer-roundtrip 12 pass en leg
record-page-roundtrip 13 FAIL [0] stale spec (#28907)
draft-publish-lifecycle 11 pass
authoring-validation-not-persisted 6 pass
org-override-registry-gate 9 pass
meta-save-preconditions-and-draft-rows 2 pass
multi-package-item-rows 4 pass
save-door-runs-authoring-rules 4 pass
removed-key-save-door-refusal 4 pass
uninstalled-package-refused 3 pass
api-written-keys-survive-studio-save 3 FAIL [2] stale spec (titleFormat retired)
expression-editors 8 FAIL [2], [4], [6] product defect, #27482
metadata-diagnostics-sweep 10 pass
validation-rules-authoring 3 FAIL [1] product defect, #27482 family
hooks-tab-authoring 3 pass
actions-tab-presets 3 pass
object-api-tab-curl 2 pass
summary-field-authoring 5 pass
picklist-bound-select 4 blocked(fixture) needs a source edit
no-entry-property-matrix 4 FAIL [1], [2]; unmeasured rows named not-run product defect + stale rows
held-edit-leave-guard 5 partial [1] blocked(environment)
held-edit-publish-refusal 3 pass
view-authoring-live 5 pass
custom-page-render-and-blocks 2 pass
page-variables-and-actions 2 pass
custom-page-source-tiers 5 partial [5] blocked(environment)
interfaces-create-dialogs 2 pass
dashboard-widget-and-filter-types 5 pass
report-authoring-options 3 pass
app-and-nav-properties 5 pass requiresObject cell per knownGaps
authored-page-slots-and-variables 3 pass
doc-on-app-menu 9 pass
i18n.studio-follows-app-locale 7 FAIL [2], [3] [2] product defect, [3] stale spec
packaged-automation-studio-lock 7 pass
automations-rail-and-new-flow 2 pass
flow-edge-type-authoring 4 pass
org-scope-flows 2 pass substituted fixture, stale spec
flow-status-matches-runtime 2 partial [4] and [1]'s second boot blocked(environment)
permission-matrix-editor-ux 9 pass narrow check read at pane width
access-permission-set-lifecycle 7 pass
access-facets-runtime 4 pass
access-owd-and-suggestions 5 pass
access-security.readonly-package-locks-studio pass

Per-clause tables, evidence and reproductions are in the four comments below, one per runner. They run together past the size of one issue body.

The PM seat re-derived the first-run-loop fail itself (Studio and API, with an en control), i18n [2]'s English read over the API, and the permission-set copy finding (Studio and API). The other fails and findings are the runners' readings.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-03T00:55Z

    Runner 1 of 4, per-clause detail for this record.

    QA RUNNER 1 - studio-authoring, 12 items
    Subject: main 3482f3d8d2c48ec8d2e3306f1ce26c0364e800c2. Config studio-qa-1, port 3791, DB /tmp/studio-qa/r1/data.db.
    Clause numbers are 0-based acceptance[n].
    
    SUMMARY
    first-run-loop                         rev 10  FAIL     acceptance[2] (zh-CN only)
    object-designer-roundtrip              rev 12  PASS     (en leg; zh-CN hits F1)
    record-page-roundtrip                  rev 13  FAIL     acceptance[0] (stale spec)
    draft-publish-lifecycle                rev 11  PASS
    authoring-validation-not-persisted     rev 6   PASS
    org-override-registry-gate             rev 9   PASS
    meta-save-preconditions-and-draft-rows rev 2   PASS
    multi-package-item-rows                rev 4   PASS
    save-door-runs-authoring-rules         rev 4   PASS
    removed-key-save-door-refusal          rev 4   PASS
    uninstalled-package-refused            rev 3   PASS
    api-written-keys-survive-studio-save   rev 3   FAIL     acceptance[2] (stale spec, titleFormat only)
    
    ENVIRONMENT AND ISOLATION
    Cookie collision: session cookies ignore ports, so other runners overwrote my cookie on localhost. I measured it: a
    cookie-only GET /api/v1/packages and get-session answered 401 while the bearer token answered 200.
    Effects I saw while not isolated:
    - the package chip read "load failed"
    - the namespace was lost, so the dialog minted an unprefixed object, rep_two (discarded, not scored)
    - one POST /packages answered 401
    - the tab bounced to /_console/ twice mid-work (cause not proven; suspected to be the same collision)
    None of these is scored as a product finding.
    
    Which verdicts ran before I isolated:
    - API legs: all driven from Node with a bearer token, no cookie jar, so unaffected. This covers
      org-override-registry-gate, meta-save, multi-package, save-door, removed-key, the uninstalled-package API legs, the
      lifecycle API legs and the authoring-validation API legs.
    - Browser legs on localhost with an in-page fetch shim adding the bearer token (not isolated): the first-run-loop en
      leg, object-designer, record-page, lifecycle Studio chrome and rollback render, the authoring-validation Source tab,
      api-written-keys. The shim means none of these rests on the cookie. None of them scores a gate or a read-only lock.
    - First zh-CN repro of F1 (two Studio runs on localhost, no shim on the first).
    
    Re-checked ISOLATED on http://r1.localhost:3791 (session cookie set by JS, cookie-only get-session 200, chip correct):
    - F1, the zh-CN Studio leg: reproduced identically.
    - uninstalled-package acceptance[3], because a not-found page is the kind of thing a 401 could fake: cookie-only
      /packages 200, /packages/com.example.nosuch 404, not-found state and no build controls on both URLs, and bogus_pillar
      on a real package answers "page does not exist".
    No clause I scored fail or partial rests on a gate, a lock or a 401.
    
    Boots: three clean boots plus one restart. API items on boot 1. zh-CN leg on boot 2, restart on the same DB to test
    recovery. Boot 3 for the en leg and all remaining browser legs, with sys_user.locale set to en via PATCH; the isolated
    zh-CN recheck set it back to zh-CN on the same boot.
    The pane defaults to zh-CN (navigator.language), and a seeded admin resolves to zh-CN there.
    Console is this tree's own build, so stale-console-bundle is ruled out.
    
    PINS (framework, each run once, all green, each >=1 test, 0 failed)
    Layers: RestServer over a mock server (rest), HttpDispatcher direct (runtime parity), repository/protocol units
    (objectql, metadata-protocol), spec and lint units. None boots HTTP, so the api clauses were driven live.
    @objectstack/rest: meta-item-version-token-occ 15, meta-draft-head-package-inheritance 25,
      meta-delete-drop-storage-other-rows 5, rest-meta-sealed-first-draft-remedy 2, meta-view-chart-binding 13
    @objectstack/runtime: meta-save-preconditions-parity 23, package-uninstall-shared-object 5,
      package-promote-runtime-owner-refusal 8, package-revert-stored-members 8
    @objectstack/objectql: sys-metadata-repository-list-drafts 6, protocol-metadata-row-actor 4,
      protocol-runtime-owner-overlay-refusal 4, protocol-sentinel-owner-package-save-refusal 15,
      default-record-page-save-door 46, overlay-precedence 26
    @objectstack/metadata-protocol: protocol.packageless-save-address 68, protocol.sealed-remedy-kind 13,
      protocol.runtime-authoring-gate 235, protocol.save-door-protection 12, protocol.save-door-lock-family 25
    @objectstack/spec: page-slots-tabs-retirement 19
    @objectstack/lint: runtime-gate.object-option-visibility-writes 18, validate-component-props-url-target 20
    ui/e2e/live pins (studio-object-designer, studio-editor): NOT run, they need a console dev server. Those items are
    scored as manual browser runs.
    
    =====================================================================
    studio-authoring.first-run-loop rev 10 - FAIL (acceptance[2], zh-CN)
    Build chip form: 'New in Studio' (home-build-in-studio).
    clause | verdict | evidence
    [0] package create, switcher | pass | POST /api/v1/packages 201, surface moved to /studio/com.example.repairs/data with no reload (fetch wrapper survived), switcher lists 维修中心. Namespace derived repairs.
    [1] published object server-real | pass | zh and en: GET /meta/object/repairs_repair_ticket fields.status has exactly open, in_repair, done. Toast 'Published 1 item in this package' with Open Access.
    [2] record persists to data plane | FAIL zh-CN / pass en | zh: GET /data/repairs_repair_ticket 404 OBJECT_NOT_FOUND, Records tab shows the load-error state, no record can be entered. en: POST 201 {name:'Ticket A',status:'in_repair'}, GET lists status in_repair.
    [3] launcher + label chip | pass en; zh-CN leg blocked by [2] | en: launcher shows Repair Center, end-user list shows Ticket A with the chip 'In Repair', raw in_repair absent. App meta navigation carries objectName repairs_repair_ticket.
    [4] zero restarts | pass | en boot: single 'Server is ready' across the loop.
    [5] server-side read-only gate | pass | forged new object into com.example.showcase 422 WRITABLE_PACKAGE_REQUIRED (draft and active), read-backs 404. Add field to showcase_task 403 ITEM_LOCKED, field absent on live.
    Note: I deleted and recreated com.example.repairs on the en leg after my own typing put a wrong namespace in it. Not a product issue.
    Fail reproduction: F1 below. Classification: product defect. RUNNER rule 7 applies (P0 fail): second independent re-derivation owed.
    
    =====================================================================
    studio-authoring.object-designer-roundtrip rev 12 - PASS (en leg, manual browser run)
    [0] edits persist | pass | after publish 1 the order was name,asset_code,condition. After renaming value broken->damaged and keyboard-reordering (Space, Left, Space), the publish review read 'Field order changed' and the live read is name,condition,asset_code with values new,used,damaged.
    [1] API name derives | pass | input shows asset_code after label blur; POST {asset_code:'AC-001'} 201; GET record asset_code AC-001, no field_N key.
    [2] typed identifier survives | pass | input reads repair_asset after char-by-char typing, preview 'Saved as repairs_repair_asset', meta name repairs_repair_asset.
    [3] empty option row | pass | 3 real options plus an empty row: no validation banner after the debounce, no 'System identifier' text, the PUT carried 3 options, published picklist has 3.
    Caveat: not driven in zh-CN. The first publish there is F1, so acceptance[1]'s stored-column half would fail.
    
    =====================================================================
    studio-authoring.record-page-roundtrip rev 13 - FAIL (acceptance[0], stale spec)
    [0] Kind=full persists with seeded regions, record:highlights present | FAIL | PUT /meta/page/invoice_page_qa1?mode=draft 200: type record, object showcase_contact, kind full, regions non-empty (page:header, record:path, page:tabs > record:details, record:activity), NO record:highlights. Reproduced on a second create (second_page_qa1).
    [1] picker | pass | dialog lists Card page:card, Section page:section, Record details record:details (plus Highlights).
    [2] published block | pass | POST .../publish 200; GET regions include page:card, title 'QA Card Marker'.
    [3] renders on a record | pass | viewer is admin. /apps/com.example.showcase/showcase_contact/record/<id> shows 'QA Card Marker' over the default.
    Negative (blank canvas): holds, the seed is not blank.
    Classification: stale spec. #28907 (8bb2417a74, 2026-11-02) made the strip the declared highlightFields only, and showcase_contact declares none. Control: a full create on showcase_account (declares highlightFields) has record:highlights in its PUT. Suggested item fix: read the clause as 'present when the object declares highlightFields', or carry acceptance[0] on showcase_account.
    
    =====================================================================
    studio-authoring.draft-publish-lifecycle rev 11 - PASS
    [0] draft staged | pass | PUT {success,version,seq,state:'draft'}; GET 404.
    [1] publish flips | pass | publish 200; GET {type,name,item} widget 'Rev 1'.
    [2] second draft | pass | live stays Rev 1 with draft 2 pending; Rev 2 after publish.
    [3] OCC | pass | stale If-Match while a draft is staged: 409 METADATA_CONFLICT with currentVersion; draft stays Rev 2.
    [4] publish-drafts atomic | pass | with an unprefixed object draft: HTTP 200, data.success false, outcome refused, NAMESPACE_PREFIX, publishedCount 0, both dashboards live 404 and drafts kept. After discarding it: published 2, both live 200.
    [5] invalid draft | pass | stray widget key 422 INVALID_METADATA at widgets.0, live unchanged.
    [6] Studio chrome | pass | 'Draft · 2 unpublished' and Changes pill 2 pre-publish; Publish all clears both at once and after a reload.
    [7] history | pass | v1 create, v2 publish, v3 create, v4 publish, all with the admin actor.
    [8] diff | pass | from=2&to=4 changed [{path:'widgets',from,to}] only; from=1&to=2 changed [].
    [9] rollback | pass | toVersion 1 200, live Rev 1, rendered dashboard shows 'Rev 1 widget' and not Rev 2. Missing, abc, 0, -3 each 400 INVALID_REQUEST; 9999 404 VERSION_NOT_FOUND.
    [10] audit | pass | exactly 1 denied metadata_conflict save row and 1 denied invalid_metadata save row (source protocol.saveMetaItem), allowed publish and rollback rows, all naming the admin. No rows for INVALID_REQUEST or NO_DRAFT.
    [11] X-Actor inert | pass | rollback and draft save with X-Actor: someone_else_entirely; the string appears in neither audit nor history, rows name the admin.
    Note: API legs ran on boot 1, chrome and render on boot 3. A repeat rollback to the same version wrote an audit row but no new history row (same seq); not a clause.
    
    =====================================================================
    studio-authoring.authoring-validation-not-persisted rev 6 - PASS
    [0] located object error | pass | 422 INVALID_METADATA 'object/qa_invalid_probe failed spec validation: 1 issue — fields.amount.type [invalid_value]', issues[].path fields.amount.type.
    [1] view guidance | pass | container-shaped body on a fresh object: 'type belongs to a single VIEW, not to the container. Wrap it: defineView({ list: ...' Flat body: 'names no object' refusal.
    [2] not persisted | pass | live and draft reads 404 after each refusal.
    [3] browser door | pass | Source tab autosave PUT 422, banner 'Fields → amount → type: Invalid option...', stored draft keeps type number.
    [4] corrected shapes | pass | both 200 state:'draft'.
    
    =====================================================================
    studio-authoring.org-override-registry-gate rev 9 - PASS
    Registry flags read: object overlay false/create true; field false/false; job false/false; view and dashboard true/true.
    [0] overlay refused | pass | PUT /meta/object/showcase_task 403 NOT_OVERRIDABLE; field variant 403 too.
    [1] declared override | pass | ensure-customization 201 then 200. PUT into .custom with overrides 200; package-less GET serves the label with _packageId com.example.showcase. Dashboard variant same. Package-less PUT 403 naming the pair.
    [2] create refused | pass | job 403 NOT_CREATABLE, read 404; new field 403 NOT_CREATABLE.
    [3] independent gates | pass | new object with sharingModel 200 in the same run; without it 422 security-owd-unset.
    [4] reset | pass | DELETE reset:true and artifact label back; second DELETE reset:false.
    Pin overlay-precedence (unit, corroborates only): 26 passed.
    
    =====================================================================
    studio-authoring.meta-save-preconditions-and-draft-rows rev 2 - PASS
    [0] pins | pass | 5 files green (above).
    [1] OCC and preconditions | pass | ?state=draft serves version, plain read 404. If-Match current 200, stale 409 METADATA_CONFLICT with currentVersion. If-None-Match:* on existing 409 with currentVersion. "abc" 400; both headers 400; label unchanged after each.
    [2] draft row label | pass | _drafts row label 'QA Draft Label'.
    [3] actor | pass | history events actor = admin id; _drafts updatedBy same.
    Probe: If-None-Match:* on an absent name saves 200.
    
    =====================================================================
    studio-authoring.multi-package-item-rows rev 4 - PASS
    [0] pins | pass | 9 files green (above).
    [1] ambiguous | pass | package-less save and delete both 409 RESOURCE_CONFLICT 'Nothing was written/deleted', rows unchanged. After deleting A's row, the package-less save updated B (stored rows: one, package com.qa.pkgb).
    [2] owner mismatch | pass | PUT pkga_qa_obj?package=B 422 OBJECT_OVERLAY_PACKAGE_MISMATCH, read-back label unchanged, pkg A.
    [3] sealed sentence | pass | exact ruled text on object showcase_task, as create, draft and ?package=.
    [4] revert | pass | revert serves the published body, removes a draft-only item (404); never-published 409 RESOURCE_CONFLICT with the draft kept.
    Accuracy note: 'the ONE ruled text every write over a sealed name reads' holds for object only. flow answers its own 403 with a declare-override/toggle remedy, and field answers a code-only sentence. Scored on the step's object. Also a package with no rows at all answers revert 404, not 409.
    
    =====================================================================
    studio-authoring.save-door-runs-authoring-rules rev 4 - PASS
    [0] pins | pass | 8 files green (above).
    [1] refusals and no storage | pass | Read-backs 404 after each:
      titel 422 INVALID_METADATA path ...properties.titel rule component-props-unknown-key
      slots.tabs 422 path slots.tabs invalid_type (also beside a valid details slot)
      second default 422 DEFAULT_RECORD_PAGE_CONFLICT naming qa_default2 and showcase_task_detail
      chart no block 422 path chart custom
      object 422 rule expression-invalid at the option visibleWhen and at validations[0].condition
      protection 400 VALIDATION_ERROR path protection; _lock 400 path _lock; label unchanged, lock none
      javascript: target 422 path target custom
      Controls saved 200 for each corrected body.
    [2] did-you-mean | pass | 'Did you mean `titel` → `title`?'
    [3] draft admitted, publish refused | pass | draft 200 with _diagnostics valid:false; publish 422 expression-invalid; live 404.
    
    =====================================================================
    studio-authoring.removed-key-save-door-refusal rev 4 - PASS
    [0] 33 rows | pass | 33 of 33 422 INVALID_METADATA in the shape the variant names: tombstone at the key's own path, aged-out at the parent with the key named and the upgrade-guide sentence.
    [1] nothing stored | pass | paired draft read equals the baseline for all 33; all baselines saved 200.
    [2] junk key | pass | view, object, page, dashboard each 422 unrecognized_keys, zzz_junk_key absent from the stored draft.
    Per-row lines: qa/studio/r1/rk.out
    
    =====================================================================
    studio-authoring.uninstalled-package-refused rev 3 - PASS
    [0] control | pass | flow 422 WRITABLE_PACKAGE_REQUIRED naming com.example.nosuch, 'Nothing was written', reads 404.
    [1] other types | pass | object, dashboard, page, draft and active: all 422 WRITABLE_PACKAGE_REQUIRED, all reads 404.
    [2] publish-drafts | pass | 404 RESOURCE_NOT_FOUND, names live 404, diagnostics has no nosuch anywhere.
    [3] Studio | pass | re-checked isolated: not-found state with a Studio home link, no build controls, on both URLs. bogus_pillar keeps its URL and says the page does not exist.
    Probes: view, action, app, report also refused. ?package= (empty) is treated as package-less and saves 200.
    
    =====================================================================
    studio-authoring.api-written-keys-survive-studio-save rev 3 - FAIL (acceptance[2], stale spec)
    [0] draft diff | pass | six variants; the diff shows only the Studio edit. One addition: the permission save adds an empty fields:{}, no API key touched.
    [1] PUT carries the keys | pass | captured PUT bodies contain every API-written key for object, field, flow, app, dashboard, permission.
    [2] live serves as written | FAIL | object.titleFormat cannot go live: publish-drafts answered 200 success:false, INVALID_METADATA rule title-format-retired, BATCH_ABORTED for the other four. Reproduced twice. With titleFormat removed from the draft by API: published 5, and every remaining key across the six variants reads back identical live, Studio edits included.
    Classification: stale spec. 3557366c02 (2026-11-02) retired titleFormat at save and publish after the item was written; the variant still lists it. Suggested fix: drop titleFormat from the object variant. Studio did not lose the key.
    
    =====================================================================
    FINDINGS
    
    F1 - PRODUCT DEFECT, HIGH. Any object first published from Studio in zh-CN never reaches the data plane, while publish reports success.
    Cause (proven by the API legs): the draft read serves the injected `queue` column with a translated label (队列). Studio's autosave echoes it. The registry judges it byte for byte with isInjectedColumnDefinition, reads it as the author's field and throws the reserved-name error. The protocol swallows that in a console.warn (packages/metadata-protocol/src/protocol.ts:10480). The en echo is stripped as identical, so the en loop passes.
    Studio repro (isolated, r1.localhost, sys_user.locale zh-CN):
     1. New object in a writable package.
     2. Add any field; the autosave PUT fields.queue.label is 队列.
     3. Publish, then 全部发布: POST publish-drafts 200, outcome published, success toast.
     4. Records tab: load-error state. GET /data/OBJ 404 'Object is not registered' OBJECT_NOT_FOUND; GET /meta/object/OBJ 200.
     5. Log: '[Protocol] registerObject failed for OBJ: ... declares a field named `queue`...'
    Expected: the record door works after publish, as in en.
    API repro:
     - GET /meta/object/<any published object> with Accept-Language: zh-CN
     - PUT /meta/object/NEW?mode=draft&package=PKG with those fields, minus __search
     - POST /packages/PKG/publish-drafts, then GET /data/NEW: 404
     - control with the en body: 200
    Durable: a restart on the same DB logs '[Protocol] Failed to hydrate object/...' and the data door stays 404.
    Stored row: json fields.queue.label is 队列 on the broken row, absent on en rows.
    Reproduced 3 times in Studio (twice not isolated, once isolated) and by API. Automation self-check: the server log and bearer API reads agree; traps ruled out. Not found in a read-only issue search.
    Related: other injected columns are also stored with translated labels but do not throw.
    
    F2 - Low, off-clause, needs a ruling. GET /meta/dashboard/NAME?package=NONHOLDER answers 200 with another package's row (_packageId com.qa.pkgb), also for a package whose own row was just deleted. Repro: rows in A and B, DELETE ?package=A, GET ?package=A. May be a deliberate fallback; a reader asking for A's row cannot tell it got B's.
    
    F3 - Low UX, off-clause. Opening Repair Center (no currency field on the object) raised a 'Set the default currency' modal over the end-user list. Seen once.
    
    F4 - Note, environment-triggered. packages-io.ts calls are cookie-only (credentials: include). With a dead cookie and a live bearer, Studio shows the failed chip, loses the namespace, and the New object dialog previews and writes an unprefixed name with no warning (rep_two, in zh-CN). Only seen under the cookie collision; a real analogue would be an expired cookie with a live bearer token.
    
    PROBE LINE
    About 10 minutes, admin persona only. Covered: empty ?package=, non-holder ?package=, rollback bad values and an unknown version, If-None-Match:* on an absent name, sealed draft/?package= variants, a package with no rows on revert, extra types on the uninstalled door, restart recovery for F1. Found F1's durability and F2.
    
    CHECKLIST ACCURACY
    - record-page-roundtrip acceptance[0]: stale since #28907.
    - api-written-keys object variant: titleFormat stale since 3557366c02.
    - multi-package acceptance[3]: 'every write over a sealed name' overstates; flow and field differ.
    - first-run-loop: no locale is named, and steps[1] uses a Chinese display name. Suggest a named zh-CN leg, since en passes and zh-CN does not.
    
    Server stopped. My DB remains at /tmp/studio-qa/r1/data.db. No source edits, commits or GitHub writes.
    
  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-03T00:55Z

    Runner 2 of 4, per-clause detail for this record.

    # Studio checklist, runner 2 (studio-qa-2, port 3792)
    
    Subject: main 3482f3d8d2, console from this tree. Package under test: com.qa.rtwo (namespace qa), authored on the run's own DB.
    
    ## Isolation note (read before trusting a verdict)
    Session cookies ignore ports, so the runners on localhost overwrite each other's cookie.
    - Symptom met here: GET /api/v1/packages and /api/v1/meta/_drafts?packageId=... answered 401 at page load, the header read "Failed to load", and "Couldn't read customizations: UNAUTHENTICATED" showed on the directory. Publish was disabled while the drafts read failed.
    - First mitigation (Run A): localhost:3792 with a page-level fetch wrapper adding the bearer token and re-firing objectui:packages-changed. The token survives per origin. Load-time reads made before the wrapper was installed could still 401.
    - Isolated (Run B): r2.localhost:3792, with the session cookie set by JS. Sign-in from that origin is refused 403 INVALID_ORIGIN, so the token came from a credentials:'omit' sign-in. GET /packages = 200, header clean.
    - API scripts used their own cookie jar and were never affected.
    
    No verdict here rests on a gate, a read-only lock or a 401 reading.
    - Run A only: expression-editors formula/conditional/test-run UI legs, validation-rules authoring, hooks, actions, object-api, summary, the Data/Step/Widget/Access cells of leave-guard, Automations and Interfaces legs of publish-refusal.
    - Re-driven on Run B: every fail below (validation nullable probe, RLS formula probe, RLS non-lowerable probe), the Data leg of publish-refusal through the literal Publish button, Data departures (a)(c) and beforeunload, the showcase directory tiles. Same readings.
    - Run B only: nav-rename control cells, form-view layout and list-view lockedBy rendering.
    - Run A driver caveat: on publish-refusal's refused Automations and Interfaces legs the Publish button was disabled by the drafts 401, so the sheet was opened from Changes, the same review sheet. The accepted legs used Publish.
    
    ## Pins (RULE 6)
    - ui, jsdom component/unit layer: 14 files, 162 tests passed at 3482f3d8d2. celAuthoring, CelPredicateField, CelTestRunDialog, PermissionAdvancedFacets.cel, ObjectValidationsPanel.presets-11861, newRuleWaits-11820, ObjectHooksPanel.firstSavePinned-11773, autosave-11787, celGate, ObjectActionsPanel.presets-11861, newActionDefault-11820, ObjectApiPanel.exampleBody-11803, StudioDesignSurface.heldPublish-11786, heldWidgetPublish-11910.
    - metadata-protocol, plugin/protocol unit layer: protocol.diagnostics-store-outage + metadata-diagnostics.union-issues, 2 files, 25 tests passed.
    Only expression-editors [7] and diagnostics [7] are scored from pins, both with oracle `test`.
    
    ## studio-authoring.expression-editors · rev 8 · FAIL (2, 4, 6)
    | clause | verdict | evidence |
    |---|---|---|
    | 0 inferred type | pass | Studio Form inspector on qa_doc.line_total: record.qty * 1.1 "Result type: Number"; record.qty + record.qty and record.name + record.code "Unknown — ... Wrap operands in double() / int() / string()" |
    | 1 returnType stamp | pass | PUT body carries returnType number on edit; unknown removes it; sib_formula returnType "text" and sib_formula2 (none) unchanged; live read after publish agrees |
    | 2 lint tiers equal engine | fail | unknown field, bare ref and non-lowerable USING are errors and match the engine. record.line_total > 7 reads "Valid CEL" at 600 ms while publish answers 422 rls-predicate-unenforceable (knownGaps[3], #27482) |
    | 3 completion | pass | previous./record. list fields (record.st: start_date, status; previous.s adds sib_*); current_user. and parent. no listbox; bare st offers only functions; bare qty error with record.qty; suggestion inserted previous.status |
    | 4 RLS save gate | fail | non-lowerable and parse fault held, perm-autosave-held, no PUT. Formula probe: PUT 200 for each newly refused text (> 5, > 6, > 7), held null; an edit after the re-read is held (#27482 residual) |
    | 5 test-run | pass | Allowed shield-check / Denied shield-x / Non-boolean "zed" triangle; celEngine.evaluate gives true / false / "zed" |
    | 6 parity | fail | all formula and conditional probes equal validateExpression; RLS formula probe differs (as clause 2). Engine read directly from @objectstack/formula and the publish door; ai.mcp-validate-expression not re-run |
    | 7 degradation | pass | celAuthoring.test.ts green, jsdom, oracle test |
    Class: product defect, open #27482. Not a new finding. Publish verdicts: matches() 422 rls-predicate-unenforceable; "record.name ==" 422 rls-predicate-unparseable.
    
    ## studio-authoring.metadata-diagnostics-sweep · rev 10 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 contract | pass | bare body, total 0 = entries 0, scannedTypes 30 = registry withSchema 30 of 32, scannedItems 418 = sum of stats |
    | 1 stats | pass | object 116/116, view 36/36, flow 32/32, dashboard 4/4 count equal lists, packages equal. Locked vs /layers envelope: object full 116 + none 9 vs count 125 locked 116 (after fixtures); flow 32 full; view/dashboard none |
    | 2 scoping | pass | ?package=com.example.showcase scannedItems 252; tiles object 24, hook 8, action 72, view 36, flow 32, page 29, dashboard 3, permission 9 etc. equal scoped stats (re-read on Run B) |
    | 3 refusals | pass | ?type=viewes 400 INVALID_REQUEST naming view/views; ?severity=error&severity=warning 400 VALIDATION_ERROR |
    | 4 severity | pass | 200, entries identical with 0 entries and again with 1 planted |
    | 5 empty state | pass | "All clear / Scanned 19 item(s) across 30 type(s)" equals ?package=com.qa.rtwo body; showcase directory has no diagnostics link |
    | 6 invalid item | pass | planted qa_doc.plant: errors include config.columns.0.field; page groups under VIEW; link /metadata/view/qa_doc.plant lands on its Studio surface; tile "1 invalid item(s)"; "View all issues (1)" tone error |
    | 7 outage | pass | protocol.diagnostics-store-outage.test.ts green, oracle test |
    Accuracy notes: steps' /apps/<app>/metadata route is now /_console/studio/PKG/metadata. knownGaps[0] "deleting the row restores a clean sweep" did not hold once the item had been read: the row was gone from SQLite, the sweep and GET still served it; DELETE on the door answered "nothing to delete" and then the sweep read clean.
    
    ## studio-authoring.validation-rules-authoring · rev 3 · FAIL (1)
    | clause | verdict | evidence |
    |---|---|---|
    | 0 presets/held | pass | COMMON RULES first, Advanced holds six types; end_after_start and not_negative PUT 200 at once; reject_when no PUT, held line. Focus lands on the wrapper div round the condition builder, not on an input |
    | 1 CEL holds the save | fail | parse fault and unknown field hold, no PUT, fix sends 200. record.qty > 1000 does not hold; see below |
    | 2 spec-valid as authored | pass | all PUTs 200 (one 409 caused by my own out-of-band publish); live validations[] carries each type, fields, message, severity, active |
    | 3 enforcement | pass | 400 VALIDATION_FAILED: rule_violation cross_field and script, invalid_format, json_schema_violation, invalid_transition, wrapped rule "Wrapped rule says no"; nothing stored; every twin 2xx |
    | 4 warning / inactive | pass | warnme 201 with warnings[warn_me]; offme 201 |
    Fail reproduction, 3 of 3 (2 fresh loads on Run A, 1 on Run B):
    Studio qa_doc (qty a nullable number) → Validations → New → Reject the save when… → Expression → type record.qty > 1000. Expected per verify: no object PUT while the error stands. Actual: PUT 200, then red cel-server-error "applies `>` to `record.qty`, which 'qa_doc' declares as nullable", aria-invalid true, no held line; editing the message PUTs 200, editing the condition PUTs 200. POST publish-drafts: refused, validations[0].condition [expression-invalid].
    Class: product defect. Same shape as #27482, on the Validations door, and there it never holds at all. Judgment for PM: the clause's named shapes (syntax, unknown field) do hold; this fail rests on a condition the publish gate calls invalid.
    Driver: the JSON textareas commit on focusout, and the pane has no focus, so focusout was dispatched by hand.
    
    ## studio-authoring.hooks-tab-authoring · rev 3 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 pins | pass | first PUT if-none-match *; later PUTs if-match hmac version |
    | 1 rename held | pass | held line names qa_hook_hook_1; no PUT on two renames; _drafts type=hook lists only qa_hook_hook_1; back + edit saves 200 |
    | 2 CEL gate | pass | "record.name ==" hooks-autosave-held, no PUT; valid condition PUT 200; published: cond stamped, other null, update to cond stamped |
    | 3 retry / On Error | pass | log: attempt 1 and 2 of maxRetries 2 then "onError=log; suppressing"; logme 201; abortme 400 HOOK_ABORTED boom-abort, read-back no row |
    Accuracy: the log line reads attempt:1, maxRetries:2, not "attempt 1/2".
    
    ## studio-authoring.actions-tab-presets · rev 3 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 presets first | pass | four presets then Advanced; change_choice bound to status on qa_act; qa_nopick disabled "Needs a picklist field on this object." |
    | 1 held until target | pass | run_flow, open_url, open_page held with line, no PUT; target sends one; Delete on held: no PUT, draft never lists it |
    | 2 member runtime | pass | member identity proved by get-session. status closed via POST actions (200) and read; flow success, touched=flowran; window.open https://example.com/r/<real id>; dialog "QA Page / QA dialog page body" |
    | 3 saves 200 | pass | every object PUT 200 |
    | 4 script | pass | draft type script with body and capabilities; More actions → Script stamp POST 200, touched=scripted |
    | 5 AI block | pass | ai:{exposed:true,description}, no aiExposed, 200, later edit 200. Held until the 40-char description is given |
    
    ## studio-authoring.object-api-tab-curl · rev 2 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 endpoints | pass | GET, POST, GET {id}, PATCH {id}, DELETE {id} under /api/v1/data/qa_api |
    | 1 draft example | pass | extra_note appears before publish; create 201; boss shown as <id of a qa_other record>, optional reference and owner_id left out |
    | 2 cURLs | pass | list 200, create 201, get 200, update 200, delete 200 with Authorization substituted |
    
    ## studio-authoring.summary-field-authoring · rev 5 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 held | pass | no PUT; line names Child object, then Aggregation, then Child field |
    | 1 numeric only | pass | sum and average picker list only Amount; name and note absent |
    | 2 spec-valid | pass | count {object,function}, sum {object,field,function} |
    | 3 compute | pass | 5/150; PATCH 5/155; DELETE 4/135 |
    Accuracy: the type picker reads "Rollup"; stored type is summary.
    
    ## studio-authoring.picklist-bound-select · rev 4 · BLOCKED(fixture), all 8 clauses
    The fixture is an uncommitted edit of examples/app-showcase/objectstack.config.ts plus a --compile boot; the brief forbids source edits and the launch config has no --compile. Confirmed: GET /meta/picklist items []; PUT /meta/picklist/qa_pl 403 code-only. knownGaps[0].
    
    ## studio-authoring.no-entry-property-matrix · rev 4 · FAIL (1, 2), partial coverage
    | clause | verdict | evidence |
    |---|---|---|
    | 0 stored and served | pass (measured rows) | draft and live reads equal for every key written; no diffs |
    | 1 runtime effect | fail | layout row below; all other measured rows hold |
    | 2 refused or effective | fail | layout:'horizontal' stored, served, inert |
    Measured, holds:
    - object: managedBy append-only POST 403, DELETE 403; ownership org no owner_id, business_unit only owning_business_unit_id; systemFields audit false no created_by/updated_by, tenant false no organization_id; external reads remote rows, POST 403 EXTERNAL_WRITE_FORBIDDEN; unknown datasource draft PUT 422 at datasource (publish leg unreachable once the save refuses); attachedOnRead declared leaf publishes, undeclared refused [expression-invalid]; activityMilestones sys_activity summary "Deal was won"; displayNameField record_label "Titled one" (detail title not read).
    - field: format GD-0003; storage.notNull 400; multiple stores id array (chips not read); internal omitted even when selected; requiredPermissions omitted and 403 for non-holder (holder leg not run); maskingRule API 138****5678 (UI and save-back not read); deleteBehavior restrict 409 DELETE_RESTRICTED, both lookup and master_detail parent.
    - flow: description, successMessage and errorMessage served and returned; variables default written (dflt row); requiredPermissions 403; errorHandling maxRetries 2 = 3 failed runs per trigger; inputSchema served.
    - dashboard.requiredPermissions 403 for member, control 200. permission.allowExport 200 with, 403 EXPORT_NOT_PERMITTED without.
    - view.form refused at parse 422: splitDirection, splitSize, splitResizable.
    Fail reproduction, 2 fresh loads, Run B: PUT /api/v1/meta/view/qa_doc.k1?mode=draft&package=PKG with config.layout 'horizontal', publish, open /_console/studio/PKG/interfaces?surface=view%3Aqa_doc.k1, Design and Run. Expected label beside input; actual label at (257,209), input at (257,226), stacked. Class: product defect, recorded by knownGaps[0] (#26715). Read on the Studio surface, not the end-user form.
    Stale spec (knownGaps[0], variants): drawerSide and drawerWidth now 422, removed in spec 18.3.0, no longer failing rows. buttons needs objects, not the string form I tried; render not re-measured.
    Census: Studio's form-view inspector now offers Layout, Columns, Default Tab and Tab Position, so those move out of the matrix per step 4. PM's call whether the layout fail belongs elsewhere.
    Stored and served only, effect NOT measured: defaults, defaultTab, tabPosition. config.sharing.lockedBy: served, no "Locked by" on the Studio view surface with one row; end-user list not read, so not scored.
    Not run: object.fileAccessDelegate, listViews, sharing.controlledByParent.ownerMayEdit, defaultRowScope + membershipKeys, userActions / editMode / searchableFields / titleFormat / imageField / indexes / publicSharing; field.useGrouping, accept / maxSize, referenceVia, inlineEdit / inlineTitle / inlineColumns / inlineAmountField, relatedListColumns / relatedListFilter, dueLike, widget, searchable / rows / step / currencyConfig / sortable / relatedList / relatedListTitle / lookupColumns / language; all view.list keys; view.form groups, subforms, submitBehavior, modalSize, allowSkip, showStepIndicator, buttons render; dataset row; permission.description, isDefault, rowLevelSecurity[].positions. knownGaps[1] keys not judgeable.
    
    ## studio-authoring.held-edit-leave-guard · rev 5 · PARTIAL (1 blocked)
    window.confirm recorded and answered by a wrapper; every prompt text "You have unsaved changes. Leave this page anyway?".
    Matrix (prompt fired = Y, Cancel stayed and kept the held line):
    | pillar edit | pillar link | rail | package switcher | Home |
    |---|---|---|---|---|
    | Data held picklist | Y | Y | Y | Y |
    | Data held lookup | Y | - | - | - |
    | Automations held step | Y | Y | Y | Y |
    | Interfaces held widget | Y | - | Y | Y |
    | Access dirty, 127 ms and 902 ms | Y | - | - | Y |
    | Interfaces nav rename dirty, about 900 ms | Y | - | - | Y |
    | clean (from Automations/Data) | N | N | N | N |
    | clause | verdict | evidence |
    |---|---|---|
    | 0 in-app departures | pass | matrix above; after autosave landed no prompt (Access link 0 prompts; bu false) |
    | 1 reload prompt | blocked(environment) | handler measured: cancelable beforeunload defaultPrevented true for held and dirty in all pillars, false when clean. The native dialog cannot be observed: navigate with a real click beforehand reported no dialog, and the pane does not expose one. Not a defect reading |
    | 2 Stay keeps it | pass | after Cancel the line stays; Data completion PUT 200 carrying prio_five. Step and widget completion evidenced in publish-refusal, not after a cancelled departure; lookup completion not driven |
    | 3 Leave writes nothing | pass | Data a/b/c/d, step (rail), widget (link): 0 PUT, _drafts [] and item read 404 before and after; Access and nav Leave inside the window: 0 PUT, draft unchanged |
    The first Access run read no prompt because the link click was synchronous with the tick, before React rendered; re-run at 127 ms. Driver artifact.
    
    ## studio-authoring.held-edit-publish-refusal · rev 3 · PASS
    | clause | verdict | evidence |
    |---|---|---|
    | 0 refusal words | pass | Data "Not published: the field “Priority” needs at least one option, so that change is not saved yet. Finish it, then publish."; "Not published: Object on the step “Update record” needs a value..."; "Not published: Dataset on the widget “New metric (kpi)” needs a value..." |
    | 1 promotes nothing | pass | no POST after each refused click; _drafts stays object:qa_nopick; live has no priority / nodes start,end / widgets 0 |
    | 2 finished goes | pass | publish-drafts 200 outcome published, count 2 each; toast "Published 2 items in this package"; live carries field, node_1, widget_1 |
    
    ## Off-clause
    Probes, about 15 minutes, as admin and member: ?severity=bogus, ?type=nosuchtype, ?package=com.nope (all 400 naming the fix), repeated type and package params, the unsubstituted reference placeholder (400 reference_not_found).
    1. Held status text "Fix the CEL syntax errors before saving." shows for non-syntax errors (non-lowerable USING, refused formula field). Copy defect. Repro: RLS tab, USING record.name.matches('a.*').
    2. A step that clears the held line still publishes unrunnable: update_record with only objectName published (qa_flow2 node_1); trigger answers 400 "Update requires an ID or options.multi=true". Fails closed.
    3. State-machine and format field pickers list system fields (organization_id, __search, ...). Not asserted by any clause.
    4. Raw fetch routes depend on the cookie while others use the bearer, so a valid token with a stale cookie reads "Failed to load" and an inert Publish. Environment-triggered, recorded for the runner's table.
    
    Security findings: none.
    
  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-03T00:55Z

    Runner 3 of 4, per-clause detail for this record.

    QA runner 3 run record: studio-authoring (10 items) + i18n.studio-follows-app-locale
    Subject: main 3482f3d8d2, this tree's console build. Config studio-qa-3, port 3793, DB /tmp/studio-qa/r3/data.db (deleted at the end).
    Date: 2026-11-03. No GitHub writes, source edits or builds. Boots: (1) stock-page legs of custom-page-render-and-blocks, page-variables-and-actions and the first three legs of custom-page-source-tiers; (2) after a DB reset, everything else.
    
    ISOLATION NOTE (coordinator warning: cookies ignore ports)
    All live driving below ran on localhost:3793, sharing a cookie jar with the other runners, EXCEPT the rows marked [isolated], which ran on r3.localhost:3793 with the session cookie and bearer set by JS (get-session by cookie 200, /api/v1/packages 200, chip reads Read-only).
    What the shared cookie visibly did before isolation: Publish dialog "_drafts HTTP 401" (once, cured by a same-origin re-sign-in); package chip "Failed to load" and a toast "Authentication is required" on the read-only showcase, so the showcase Studio surfaces drew without the read-only lock; one unexplained full navigation to /_console/.
    Re-checks in isolation:
    - i18n clause 2 fail: reproduced [isolated].
    - i18n clause 3 fail: reproduced in zh [isolated]; en leg was pre-isolation and is not a gate.
    - doc-only app blank landing (F1): reproduced [isolated].
    - Publish dialog: lists 3 drafts with no 401 [isolated]; not a product defect.
    - Unexplained navigation: same sequence (New report, then New page, two taken identifiers) stayed on one page load [isolated]; not reproduced, not filed.
    - Sample of a passing gate: outsider on /docs/docs_book_gated and /apps/com.qa3.docs/docs/docs_gated_page reads the refusal, tree 403, doc 403 [isolated].
    No verdict here was scored fail or partial on a read-only lock or a 401. Pass verdicts that ran pre-isolation and read a gate through the console: app-and-nav clauses 3, 4, 5 and doc-on-app-menu clauses 2, 3, 4. Each persona read was proved by bearer get-session, and doc clauses 1 and 4 also have server reads through per-persona curl jars, which the shared browser jar cannot touch. App-and-nav clause 3 and 4 menus were NOT re-driven in isolation. All authoring ran on writable packages, so no lock clause depended on the cookie.
    
    SUMMARY
    studio-authoring.view-authoring-live r5: pass
    studio-authoring.custom-page-render-and-blocks r2: pass
    studio-authoring.page-variables-and-actions r2: pass
    studio-authoring.custom-page-source-tiers r5: partial (5 blocked environment)
    studio-authoring.interfaces-create-dialogs r2: pass
    studio-authoring.dashboard-widget-and-filter-types r5: pass
    studio-authoring.report-authoring-options r3: pass
    studio-authoring.app-and-nav-properties r5: pass (clause 6 requiresObject cell not judged, per knownGaps[0])
    studio-authoring.authored-page-slots-and-variables r3: pass
    studio-authoring.doc-on-app-menu r9: pass
    i18n.studio-follows-app-locale r7: fail (2 product defect, 3 stale spec)
    
    Fixture: packages com.qa3.p1, p2, p3, p4, docs (POST /api/v1/packages). Objects carry the namespace prefix (p1_alpha, p3_asset, ...), each with sharingModel public_read_write.
    
    ---- studio-authoring.view-authoring-live r5 (object p3_view2, fresh, no stored container)
    Personas: no grant on p3_view2, so end-user surfaces were scored as the admin (item permits, stated here).
    1 | pass | PUT /meta/view/p3_view2_views?mode=draft&package=com.qa3.p3 -> 200 {success:true, version:hmac-sha256:7bc4..., seq:55, state:"draft"}
    2 | pass | list after draft shows synthesized default Name, Status, Amount, Note, Extra; GET /meta/view?object=p3_view2 items:[]; GET /meta/view/p3_view2_views 404
    3 | pass | after publish + client reload: columns Name, Amount; New dialog ?form=new shows "Authored Section QA3" with inputs name, note only. Log: one boot banner, no restart line
    4 | pass | GET /meta/view?object=p3_view2 returns p3_view2.default (columns name, amount) and p3_view2.form (authored section); container name appears 0 times
    5 | pass | re-publish with amount, extra, name: live headers Amount, Extra, Name
    Note: my first attempt on p3_view lost its pre-publish leg because publish-drafts swept the draft, so the record uses p3_view2.
    
    ---- studio-authoring.custom-page-render-and-blocks r2
    Seed: showcase_project 5, showcase_task 10, showcase_account 14.
    1 | pass | command center, my work, project workspace, active projects: no role=alert, no Unknown component; metric tiles, 5 sized recharts SVGs, grid, master-detail all present
    2 | pass | KPIs 2 active, 8 open, 2 review, 1 red, 14 accounts, US$1,090,000.00 equal data plane; queue rows Warehouse schema, Design system equal the in_review tasks (page filters to in_review, per IN_REVIEW in source)
    3 | pass | status bar 361x350, 5 bars height 282 each = aggregate 2 each; priority 71,282,212,141 = low 1, medium 4, high 3, urgent 2; donut 3 sectors = 3 health values; area 3 months; budget 6 bars
    4 | pass | created QA3 foreign task owned by a member; grid shows the admin's 10 rows, foreign title absent; Leadership View card renders for admin
    5 | pass | 4 rows, no Legacy Sunset (completed exists in data plane), budgets 600k, 200k, 150k, 90k desc; health and status filters; Display > Layout offers grid and kanban
    6 | pass | parent inputs name, account, status, health, budget, end_date plus Tasks child grid with Add task
    7 | pass | qa_page_probe: "数据加载失败 Metadata not found for object: qa_no_such_object" and "Unknown component type: qa_bogus_block"; markers before and after both render
    
    ---- studio-authoring.page-variables-and-actions r2
    1 | pass | first render: hint visible, detail panel absent
    2 | pass | Data Platform picked by ref: hint gone, "✓ Project selected" shown, same performance.timeOrigin
    3 | pass | DOM read: hint false, heading true, body true
    4 | pass | typing into field_email alone shows "✓ Looks good"
    5 | pass | POST /api/v1/forms/contact-us/submit body {"name":"QA Three","email":"qa3@example.com","company":"QA3 Co","message":"QA3 message body"}, 201
    6 | pass | showcase_inquiry row hLDWP8HEM_cewcau carries the four values, source web
    Negative probe: reload without pick: hint true, heading false.
    
    ---- studio-authoring.custom-page-source-tiers r5
    1 | pass | capability map and command center (HTML) are composed static boards with no compile panel; workbench is live and interactive
    2 | pass | Open Tasks 128 vs 10 tasks; resource timing shows only sys_user_preference data calls, no find or aggregate for the tiles
    3 | pass | TOTAL PROJECTS 5 = data plane; GET /data/showcase_project?top=200 fired
    4 | pass | row click: EDITING Data Platform, name=Data Platform, budget=600000; + New project: EDITING New project, empty inputs; same page load
    5 | blocked(environment) | on-state proved live. Off-state needs a boot with OS_PAGE_REACT_ENABLED=false: launch.json has no such entry, the brief forbids Bash servers and source edits, so I did not add one. Component layer only: renderer-copy-locale-25916.test.tsx 7 passed (drives disableCapability(CAP_REACT_PAGES), jsdom, no server). It does not score the clause. To unblock: a launch entry with that env.
    6 | pass | active save and publish of faulting html both 422 INVALID_METADATA naming 6 diagnostics; live read keeps old source; Studio draft preview "HTML page failed to compile (6)" with the messages; throw renders "React 页面错误 / Error: qa3 boom"; syntax error renders "SyntaxError: Unexpected token..."
    
    ---- studio-authoring.interfaces-create-dialogs r2
    Pin: 3 files, 21 tests passed (jsdom component layer, no live writes). Live legs driven below.
    Step 1: P1 pillar says no app, offers Create app, no if-create-menu.
    1 | pass | "Repair Center" derives repair_center; draft holds nav_p1_alpha, nav_p1_beta; P2 box cleared gives navigation []; URLs stay on /interfaces; toast 已存为草稿 — 发布后即可打开
    2 | pass | dashboard, report, html page, react page: each item PUT ?mode=draft&package=com.qa3.p1 with If-None-Match:* 200 + app PUT carrying matching entry 200; canvas caption names the kind; both pages open source
    3 | pass | no dataset and dataset without measure both show 请选择这个报表读取的数据集,以及其中的一个度量; 0 writes captured
    4 | pass | p4_pub_dash, p4_draft_dash, p4_pub_page, p4_draft_page each 已有标识为「…」, 0 writes
    5 | pass | /meta/app lists repair_center; ?id= returns 6 entries; launcher shows it; alpha and beta lists, dashboard (empty, no widgets), report Count 4, both pages render with no alert
    Negative: no item-only or menu-only create seen.
    
    ---- studio-authoring.dashboard-widget-and-filter-types r5
    Add menu listed all 20, so none was "missing". Authoring deviation: one widget (bar) added and bound in Studio with autosave 200; the other 19 written through the draft PUT with it kept in the body. The render clause does not distinguish.
    1 | pass | all published on p3_board, no error. bar 3 buckets, column 2, horizontal-bar 3, line 6 months, area 6, pie 3, donut 2, funnel 3, scatter sized 365x350 with 3+ x values, treemap 3, sankey 3, combo 6, radar 3 (SVGs 365x350); gauge 12, solid-gauge 12, metric 12, kpi 7800, bullet 12 show values; table 3 rows + total; pivot Status x Category grid
    2 | pass | Add menu and Type picker each 20 entries, same types. Wording differs for metric: 指标 (KPI) vs KPI 指标
    3 | pass | text input, select, date preset picker, number input, lookup select all present
    4 | pass | one query per bound widget: text note $contains n1 (column only); number amount 100 (line only); select status hold (bar + metric, metric 12 to 4); lookup category alpha (area only); date acquired 2026-07-01..09-30 (hbar only, named empty state). Unbound Table sent nothing and stayed at 12 / 7800. Reset re-queries with no filter
    5 | pass | (a) description saved before Add item survives reload; (b) no PUT while held, edit made while held carried by completing PUT, 200; (c) synthetic beforeunload: defaultPrevented false with nothing held, true while held; after reload second item gone, by design. The native prompt itself was not seen by eye; the tool navigates through it
    
    ---- studio-authoring.report-authoring-options r3
    Studio: New report plus type changes tabular, matrix and a row dimension, autosaved. Copies written by API.
    1 | pass | tabular 9 flat rows; summary groups with total; matrix Status x Alpha/Beta/Gamma with totals; joined 2 tables Block One, Block Two; no alert
    2 | pass | R Summary rows Hold 1900, Closed 1800, Open 100, total 3800 = POST /analytics/dataset/query with runtimeFilter category beta, order desc. Chart bars heights 201, 191, 11 in the same order
    3 | pass | chart: 422 path chart "a `joined` report draws no chart"; block: 422 path blocks.0.dataset; NO_DRAFT 404 after each. (Blocks key is `name`; my first tries with `id` were refused for that.)
    
    ---- studio-authoring.app-and-nav-properties r5 (app p3_main, first in P3)
    1 | pass | --brand-primary #e11d48, --primary 347 77% 50%, --accent 199 89% 48%, vite.svg naturalWidth 150; Area One and Area Two switch their own entries; QA Scope selector renders (URL gains qa_scope). Admin and member
    2 | pass | V Second lands /p3_asset/view/second, title Second List QA3; R Edit lands record/2wb99iDUWo61bKDl/edit with inputs and Save; F Open lands /data?filter[status]=open, 4 rows = data read 4 of 12; badge NEW3 class bg-destructive
    3 | pass | A1 Req Hidden absent for admin and member
    4 | pass | admin has A1 Vis Owner, member (org_member, everyone) does not. get-session proved each. Pre-isolation, see note
    5 | pass | member /meta/app?id=p3_second returns it hidden:true; switcher: 功能展示, Repair Center, P3 Main; launcher omits it; avatar menu lists P3 Second Hidden; /apps/p3_second renders its list
    6 | pass (requiresObject not judged, knownGaps[0]) | inspector controls hold viewName second, record id + mode 编辑, filter status=open, badge + variant, order, visible; edit badge and order autosaved PUT 200 with If-Match. Areas, branding, selectors survive the autosave
    
    ---- studio-authoring.authored-page-slots-and-variables r3
    1 | pass | p3_slot record: QA3 authored highlights, details, discussion in place; header and actions keep defaults
    2 | pass | defaults: only S-STRING empty, S-NUMBER gt2, S-BOOL true, S-OBJ v, S-ARR nonempty, S-REC empty show; six false twins absent
    3 | pass | typing: S-STRING filled; picking: S-REC filled; same timeOrigin
    
    ---- studio-authoring.doc-on-app-menu r9
    Fixture via API in com.qa3.docs: gated book (showcase_auditor), org book, empty book labelled first. Stock corpus verified first: only showcase_manual public, 0 doc nav entries. Teardown step: DB deleted, no read-back.
    1 | pass | member: all 6 entries; outsider and admin: e_odoc, e_obook, e_oboth only
    2 | pass | rendered menus equal each answer, empty diff, member and outsider
    3 | pass | /docs/docs_gated_page goes to /docs/docs_book_gated/docs_gated_page, not Aaa Empty; book entries land on first page; sidebars equal the tree; gated page absent from Org Book for outsider
    4 | pass | six gated routes: "You do not have access to this documentation" + server reason; doc and tree 403. Control /docs/no_such_doc_qa3 "Documentation not found", doc 404. Two routes re-checked [isolated]
    Note: GET /meta/book/no_such_book_qa3/tree answers 200 with empty groups, not 404 (F7).
    
    ---- i18n.studio-follows-app-locale r7
    1 | pass | object editor settings: 基础信息, 标识、显示名与分类, 名称, 显示名, 访问控制 [isolated too]
    2 | FAIL | no dotted keys. Residuals with no bundle label are debt and tick (Announcement, Expense Line, Inquiry, Invoice Line, Personal Note, Semantic Zoo, External Customer/Order, widget titles). Account (Success Overlay) is not: see reproduction
    3 | FAIL (stale spec) | grid paints absolute 10月28日 / Oct 28; relative is the tooltip, 6天前 / 6 days ago, so it does follow the locale
    4 | pass | full reload stays zh-CN, objectui-locale=zh-CN; a fresh origin with empty storage followed the stored choice
    5 | pass | en: object editor and records grid 0 Han characters, pillars Data, Automations, Interfaces, Access, All metadata
    
    FAIL reproduction, clause 2 (product defect), reproduced on 3 fresh loads, 2 origins, plus API:
     Sign in, switch to 中文 through the avatar menu, open /_console/studio/com.example.showcase/data?surface=object:showcase_account.
     Expected: 客户 (bundle objects.showcase_account.label from /api/v1/i18n/translations/zh-CN; app shell sidebar and list read 客户).
     Actual: rail and object header read Account (Success Overlay).
     API: GET /api/v1/meta/object/showcase_account?locale=zh-CN with Accept-Language zh-CN -> label "Account (Success Overlay)", pluralLabel "客户"; showcase_project in the same read -> label 项目. Source label comes from examples/app-showcase/src/data/extensions/account.extension.ts. The server's label localisation appears to lose on an object with an extension label (root cause not read). Traps: stale bundle not applicable, server answers it.
     Judgement for PM: object editor option "Project Name (name)" also shows source while the bundle has 项目名称. It sits in an editor over source metadata and the negative's wording ties designers to their catalogue, so I do not count it as the fail; rule on it if you read it stricter.
    
    FAIL reproduction, clause 3 (stale spec, assertion needs revising):
     POST /api/v1/data/p3_asset with acquired 2026-10-28; open /_console/studio/com.qa3.p3/data?surface=object:p3_asset.
     Expected by item: 6天前 painted. Actual: span title="6天前" painting 10月28日; en title="6 days ago" painting Oct 28.
     Source: ui/packages/fields/src/index.tsx inGridColumn, #25884 ruling "absolute in a sortable column, relative at most as a tooltip". Designed behaviour; the clause and step predate it. No grid in the Data pillar painted a relative form (project, task, field zoo, p3_asset). Suggest verifying the tooltip.
    
    OFF-CLAUSE FINDINGS
    F1 Doc-only app lands on a blank page. PUT an app whose navigation has only type:doc entries, publish, open /_console/apps/PKG. main innerText empty, no redirect, no empty state, for admin, reader, outsider [isolated too]. Contrast: p3_main as the outsider draws an explanatory empty state; an object-first app redirects. Product defect.
    F2 Source sheet vs "side by side". New page dialog says 源码与实时预览并排显示, and the item's step says beside the preview; the source opens in a modal sheet with the preview dimmed and blurred behind it, at 1372 px. Copy or layout defect. Clause 2 does not read it.
    F3 Filter editor: typing into Options From Label Field and clearing it leaves optionsFrom held as required (尚未保存…「Options From」需要填写), and I found no way out except completing all three subfields. Seen once; I got there by a stray automated write, so not reproduced twice.
    F4 New dashboard's nav entry carries no label (report and page entries do): Studio rail shows p1_board until reload. Published menu reads P1 Board. Minor.
    F5 Default-currency prompt returned after sign-in although 暂不设置 was chosen earlier, and covered an open menu. Seen twice, across sign-outs. Minor; may be intended per session.
    F6 Object draft door accepts a name without the package namespace prefix (200); publish then 422 NAMESPACE_PREFIX. May be intended; the draft sits until deleted.
    F7 GET /meta/book/<unknown>/tree 200 with groups [] and label echo, while /meta/doc/<unknown> is 404.
    
    CHECKLIST ACCURACY
    - view-authoring-live fixture names repair_asset: in an authored package the object needs the namespace prefix to publish.
    - view-authoring-live steps: POST /packages/PKG/publish-drafts also publishes a staged view, so a run using it for other fixtures loses the pre-publish leg.
    - i18n knownGaps says 414; scripts/i18n-coverage-baseline.json reads 320 for the showcase.
    - RUNNER: nothing warns that session cookies ignore ports, so parallel runners on localhost clobber each other.
    
    PROBES: about 10 minutes beside the clauses. Doors: metadata draft, publish, books, docs; console create dialogs. Personas: admin, member, outsider. Boundaries: unknown doc and book names, faulting sources, missing namespace, incomplete filter. Results F3, F6, F7.
    SECURITY: none.
    
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-03T00:55Z

    Runner 4 of 4, per-clause detail for this record.

    QA runner 4 of 4 - studio-qa-4, port 3794, DB /tmp/studio-qa/r4/data.db, tree at main 3482f3d8d2
    Persona: seeded admin admin@objectos.ai; M = m4@example.com (holds project_editor); M2 = m5@example.com (holds no set of the package). Identity proved with get-session before persona legs.
    Scratch fixture: base package com.example.qa-base (namespace qa_base), objects qa_base_project (14 fields incl. system) / qa_base_item (required master_detail to project) / qa_base_note. Flows authored by PUT or Studio.
    
    ENVIRONMENT NOTE (affects every runner in the shared pane)
    Session cookies are host-scoped, not port-scoped. Four runners signing in on localhost overwrite each other's better-auth.session_token. On localhost:3794 my first load showed the header badge "Load failed", GET /packages and /automation/_status 401 while Bearer-carrying /meta reads answered 200, so writability read UNKNOWN and the pillar drew ungated on the read-only package ("Add node" live, no ledger switch, no Customize). That is the hydration-race fake-gate-failure, not a product verdict. A second host, 127.0.0.1, also lost its cookie part-way; the likely cause is another runner on that host, not confirmed. Fix used: admin on http://r4.localhost:3794 and M on http://r4m.localhost:3794, with the session cookie and auth-session-token set by JS from a curl sign-in, because the sign-in door answers 403 INVALID_ORIGIN off localhost. Every verdict below was read on that isolated host after the switch. Other runners' results on localhost may be tainted; check theirs for "Load failed" or 401 on /packages.
    
    PINS
    13 ui pin files, one vitest run from ui/ (--root . --maxWorkers=1): 13 files passed, 113 tests passed, at 3482f3d8d2. Layer: jsdom component over a mocked server. Files: automationsReadOnly-11124, ledgerSwitch-28148, railSearch-11794, flowPresets-11861, flowStatusTruth-11779, packageLessFlows-11553, orgScope-11553, FlowStatusDot, railUnboundReason-11281, PermissionMatrixEditor fieldBulk / readonly / artifactTier, accessNewSimilar-28573. Every clause below was also driven live; no clause is scored on a pin alone.
    Not run: packages/runtime/src/domains/permission-set-clone.test.ts (the clone door was driven live over HTTP instead).
    
    --------------------------------------------------------------------
    studio-authoring.packaged-automation-studio-lock  rev 7  ITEM: pass
    1 | courtesy gate; New lands in the pair, no base save | pass | ledger switch and Customize drawn, "Read-only" badge. New: POST /packages/com.example.showcase/ensure-customization 201, then PUT /meta/flow/qa_pair_probe?mode=draft&package=com.example.showcase.custom 200; draft _packageId is the pair. Canvas drag and typing, 5 s past debounce: zero writes.
    2 | definition not editable, forged writes refused | pass | PUT ?mode=draft&package=com.example.showcase 403 ITEM_LOCKED; PUT ?mode=draft 403 NOT_OVERRIDABLE. Flat {error, code}. A bare PUT gave the same sealed message; its status was not captured. Messages name clone/override and toggle. Meta read byte-identical to baseline (sha 02b51ab6, re-read at teardown too).
    3 | switch is the ledger switch, no metadata write | pass | auto-ledger-switch data-reading on -> off -> on, text "On/Off for this deployment"; trace holds only POST /automation/showcase_urgent_task_alert/toggle {"enabled":false} 200, then {"enabled":true} 200.
    4 | inspector inert or announced | pass | all 17 inspector inputs disabled, Remove node disabled; typing into Label left "Notify Assignee"; same after reload.
    Negatives: no 2xx flow write; no silent-vanish edit. Traps: hydration-race ruled out by the host switch, see note above.
    Accuracy: step 3 says to read where the dialog says the flow lands. The dialog text names no package at all; the target was proved by creating a probe flow (deleted after). No clause moved.
    
    studio-authoring.automations-rail-and-new-flow  rev 2  ITEM: pass
    1 | grouped by watched object, glyph and dot | pass | headings QA Item / QA Note Changed / QA Project each with its flow, trailing "No object" with manual and schedule. Glyphs lucide-zap (record), corner-down-right (autolaunched), clock (scheduled); titles "... · Record-triggered · On" etc.
    2 | legend | pass | no Off flow: "Record-triggered|Scheduled|Screen|Autolaunched|On|Not running here". After disabling QA Manual Thing and publishing: same plus "|Off". Refused not reachable here, and not drawn.
    3 | search | pass | "qA mAnU" -> qa_base_manual only; "rec_proj" (name only) -> qa_base_rec_project only; "zzqqzz" -> no rows, text 'No matches for "zzqqzz"' + Clear search; cleared -> all rows back.
    4 | preset refuses, new flow starts Off | pass | no object: "Choose the object it watches.", zero writes. With object: PUT ?mode=draft&package=com.example.qa-base; draft status obsolete, Start config objectName qa_base_note, triggerType record-after-create, _diagnostics valid. Switch off under "Off by default".
    Accuracy: the legend's Off needs a publish; the draft-only disable keeps the row and legend at On, with the toast "Disabled — publish to apply". Step 3 says "disable one flow and re-read".
    
    studio-authoring.flow-edge-type-authoring  rev 4  ITEM: pass
    1 | each Type writes the spec member | pass | draft edges: e3 conditional, e5 fault, e7 back; e7 set to Normal stored no type key, then Back-edge stored back.
    2 | label, condition, default branch | pass | e3 label "Open path", condition record.status == 'open' (string on first save, {dialect:'cel',source} on later saves; same source); e4 isDefault:true. Inspector showed the same.
    3 | survive publish | pass | live edges after publish identical in type, label, condition, isDefault.
    4 | invalid condition flagged before publish | pass | `record.name ==`: flow-edge-server-error text at the condition, canvas banner, Problems count 1; draft _diagnostics valid:false, code expression-invalid, path edges.2.condition.
    5 | publish refuses, nothing published | pass | publish-drafts outcome refused, failedCount 1, rule expression-invalid, path flows[0].edges[2].condition, where "edge 'e3' (dec→endA) condition"; live read byte-identical. Fix then publish: outcome published.
    Negative held. Normal -> undeclared cycle reads valid:false flow-undeclared-cycle, as expected for a closing edge.
    
    studio-authoring.org-scope-flows  rev 2  ITEM: pass (with a substituted fixture, stale spec below)
    1 | one pillar, package-less only | pass | Studio landing "Organization flows" -> /studio/~org/automations; nav has only Automations, no Create app, no New. Rail lists only the package-less flow. /studio/~org/data landed on ~org/automations.
    2 | opens editable, draft bound to no package | pass | switch enabled, Add node present; node label edit -> PUT /meta/flow/qa_org_raw?mode=draft&surface=studio (no package), 200; /meta/_drafts?type=flow row packageId null.
    3 | publish by reference | pass | one POST /meta/flow/qa_org_raw/publish 200, no publish-drafts; toast 'Published flow "Raw"'; live node label "Done Edited".
    Negative: deep link ~org/automations?surface=flow:showcase_urgent_task_alert opens nothing, says 'The link names flow "showcase_urgent_task_alert", which is not here.'
    STALE SPEC, fixtures.requires[1] and steps[1]: the clone door no longer makes a package-less flow. POST /automation/showcase_urgent_task_alert/clone returned packageId com.example.showcase.custom (#28149, automation-flow-clone-custom-package.test.ts). Substitute used: PUT /meta/flow/qa_org_raw with no package, 200 "env-wide". Also stale, steps[0]: empty state now reads "No flows outside a package. To change a packaged flow, open its package and use New similar." with no Open in Setup link. Empty state was read before any package-less flow existed. Note the pair existed by then; I did not test a clone with no pair, though the door ensures one.
    
    studio-authoring.flow-status-matches-runtime  rev 2  ITEM: partial
    1 | Studio words equal _status | blocked(environment) on boot 2; default boot pass | default-boot pairs: rec_item, rec_project enabled/bound -> On; manual enabled/unbound no reason -> On; manual disabled -> Off; sched enabled, bound false, deployment_policy -> Not running here; qa_note_created on: enabled/bound -> On; off: enabled false -> Off. No row disagrees.
    2 | reason verbatim, not a problem | pass | rail dot title 517 chars equals _status reason; header title "Status: Not running here\n" + reason; Problems panel shows it as a note beside "No problems — this flow is structurally valid.", tab has no count.
    3 | switch is the runtime switch | pass | enabled and published, POST /data/qa_base_note: one run, status completed, trigger record-after-create. Disabled and published, second POST: 201, runs still 1.
    4 | scheduled-work boot | blocked(environment) | launch.json has no config exporting OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1; the brief bars dev servers through Bash and edits; the project-root .env is shared with the other runners. Needs a launch entry from the PM.
    Negative held on the default boot: grey Not running here, not green, not counted as a problem.
    
    studio-authoring.permission-matrix-editor-ux  rev 9  ITEM: pass
    1 | filter narrows | pass | qa_base_project 14 / 14; "notes" -> 2 / 14, only notes_a and notes_b in DOM; budget row absent.
    2 | bulk applies to visible only | pass | Write all: notes_a, notes_b checked/checked; filter cleared, the other 12 unchanged.
    3 | saved map proves scope | pass | live read after publish keys only qa_base_project.notes_b and qa_base_project.title. No hidden field appeared.
    4 | Clear drops overrides | pass | notes_a after Read-only all {editable:false, readable:true}; after Clear key absent from draft and live; notes_b and title untouched.
    5 | Bulk column reachable when narrow | pass | resize_window is barred by the brief, so read at pane width: perm-matrix-pane clientWidth 1132 < scrollWidth 1304; scrollLeft 0 -> 172; hovered row shows R, CRUD, All, None fully on screen.
    6 | read-only gate | pass | showcase_contributor Studio: 192 checkboxes, 0 enabled; no Save; no field bulk (0 buttons); row bulk R/CRUD/All/None disabled; badges "Read-only", titles name the package. Metadata admin: 936 checkboxes 0 enabled; no Save; 468 row bulk buttons 0 enabled; "Read-only (this set is provided by a code package)". Wall and type gates not reachable on stock.
    Grant-to-access flip not re-proved here, as the item directs.
    
    studio-authoring.access-permission-set-lifecycle  rev 7  ITEM: pass
    1 | derives identifier, creates draft | pass | "Project Editor" -> project_editor; toast 'Permission set “Project Editor” created'; PUT ?mode=draft&package=com.example.qa-base; draft label and _packageId right.
    2 | taken identifier refused | pass | draft-held and published: both show "This package already has a permission set with the identifier “project_editor”: Project Editor. Open it, or choose another identifier.", zero writes; draft and live reads unchanged.
    3 | rail search and empty states | pass | "No permission sets in this package yet." on the empty package; "pROJECT eD" and "ct_edi" match; "zzqq" -> 'No matches for "zzqq"' + Clear search.
    4 | read-only banner, New into the pair | pass | "Read-only" with title "...The package is read-only: its permission sets can be viewed here, but not changed."; matrix disabled; New: ensure 200 (created:false; 201 was read on the first use in item 1), PUT ...package=com.example.showcase.custom; draft in the pair.
    5 | forged writes | pass | packaged set 403 ITEM_LOCKED; new name 422 WRITABLE_PACKAGE_REQUIRED; showcase_contributor byte-identical (sha 343156e9), new name 404.
    6 | New similar | pass | access-new-similar drawn, no studio-customize. POST /security/permission-sets/showcase_contributor/clone exactly {"name","label"}, 200, package com.example.showcase.custom, notice names what was left out; opens selected, "In com.example.showcase.custom". Pair read: 8 objects, 3 fields, RLS equal to source; only isDefault absent, as the notice says. Taken name: 409 RESOURCE_CONFLICT shown verbatim, dialog stayed open. See finding F2.
    
    studio-authoring.access-facets-runtime  rev 4  ITEM: pass
    1 | capability both ways | pass | before: M POST /actions/qa_base_project/qa_capped 403, button absent from the record header (More menu opened only on the removal leg). Chip: draft systemPermissions ["showcase.restricted_ops"]; published: "QA Capped" in M's record header, POST 200 {ok:true}. Removed and published: 403 naming the capability, absent from header and More menu.
    2 | RLS stored as authored, Test dry-run | pass | draft entry name open_only, object, operation select, using, enabled true. Test: {"status":"open"} Allowed; {"status":"closed"} Denied. Second policy no_blocked, update, check, stored with using:"".
    3 | policy scopes M | pass | M rows A, B (status open), admin holds A, B, C; GET closed row 404. PATCH status blocked 403; PATCH status open 200, landed. open_only disabled and published: rows A, B, C.
    4 | Tab visibility writes full name | pass | typed showcase_app, Hidden: draft and live tabPermissions {"showcase_app":"hidden"}.
    5 | hidden app gone | pass | M /me/apps ["account"], admin lists showcase_app. M's Home "My apps 0", no Showcase tile.
    Accuracy: steps say operation read; the select offers all, select, insert, update, delete. Probe note: M still opens the hidden app by deep link; the clause is about lists only.
    
    studio-authoring.access-owd-and-suggestions  rev 5  ITEM: pass
    1 | lists all, saves only the changed one | pass | three rows with OWD. Note private -> public read: "Unsaved", "Save 1 change(s)", one PUT /meta/object/qa_base_note?mode=draft&package=com.example.qa-base sharingModel public_read 200; live stayed private until publish.
    2 | controlled_by_parent | pass | child shows "By parent → qa_base_project". Master-less Note: warning "By parent — no master: the object needs exactly one required master-detail field"; Save 422 INVALID_METADATA path sharingModel; no draft, live private. Child published: live controlled_by_parent, project field master_detail required:true.
    3 | Grant and Revoke at once | pass | pending, banner 'This package suggests granting "qa_everyone_a" to all signed-in users (Everyone).' Grant, Dismiss. M2 403 -> Grant -> confirmed, 200 -> Revoke -> dismissed, 403.
    4 | Dismiss | pass | qa_everyone_b pending -> dismissed; '"qa_everyone_b" is not granted: the suggestion was dismissed or revoked.'; M2 403.
    Guest prompt: blocked(fixture) per knownGaps, not a clause. Self-check: one batch of my coordinate clicks landed on the wrong row after the error banner shifted the rows, and published Item as public_read; the clause reads above come from a reload with verified rows. The live result of that stray save also showed a two-change Save writes one (200) and refuses the other (422).
    
    access-security.readonly-package-locks-studio  rev 5  ITEM: pass
    1 | badge and disabled controls | pass | Object designer showcase_task: Settings 16, Validations 20, Actions 63 controls, 0 enabled. Matrix: 192 checkboxes, 0 enabled; no Save; real click on a disabled box sent nothing, state unchanged. Beside this: Form tab's "Add field" (data-form-add-extension-field) is live by design, it writes the pair's extension (#28575, source comment). Not clicked.
    2 | server refuses | pass | bare PUT /meta/object/showcase_task 403 NOT_OVERRIDABLE; ?package=com.example.showcase 403 ITEM_LOCKED; ?mode=draft&package= 403 ITEM_LOCKED.
    3 | writable contrast | pass | POST /packages 201 (writable:true); PUT /meta/object/qa_base_note?package=com.example.qa-base 200 twice; GET shows "QA Note Changed".
    4 | byte-identical | pass | sha 55eab04f before, after and at teardown.
    
    --------------------------------------------------------------------
    OFF-CLAUSE FINDINGS
    F1 product defect: scrolled matrix hides the field Read/Edit boxes.
      Path: Studio Access, a writable set, expand an object, scroll perm-matrix-pane right. The sticky field-name TD (perm-field-name-OBJ-FIELD, 697 px wide) covers both checkboxes. document.elementFromPoint at the box centre returns the TD from scrollLeft 80 up (not at 0, 40, 57). Expected: boxes reachable while scrolled; actual: invisible and unclickable. The matrix needs that scroll below 1304 px, so a narrow pane cannot reach them. Reproduced on two fresh loads. Same family, cosmetic: on the hovered row the C box shows through the sticky object cell. Clause 5 covers only the Bulk column, so this is the missing clause.
    F2 product defect: a New similar copy in the pair reads as empty.
      POST clone showcase_contributor, open /studio/com.example.showcase/access?surface=permission:NAME. Header "0 objects granted · 0 field overrides", matrix "No objects match the filter."; server read holds 8 objects, 3 fields. The package-scope matrix lists only the pair's own objects (the #24604 note), and the pair declares none, so the copied grants cannot be seen or edited there. Reproduced with two copies. Editing a capability on the copy kept the grants (draft 8 objects). The all-metadata door shows every object.
    
    PROBES (about 10 minutes, on the same boot)
    M2 with no sets: toggle, flow clone, set clone, ensure-customization, publish-drafts, PUT flow, PUT permission, list and confirm suggestions, flow runs, /meta/_drafts: all 403. GET /automation/_status and /meta/flow 200 to any member: by design, automation.ts #7900 audit comment, not a finding.
    Admin boundaries: clone with no name 400; unknown set or flow 404; enabled:"nope" 400; ensure on a writable base 409; ensure on an unknown package 404; toggle on an authored flow 409. No finding.
    
    TEARDOWN
    qa_pair_probe, clones and copies deleted; server stopped. Base flow, set and object byte-identical at the end. Tree: no new changes, no installed-packages leak. Scratch package left in my DB only.
    
  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-03T00:55Z

    Close-out of this run.

    Extracted: #29673 · studio-authoring.first-run-loop · acceptance[2]
    Extracted: #29675 · i18n.studio-follows-app-locale · acceptance[2]
    Extracted: #29676 · studio-authoring.no-entry-property-matrix · acceptance[1]
    Extracted: #29674 · studio-authoring.access-permission-set-lifecycle · off-clause
    Extracted: #29677 · studio-authoring.permission-matrix-editor-ux · off-clause
    Extracted: #29678 · studio-authoring.doc-on-app-menu · off-clause
    Parked-on: #27482 · studio-authoring.expression-editors · acceptance[2], [4], [6]
    Parked-on: #27482 · studio-authoring.validation-rules-authoring · acceptance[1]
    Parked-on: #29679 · studio-authoring.record-page-roundtrip · acceptance[0], stale spec
    Parked-on: #29679 · studio-authoring.api-written-keys-survive-studio-save · acceptance[2], stale spec
    Parked-on: #29679 · i18n.studio-follows-app-locale · acceptance[3], stale spec
    Parked-on: #29679 · studio-authoring.picklist-bound-select · blocked(fixture)
    Parked-on: #29679 · studio-authoring.flow-status-matches-runtime · partial, launch entry
    Parked-on: #29679 · studio-authoring.custom-page-source-tiers · partial, launch entry

    The other off-clause notes in the runner comments are not extracted, and held-edit-leave-guard [1] stays open: the native prompt cannot be observed from the pane.

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Record closed · 2026-11-08T01:12Z

    Closed completed on ruling #31305 (A). Close-out was already complete in comment 6592489234: every FAIL has a disposition, and every card extracted or parked from this record is closed except #27482, which carries its own residual. Retests owed for the landed fixes come through retest-owed.mjs under the qa#1 seat, not through this record.


    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