Skip to content

QA run · release-18.3.0-w5-fail (20/20) · 589049b0 · 2026-11-03 · 15 PASS / 0 PARTIAL / 5 FAIL / 0 BLOCKED / 0 NOT-RUN #29890

Open

Description

@objectstack-fleet

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

QA run record for the 18.3.0 pre-cut re-check, wave 5 card W5-FAIL: every item wave 4 judged FAIL (20 items) re-judged live and in full (every acceptance and negative clause, not only the failed ones) at one subject, plus the two release blockers: records-forms.list-view-capabilities A8 (#29173) and the #29015 path as an off-clause re-check. Item verdicts are derived from clause verdicts, never hand-assigned. Run under .claude/skills/checklist-test/SKILL.md and docs/qa/platform-checklist/RUNNER.md; a QA run, not a lane card. Warren dispatched the four wave-5 cards on 2026-11-03 08:21Z.

Result: 20/20 judged · 15 PASS / 0 PARTIAL / 5 FAIL / 0 BLOCKED / 0 NOT-RUN. No item is planned. 181 clause rows, all driven live: 169 pass, 12 fail. Wave 4 had all 20 at FAIL; 15 move to PASS. Of the 5 still failing, 2 are stale spec (one carried by an open card, one carded now) and 3 are product defects (one is the same clause still failing after its fix, two are new rows or surfaces). Blockers: #29173 A8 PASS; #29015 re-check FAIL (the fix is incomplete, confirmed by an independent verify pass, carded). One off-clause security finding was reported privately to the dispatching PM session (one line below).

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), detached; it never followed main or the Version PR chore: version packages #26517. ui/ is the same commit; the Console was built in-repo from it.
  • CI on the subject: push run 41600797636 (CI) re-read before posting: completed / success.
  • Build: pnpm install --frozen-lockfile (30 s), pnpm exec turbo run build --concurrency=2 (118/118 tasks, exit 0, 15m13s), pnpm console:build (71/72 cached plus the copy). check-dev-prereqs: 106 build artifacts present. Node v22.23.4, pnpm 10.31.0, Playwright Chromium 1243 from /opt/pw-browsers, 4 CPUs, ~15 GiB.
  • Boot, one or more per runner: a scratch copy of examples/app-showcase (tar without node_modules and .objectstack, node_modules symlinked), 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 in 16 s and served /_console/ 200 before any runner started. Ports 41710–41819. Seed: --seed-admin (admin@objectos.ai), the stock showcase seed and demo personas; members minted by invite + sign-up or admin create-user + change-password, each persona's identity proved server-side before its cells.
  • Execution: 8 item runners + 2 blocker runners + 1 independent verify runner (Agent subagents, opus, effort high), at most 3 browser scripts at once (flock semaphore) and at most 2 vitest pins at once (--maxWorkers=1). Chromium was OOM-killed a few times under the 8-boot load; every affected run was repeated, none is scored on a crashed page.
  • Hygiene: the tree stayed clean (git status empty), examples/app-showcase/.objectstack does not exist, no listener remains on any run port.
  • Time: setup 08:25–08:42Z (17 min, build-bound); testing 08:42–09:57Z (75 min wall, ~420 runner-minutes); probes 160 runner-minutes inside that.

Scope

Selector release-18.3.0-w5-fail: W5-FAIL-items.txt (computed from the ledger at main 55dca466, whose ledger equals the subject's). Every revision matches the subject:

ai.mcp-run-action-exposure-gate r6 · api-backend.rest-metadata-config-contract r9 · api-backend.error-sentences-and-unique-violation-wire-code r6 · automation.packaged-flow-clone-contract r11 · automation.setup-packaged-automation-board r11 · i18n.notification-localized-and-clears r6 · i18n.surface-matrix r13 · integration-system.email-template-render r13 · integration-system.connector-auth-kind-application r6 · platform-core.home-admin-cluster-links r6 · records-forms.list-view-capabilities r8 · records-forms.adhoc-filter-sort-builder r8 · studio-authoring.draft-publish-lifecycle r11 · studio-authoring.expression-editors r8 · studio-authoring.packaged-automation-studio-lock r7 · studio-authoring.dashboard-widget-and-filter-types r5 · studio-authoring.access-permission-set-lifecycle r7 · studio-authoring.no-entry-property-matrix r5 · ux-conventions.write-feedback r10 · ux-conventions.disabled-control-reason r4

Off-clause, not an item of this selector and not counted in the title: the #29015 re-check, recorded under studio-authoring.api-written-keys-survive-studio-save (r4).

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

Derived item verdicts

15 PASS / 0 PARTIAL / 5 FAIL / 0 BLOCKED / 0 NOT-RUN (20 of 20 judged).

Delta against wave 4 (per item, each item's own wave-4 record)

item wave-4 record w4 w5 what moved it
ai.mcp-run-action-exposure-gate #28942 FAIL FAIL N1 fail→pass (#28943 → PR #29036, af8448f: revoke_api_key listed unavailable: PERMISSION_DENIED to an osk_ caller, 36/36 unavailable entries refuse with their listed code, 0 side effects). A4 fail→fail: never re-pointed (it was parked on the anchor, which closed)
api-backend.rest-metadata-config-contract #28929 FAIL PASS A2: #28934 → #28996 (18e4ac1) + r9; describe() and clause both name 13 routes, measured 13
api-backend.error-sentences-and-unique-violation-wire-code #28929 FAIL PASS A1: re-point #28936 → #28980 (ab085a0) to HOOK_ABORTED; holds live on 2 fresh boots
automation.packaged-flow-clone-contract #29002 FAIL PASS A1, A2, A6: re-pointed r10→r11 under #28579 (#29438) to ADR-0146 D4; the new A7 (four package arms) holds
automation.setup-packaged-automation-board #29124 FAIL PASS A0, A1, A2, A3, A5, A6: re-pointed to r11 under #28579; A5 also #29139 → #29224 (role="alert" on the refusal strip)
i18n.notification-localized-and-clears #28981 FAIL PASS A1: #28982 → #29104 (692e0dc), the zh-CN review banner is translated
i18n.surface-matrix #29151 FAIL PASS N1: #29153 → #29217 (20f052d) + #29280 (6dd6a53), translated field help shows
integration-system.email-template-render #29151 FAIL FAIL A7 fail→fail: #29152 → #29250 (5cdaf9a) fixed malformed address strings; a non-string display name still answers 500 (R-2)
integration-system.connector-auth-kind-application #29151 FAIL PASS A3, N1: re-point #29154 → #29214 (22d0710) to ADR-0143 D1
platform-core.home-admin-cluster-links #28981 FAIL PASS A2: re-pointed r5→r6 under #28579 (ADR-0146 D1, #28818)
records-forms.list-view-capabilities #29172 FAIL PASS A8: #29173 → PR #29231 (e7d3ada); one aggregate POST for the selection, 200, 3/3
records-forms.adhoc-filter-sort-builder #29172 FAIL PASS A3, N2: #29174 → #29305 (62e2d07), the sign-out redirect no longer carries uf_* state
studio-authoring.draft-publish-lifecycle #29002 FAIL PASS A10: re-point #29013 → #29169 (fa31010)
studio-authoring.expression-editors #29002 FAIL FAIL A2, A4, A6, N0 fail→fail: #27482 still open, no ruling (R-3); N1 holds
studio-authoring.packaged-automation-studio-lock #29002 FAIL PASS A0 (New half), A2: re-pointed r6→r7 under #28579 (ADR-0146 D3/D4)
studio-authoring.dashboard-widget-and-filter-types #29002 FAIL PASS A4: #29006 → #29300 (3ec2647), a partly filled Options From is held, not saved
studio-authoring.access-permission-set-lifecycle #29002 FAIL PASS A3: re-pointed under #28579 (ADR-0146 D3)
studio-authoring.no-entry-property-matrix #29002 FAIL FAIL all 5 wave-4 rows now pass (#29008/#29385, #29009/#29312, #29010/#29334; titleFormat and drawerSide/drawerWidth judged as refusal rows after #29499 and #29287). A1, A2, N0 still fail on a row wave 4 recorded passing, field.sortable (R-4)
ux-conventions.write-feedback #28981 FAIL PASS A0, A1: #28984 → #29310 (bbfccbd), the autosave toasts what it saved
ux-conventions.disabled-control-reason #28981 FAIL FAIL the field inspector (#28986 → #29081) and flow node/edge inspectors (#28985 → #29206, #29203) now pass; A0–A2 fail on the All-metadata item page's default inspector, which wave 4 did not read (R-5)

Counts: FAIL→PASS 15 · FAIL→FAIL 5 (of which 2 moved surface: no-entry, disabled-control) · PASS→FAIL 0.

Reproduction rule for every fail

R-1 · ai.mcp-run-action-exposure-gate · A4 · stale spec (2/2 fresh boots)

  1. Stock boot. As admin (session): POST /api/v1/keys {"name":"k"} → key K.
  2. MCP Streamable HTTP POST /api/v1/mcp, Authorization: Bearer K: initialize, notifications/initialized.
  3. tools/call list_actions {}. Clause expects 0 sys_* actions; actual totalCount 37, 36 sys_*, all aiExposed: true, all unavailable for an osk_ caller.
  4. run_action {"actionName":"create_api_key","objectName":"sys_api_key","params":{"name":"x"}}. Clause expects 'Object "sys_api_key" is a system object and its actions are not exposed via MCP'; actual 'Action "create_api_key" on system object "sys_api_key" is not exposed via MCP: …'. Without objectName, likewise a new spelling.
  5. With the admin's session bearer, run_action revoke_api_key (confirm: true) → ok: true, key revoked: true. Clause expects a wholesale refusal.
    Rulings: 6513869839 (mcp: run_action refuses every action on a sys_* object, so an agent cannot reach the identity actions (ban_user, impersonate_user, set_user_password) that REST POST /actions serves #27681), 6523087116 ([Decision] sys_* kernel objects, assessed as one set: which of the 58 platform identity actions may an agent ever reach (plain declaration / declaration + confirmation / human-only), executing #27681 B parameter 2 in one ruling #27900), amended by 6540309323. Code: systemObjectActionExposureError (packages/runtime/src/action-execution.ts), isListableAction (packages/mcp/src/mcp-http-tools.ts).

R-2 · integration-system.email-template-render · A7 · product defect (2 fresh boots, 3 runs; dist equals source)

  1. Stock boot, admin bearer.
  2. POST /api/v1/email/send {"to":{"address":"ok@example.com","name":42},"subject":"x","text":"x"}.
    Expected 400 VALIDATION_FAILED naming the display name; actual 500 {"code":"EMAIL_SEND_FAILED","error":"a.name?.trim is not a function"} (log: rawAddressText … recordRejectedMessage). Same for cc name 42, replyTo name true, a to list entry with an object name. Every other A7 bound holds (200 admin; 401 anonymous; 400 non-object body; 403 member, foreign from, sentBy, bad headers, other organizationId, identityMail; malformed address strings 400 since fix(plugin-email): a malformed recipient address answers 400 VALIDATION_FAILED, not 500 #29250).

R-3 · studio-authoring.expression-editors · A2, A4, A6, N0 · stale spec against the #26858 ruling (3/3 fresh loads)

  1. Stock boot, admin form sign-in; a permission set on an object with a virtual formula field f_formula.
  2. Studio › Access › RLS, type the USING record.f_formula > 5.
    Clause expects the editor to refuse and hold with no PUT; actual "Valid CEL" for ~1.6 s, autosave PUT 200, then the error after the server re-read; editing to > 7 PUTs again; the engine refuses the text at publish (422 rls-predicate-unenforceable). This is the fix(app-shell): a server error at an RLS clause's path holds the permission-set autosave #27612 design (held once the server verdict on that text is read back); the Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858 ruling forbids a client copy of the publish rule. Owner: open Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482 (a ruling: re-point to "held from the server re-read on", or keep "no PUT" and fund a validate-only door). Control: the same fill holds the other probe tiers with 0 PUTs.

R-4 · studio-authoring.no-entry-property-matrix · A1, A2, N0 (the field.sortable row) · product defect (4/4 fresh loads)

  1. Writable package, object qar8_fz with nosort: {type:'text', sortable:false}, published, a few rows.
  2. GET /api/v1/meta/object/qar8_fz → fields.nosort.sortable: false; served sortability projection → fields.nosort = {sortable:true}.
  3. GET /api/v1/data/qar8_fz?sort=nosort → 200.
  4. Console list (default tab and an explicit-columns view): click the "No Sort" header → request sort=nosort; the Sort picker offers "No Sort".
    Expected: not sortable in list views (field.zod.ts, liveness grade "grid"); actual: sortable everywhere. Control: the Name header sends sort=name.

R-5 · ux-conventions.disabled-control-reason · A0, A1, A2 · product defect (3/3 and 2/2 fresh loads)

  1. Stock boot, admin form sign-in.
  2. Open /_console/studio/~all/metadata/object/showcase_task or /_console/studio/~all/metadata/flow/showcase_task_completed (banner: "This item is locked and cannot be edited or deleted.").
  3. With nothing selected, the Properties panel's controls are disabled (object: Name, Label, Plural label, Icon, Description, Exposure posture, Required capabilities; flow: button#mdf-type).
    Expected: tooltip on hover, a Tab-reachable wrapper with aria-describedby carrying the reason; actual: none of the three. Control: a writable item renders the panel enabled; the node inspector on the same page explains itself since fix(app-shell): the metadata editor page tells its scoped inspector why it is read-only #29203.

Release blockers

Off-clause probes (RULE 8)

160 runner-minutes over all 20 items (2–10 min each), as admin, members, demo personas and anonymous; boundary classes empty string, missing/unknown id, wrong type, posture opposites (osk_ vs session bearer; member vs admin) and readers outside the asserted scope. Findings that passed the filing gate are extracted (close-out): the per-object batch door's row code for an undeclared hook refusal (beside api-backend.error-sentences-and-unique-violation-wire-code) and a mis-cased sys_user.locale (beside i18n.notification-localized-and-clears). Kept in notes, not carded: the clone refusal "is not shipped by a package" for a copy already in the .custom pair (wording only, the refusal is right); the send door keeping a CRLF subject while refusing multi-line custom headers (admin-only door, transports fold header line breaks; hardening note); the send door accepting unknown body keys; a member reaching the Setup Applications page by typed URL (UI-only, server refuses the writes, same family as #28988 and parked upstream as objectui#12706).

  • records-forms.adhoc-filter-sort-builder · observed beside the clauses · security finding — detail withheld, reported to PM

Fixture gaps and partial coverage

  • studio-authoring.no-entry-property-matrix: knownGaps[1] keys not judgeable at the subject (mailAssociation, ackPlaintextMasking, lifecycle.ttl, dimensions) were not run, as the item says; form defaults measured on the named form page only (knownGaps[0]). The item verdict is FAIL on R-4 regardless.
  • i18n.surface-matrix A5 confirm half: pin-only on stock as the item says (2 ui pins, 7/7).
  • objectui live e2e pins for list-view/adhoc were run from a byte-identical scratch copy of ui/e2e/live; one case neither item cites ("Workbench: grid renders data columns even when the source view is hollow") failed 2/2 under memory pressure and was not investigated.

Checklist-accuracy findings for #27377

  • records-forms.list-view-capabilities A8 / steps[8] (and records-forms.bulk-select-all-matching steps[5]): still name POST /api/v1/apps/showcase/recalc with top-level _selectedIds; the console posts to the action door {params:{_selectedIds}} since Console: send a same-origin type: 'api' action click through POST /actions/:object/:action instead of fetching its target directly (ruling on #27506, step 3 of 3) #27611 (the door forwards flat). The re-point parked in wave 4 "once action door: a record-less aggregate api action that declares recordIdParam is refused 400 ("identifies its record by id"), so bulk "Recalculate Selection" never runs #29173 lands" is now owed.
  • RUNNER environment fact "view is overlay-allowed, a view PUT answers 2xx on stock showcase with no ?package=" no longer holds (ADR-0146 D11): a bare view/email_template PUT answers 403 NOT_OVERRIDABLE; the working recipe is POST /packages/com.example.showcase/ensure-customization, then PUT ?package=com.example.showcase.custom with overrides:{package,basedOn}. Affects list-view N0/N1 staging and email-template-render's admin-edit steps.
  • ai.mcp-run-action-exposure-gate: A1.verify "the snapshot is the one exposed action" is stale (entries marked unavailable are part of the contract); step 6 should name a non-exposed action.
  • api-backend.error-sentences-and-unique-violation-wire-code step 0 offers /data/:object/batch as an equivalent door; it cannot answer the 400 the clause asserts.
  • integration-system.connector-auth-kind-application step 1: outputVariable is retired, bind with a flow variable; a reflecting echo writes secrets into flow run output, so runners should not grep run records.
  • integration-system.email-template-render: steps 9(c) should add a non-string display-name probe; each rejected send writes a sys_email row failed; the "no locale" branch is unreachable from the HTTP door (notify gives the deployment default en); the by-name read ignores ?locale=.
  • automation.packaged-flow-clone-contract: the dropped-key list is 11 (overrides added); the "In com.example.showcase.custom" text is on the designer header, the list row carries an "Added" mark (Studio: an item header keeps the item name readable; chips and tools collapse first #29693).
  • automation.setup-packaged-automation-board: switch text is "Switched on/off for this deployment"; a copy opens on the base pillar's URL.
  • studio-authoring.packaged-automation-studio-lock step 3: the New dialog names no target, so it is provable only by creating a flow.
  • platform-core.home-admin-cluster-links: an extra app-id canonicalisation hop from /apps/setup; "Keyboard shortcuts" is in the Help menu only inside an app.
  • i18n.notification-localized-and-clears steps[4]: a member's popover has no Activity tab. i18n.surface-matrix: automated.ref still says --maxWorkers=2; no field-help variant (N1 reached only incidentally); knownGaps[3] (live confirm specimen) is stale.
  • records-forms.adhoc-filter-sort-builder: steps[5], A3 and N2 should name the account-menu sign-out path; A2's verify reads filter and sort but not search.
  • studio-authoring.dashboard-widget-and-filter-types A4 / steps[5]: add the wave-4 shape (Field filled, Options From begun) as its own case; sankey with two dimensions draws no root node.
  • studio-authoring.draft-publish-lifecycle A3: GET ?mode=draft answers 400; the no-overwrite proof is the next publish serving REV2.
  • studio-authoring.no-entry-property-matrix: knownGaps[0] still says lockedBy "is never shown" (stale since fix(plugin-list): a list view locked by lockedBy alone shows its lock indicator #29312); the sortable variant line should name its observable (header and Sort picker); compactToolbar/showRecordCount/resizable probe only at a non-default value; modalSize acts only on the isDefault form; conditionalFormatting is stored as a CEL envelope, so A0's byte-equal compare must accept it.
  • studio-authoring.expression-editors fixture: a new object needs sharingModel or publish refuses security-owd-unset.
  • ux-conventions.disabled-control-reason: knownGaps[0] stale (Archive declares its own reason, record_header placement only); the automation Enabled toggle is enabled on read-only packages; variants 4 and 5 have no stock instance; variants[2] should name the ~all item page.
  • ux-conventions.write-feedback: the Invite text is the outcomeMessages.link_only copy; the per-group approval stays pending after one approval (read sys_approval_action).
  • studio-authoring.access-permission-set-lifecycle: the same manage_metadata refusal answers PERMISSION_DENIED on clone and FORBIDDEN on the metadata PUT.
  • fix(app-shell): Studio opens no buffer over a dashboard draft the server refuses to read #29132's test StudioDesignSurface.draftUnreadable-29015.test.tsx pre-registers the dashboard preview, so it cannot reach the ordering in R-6.
  • Environment: a fresh DB shows a "Set the default currency" / timezone prompt over the admin's first page (workspace-timezone-prompt-decline).

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbZtZMhQkARKJtViA9U

    Per-clause verdicts (1/3): ai · api-backend · automation · i18n.notification. w4 is the clause's verdict in the item's wave-4 record (named per item); w5 is this run's verdict, every clause driven live at 589049b0 (pins were run as well where an item has one, and score only the layer they drive).

    ai.mcp-run-action-exposure-gate (wave 4: #28942)

    item · rev clause w4 w5 layer · text evidence
    ai.mcp-run-action-exposure-gate · r6 A0 pass pass HTTP-live · MCP /api/v1/mcp, admin osk_ key: run_action showcase_mark_done (with and without objectName) -› isError "Action 'showcase_mark_done' on 'showcase_task' is not exposed to AI — the app author must opt it in with ai: { exposed: true, description: … }"; REST re-read status/done/progress/updated_at unchanged
    ai.mcp-run-action-exposure-gate · r6 A1 pass pass HTTP-live · list_actions {} (objectName -› -32602 Unrecognized key(s) objectName): snapshot present, mark_done absent, all 37 aiExposed:true. Staged qa_ai_gated (manage_metadata): admin total 38 incl. it, Mei (osk_) 37 without it, snapshot in both; teardown DELETE 200 / GET 404
    ai.mcp-run-action-exposure-gate · r6 A2 pass pass HTTP-live · run_action showcase_portfolio_snapshot -› {ok:true, scope:'global', accounts:14, projects:5, invoices:12}; REST totals showcase_account 14, showcase_project 5, showcase_invoice 12
    ai.mcp-run-action-exposure-gate · r6 A3 pass pass HTTP-live (server log) · boot.log: "[action-audit] MCP run_action 'showcase_portfolio_snapshot' on 'global' — body executes TRUSTED (system-elevated context, RLS/FLS-bypassing) for user '4Ko7wf94…'" = admin's user id
    ai.mcp-run-action-exposure-gate · r6 A4 fail fail HTTP-live · stale spec: sys_* is not refused wholesale. list_actions lists 36 sys_* actions (all aiExposed); a session caller ran revoke_api_key ok. Non-exposed create_api_key is refused, but in the new spellings, not either quoted one. 0 rows created. 2/2 fresh boots
    ai.mcp-run-action-exposure-gate · r6 N0 pass pass HTTP-live · mark_done refusal left task unchanged; create_api_key refusals: sys_api_key rows 3 -› 3; all 36 unavailable actions run under osk_ refused with 0 change to sys_user/api_key/organization/member/invitation/team/team_member/oauth_application/sso_provider counts; Mei's refused qa_ai_gated left progress 100 -› 100
    ai.mcp-run-action-exposure-gate · r6 N1 fail pass HTTP-live · osk_ caller: revoke_api_key now listed with unavailable {code: PERMISSION_DENIED, reason 'Changing an API key requires a signed-in session…'}; run_action confirm:true -› 403 PERMISSION_DENIED with the same sentence, key unrevoked. Sweep: 36/36 unavailable entries refused with their listed code; qa_ai_gated hidden from Mei and refused for her; session control lists revoke runnable and revokes ok

    api-backend.rest-metadata-config-contract (wave 4: #28929)

    item · rev clause w4 w5 layer · text evidence
    api-backend.rest-metadata-config-contract · r9 A0 pass pass HTTP-live · live :41750 admin: /meta, /meta/types, /meta/diagnostics, /meta/_drafts, /meta/object, /meta/object/showcase_task (+/references, /layers), /meta/book/showcase_manual/tree all 200, none equal to control 404 ENDPOINT_NOT_FOUND body; pin rest-config-contract-live (in-process HTTP) in 14/14 passed
    api-backend.rest-metadata-config-contract · r9 A1 pass pass plugin-unit (real RestServer) · oracle test: rest-config-contract-harness + discovery-services-route-follows-mount pass; own scratch enumeration off packages/rest/dist: prefix '/metadata' -› 0 routes under /meta, 20 under /metadata = default 20 re-based; live default discovery routes.metadata=/api/v1/meta
    api-backend.rest-metadata-config-contract · r9 A2 fail pass plugin-unit (real RestServer route table) · own enumeration (baseline 79) and rest-config-mount-table.pin (in 154/154 over 4 rest files): types -2 (GET /meta, /meta/types), items -1 (GET /meta/:type), maintenance -3 (incl POST _migrate-stored), item -13 incl GET /meta/app/:appId/bundle; 0 grown each; describe() now names the bundle
    api-backend.rest-metadata-config-contract · r9 A3 pass pass HTTP-live · live: client_liaison member (invite+sign-up, sys_user_position bound) /meta/object/showcase_project 20 fields vs admin 23; missing exactly budget, spent, budget_remaining; no budget/spent text in body (account.relatedListColumns also drops budget); meta-object-fls + §D maskObjectFields:false kept pass
    api-backend.rest-metadata-config-contract · r9 A4 pass pass plugin-unit (real RestServer) · oracle test: rest-config-contract-harness passed (14/14 with live file): enableCache true -› getMetaItemCached, false -› getMetaItem; carve-outs app, dashboard, ?state=draft, ?preview=draft, ?package=, ?member=, audience-gated (book, doc) each call getMetaItem
    api-backend.rest-metadata-config-contract · r9 A5 pass pass plugin-unit (real RestServer) · oracle test: harness 'cacheTtl (3600, 0, -1) and endpoints.schema refuse with the schema and the replacement' passed; §E in rest-sub-config-parse-not-cast passed
    api-backend.rest-metadata-config-contract · r9 N0 pass pass HTTP-live · liaison's served showcase_project schema carries none of the three withheld field names (also /layers, /published, ?package=, ?preview=draft reads: 0 budget hits as liaison vs 11-22 for admin)
    api-backend.rest-metadata-config-contract · r9 N1 pass pass HTTP-live · masking scored from the liaison's document (session confirmed liaison.r3@example.com); admin copy used as control only
    api-backend.rest-metadata-config-contract · r9 N2 pass pass plugin-unit (real RestServer) · each switch=false table lacks every route of its radius (own enumeration + mount-table pin both directions); non-default config not live-authorable by ruling #15543 (knownGaps)

    api-backend.error-sentences-and-unique-violation-wire-code (wave 4: #28929)

    item · rev clause w4 w5 layer · text evidence
    api-backend.error-sentences-and-unique-violation-wire-code · r6 A0 pass pass plugin-unit + engine (+ HTTP-in-process) · at 589049b, maxWorkers=1: @objectstack/rest rest-write-route-hook-refusal-sentence.ledger 601 passed; rest-hook-script-fault-envelope 18 passed; @objectstack/runtime dispatcher-unique-violation-wire-code 7 passed; @objectstack/service-storage storage-routes.server-fault-withheld 18 passed; dogfood error-sentences-live 4 passed; all rc=0
    api-backend.error-sentences-and-unique-violation-wire-code · r6 A1 fail pass HTTP-live · scratch showcase copy with a sandboxed beforeInsert hook throwing a sentence, no code: POST /data/showcase_qa_errbudget and /createMany -› 400 {error:‹sentence verbatim›, code:'HOOK_ABORTED', object}; 0 rows written; 2/2 fresh boots. Nested-envelope rows HOOK_ABORTED_400 via ledger pin (pin-only, as the clause says)
    api-backend.error-sentences-and-unique-violation-wire-code · r6 A2 pass pass HTTP-live · declarative update action patching a taken unique email: POST /actions/showcase_qa_errcontact/showcase_qa_take_email -› 409 error.code UNIQUE_VIOLATION, details keys exactly [developerMessage, object], details.object showcase_qa_errcontact; the row keeps free.r3@…; 2/2 boots
    api-backend.error-sentences-and-unique-violation-wire-code · r6 A3 pass pass HTTP-live · OS_STORAGE_LOCAL_ROOT under a regular file: POST /storage/upload/chunked -› 500 {code:'INTERNAL', message:'Internal error'}. Body has no ENOTDIR, path, 'storage-root', mkdir or stack; server log carries the ENOTDIR mkdir fault; 2/2 boots

    automation.packaged-flow-clone-contract (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    automation.packaged-flow-clone-contract · r11 A0 pass pass HTTP-live · POST …/showcase_urgent_task_alert/clone {} → 400 VALIDATION_FAILED details.fields=[{name,required}] (one); {name:x} → [{label,required}]; {label:x} → [{name,required}]; {name:'',label:'Empty'} → [{name,invalid_type}]; {name:qa_x,label:''} → [{label,invalid_type}]; unknown key foo → 400 unknown_field; GET qa_x/x → 404
    automation.packaged-flow-clone-contract · r11 A1 fail (stale spec) pass HTTP-live · clone 200; data.flow vs source differs on exactly name, label, status active→obsolete, plus _provenance/_packageId/_packageVersion absent; stored sys_metadata row body keys name,label,description,version,status,type,nodes,edges,runAs (0 of the 11 dropped keys), package_id com.example.showcase.custom; data.packageId 'com.example.showcase.custom'; no clonedFrom/basedOn anywhere
    automation.packaged-flow-clone-contract · r11 A2 fail (stale spec) pass HTTP-live · 3 urgent showcase_task creates (201): base runs 0→1→2→3, copy runs 0→0→0→0; _status row qa_urgent_alert_clone enabled:false bound:false status:obsolete; notice === FLOW_CLONE_NOTICE verbatim ('The copy is switched off and does not run until you switch it on, and it still uses the same subflows, actions and objects as the original.'), noticeCode FLOW_CLONE_DISABLED_COPY
    automation.packaged-flow-clone-contract · r11 A3 pass pass HTTP-live · same name → 409 RESOURCE_CONFLICT '…Same-name clones are refused on purpose: the automation engine keys flows by bare name … (for example 'showcase_urgent_task_alert_copy')'; does_not_exist → 404 RESOURCE_NOT_FOUND "Flow 'does_not_exist' not found"; 'Bad-Name!' → 400 VALIDATION_FAILED 'Invalid flow definition: name: … must match pattern /^[a-z_][a-z0-9_]*$/' (invalid_format)
    automation.packaged-flow-clone-contract · r11 A4 pass pass HTTP-live · clone of showcase_task_done_notify_owner → qa_task_done_clone 200 (packageId com.example.showcase.custom); subflow node call_notify config.flowName 'showcase_notify_owner' in the response and on the stored GET
    automation.packaged-flow-clone-contract · r11 A5 pass pass HTTP-live · PUT /api/v1/automation/qa_urgent_alert_clone (own definition, label '(edited)') → 200; re-read label 'QA urgent alert clone (edited)', status obsolete, _packageId com.example.showcase.custom
    automation.packaged-flow-clone-contract · r11 A6 fail (stale spec) pass browser-live · cold restart same DB (listener pid 16099→28654): GET clone 200 label '(edited)' status obsolete; GET /meta/flow holds qa_* under com.example.showcase.custom; post-restart urgent create: base 3→4, copy 0; Studio rail lists 'QA urgent alert clone (edited) · Switched off' with Added mark (title '…customization package com.example.showcase.custom'), opens heading + chip 'In com.example.showcase.custom'; ~org: 'No flows outside a package…'
    automation.packaged-flow-clone-contract · r11 A7 pass pass HTTP-live · package '' → 400 VALIDATION_FAILED [{package,invalid_type}]; 'qa_no_such_pkg' → 422 WRITABLE_PACKAGE_REQUIRED [new_similar_target_not_installed]…'Nothing was written.'; 'com.example.showcase' → 422 [new_similar_target_read_only]; scratch com.example.qa-r4-scratch (writable, no deps) → 409 RESOURCE_CONFLICT [new_similar_target_outside_closure]; GET qa_x → 404 after each
    automation.packaged-flow-clone-contract · r11 N0 pass pass HTTP-live · no facet missing: description, version, type, nodes, edges, runAs deep-equal source on response and stored row
    automation.packaged-flow-clone-contract · r11 N1 pass pass HTTP-live · no clonedFrom/cloned_from/basedOn key in clone response, stored GET, sys_metadata row, /meta/flow listing, or post-restart read
    automation.packaged-flow-clone-contract · r11 N2 pass pass HTTP-live · same-name clone → 409 RESOURCE_CONFLICT; a name held by a pair flow (qa_urgent_alert_clone) → 409 too; never 2xx
    automation.packaged-flow-clone-contract · r11 N3 pass pass HTTP-live · anonymous clone → 401 UNAUTHENTICATED; auditor.demo@example.com (session proven) → 403 PERMISSION_DENIED 'Authoring automation flows requires the manage_metadata capability.'; GET qa_anon/qa_aud → 404
    automation.packaged-flow-clone-contract · r11 N4 pass pass browser-live · every copy row package_id com.example.showcase.custom (never null); /_console/studio/~org/automations reads 'No flows outside a package. To change a packaged flow, open its package and use New similar.'

    automation.setup-packaged-automation-board (wave 4: #29124)

    item · rev clause w4 w5 layer · text evidence
    automation.setup-packaged-automation-board · r11 A0 fail pass HTTP-live+browser-live · GET /meta/app?id=setup: {id nav_packaged_actions,type component,componentRef automation:packaged-actions,label 'Packaged Actions'}, no requiresService/requiredPermissions; nav_packaged_automation 0, 'automation:packaged' 0. Sidebar click -› /component/automation/packaged-actions (heading 'Packaged actions'); bookmark -› /studio/~all/metadata/flow; ?package=com.example.showcase -› /studio/com.example.showcase/automations?package=... (query kept)
    automation.setup-packaged-automation-board · r11 A1 fail pass browser-live+HTTP-live · rail 32 auto-rail-row-* = _status 32 = meta/flow 32 (_packageId com.example.showcase), none missing/extra; opened all 32: aria-checked == _status.enabled 32/32; readings 29 on, 2 not-running (scheduled_digest, task_due_reminder, reason 'disabled by deployment policy'), 1 off (approver_bindings). Copy from A6 showed in rail with 'Added…by its customization package com.example.showcase.custom' mark + header chip 'In com.example.showcase.custom'
    automation.setup-packaged-automation-board · r11 A2 fail pass browser-live+HTTP-live · auto-ledger-switch on showcase_urgent_task_alert -› POST …/toggle {enabled:false} 200 (only write); after full reload aria-checked=false data-reading=off 'Switched off for this deployment'; sys_metadata_activation row {flow, showcase_urgent_task_alert, com.example.showcase, active:false}; _status enabled:false; flipped back -› 200, row active:true
    automation.setup-packaged-automation-board · r11 A3 fail pass browser-live · flow ⋯ (auto-item-more) menu = ['New similar'], auto-item-new-similar count 1; packaged-actions-section: 141 rows, 141 switches, 0 clone/copy/new-similar/duplicate buttons or text
    automation.setup-packaged-automation-board · r11 A4 pass pass browser-live · with copy qa_r5_copy_r1 present in com.example.showcase.custom: copy page, base flow page and Packaged actions section carry 0 lineage/drift/diff/cloned/ancestor testids and no clonedFrom/copied-from/diff/drift text (only 'original' inside the door's notice sentence); pair mark/chip only, as the clause allows
    automation.setup-packaged-automation-board · r11 A5 fail pass browser-live+HTTP-live · admin: switch on showcase_notify_owner -› POST toggle 409 DELETE_RESTRICTED; [data-testid=studio-refusal-message] role=alert, text == server message verbatim naming showcase_task_done_notify_owner; getByRole(alert)=1; 'Could not change activation' 0; switch stays checked. Member (scored over API, member reaches neither door): toggle 403 PERMISSION_DENIED 'Enabling or disabling an automation flow requires the manage_metadata capability.' (exact)
    automation.setup-packaged-automation-board · r11 A6 fail pass browser-live+HTTP-live · dialog prefilled showcase_urgent_task_alert_copy / '… (copy)'; submit disabled for name empty, label empty, both empty (and whitespace-only); Bad-Name! -› POST …/clone 400 VALIDATION_FAILED, auto-new-similar-refusal role=alert 'Invalid flow definition: name: Invalid string: must match pattern /^[a-z_][a-z0-9_]*$/', dialog open with names kept; legal copy (no package sent) 200, strip 'Saved the copy qa_r5_copy_r1 in package com.example.showcase.custom.' + notice verbatim; copy status obsolete, definition switch 'Switched off'; deleted after
    automation.setup-packaged-automation-board · r11 A7 pass pass browser-live+HTTP-live · posture recorded (member minted invite-first, sys_member role member, fresh context, form sign-in cookie+token, get-session = member): Setup packaged-actions -› 'You don't have access to this app' (GET /meta/app/com.objectstack.setup 403); Studio -› 'You need Studio access to open Studio'; 0 switches either; sys_metadata_activation read 403 PERMISSION_DENIED; _status 200; /meta/action 200; /meta/flow 200 — same posture as #29124
    automation.setup-packaged-automation-board · r11 N0 pass pass HTTP-live+browser-live · nav clause not scored off a typed URL: served GET /meta/app?id=setup body carries nav_packaged_actions (automation:packaged-actions) AND a real sidebar click on 'Packaged Actions' navigated to /component/automation/packaged-actions (packaged-actions-section rendered); bookmark legs recorded separately
    automation.setup-packaged-automation-board · r11 N1 pass pass browser-live+HTTP-live · member cell in a fresh browser context, own form sign-in (cookie+token), GET /auth/get-session = qa.r5.member@example.com; toggle sent from that page with the member's own token+cookie -› 403 PERMISSION_DENIED manage_metadata sentence (not the admin's 409)
    automation.setup-packaged-automation-board · r11 N2 pass pass browser-live+HTTP-live · after flip OFF + full reload: switch aria-checked=false/data-reading off AND sys_metadata_activation row active:false AND _status enabled:false; after flip back ON: switch on AND row active:true AND _status enabled:true — no mismatch either way
    automation.setup-packaged-automation-board · r11 N3 pass pass HTTP-live+browser-live · served setup nav: 'automation:packaged' 0, nav_packaged_automation 0; rendered Setup sidebar 'Packaged Automation' text 0; Setup Packaged actions page has 0 packaged-flow-* rows (actions only, 141 rows)

    i18n.notification-localized-and-clears (wave 4: #28981)

    item · rev clause w4 w5 layer · text evidence
    i18n.notification-localized-and-clears · r6 A0 pass pass browser-live · flow notify_assignee cfg.title 'New task assigned: { { record.title }}' (inline, no template). zh recipient (sys_user.locale zh-CN via switcher) bell popover 消息中心 tab 通知 1: row 'New task assigned: Design system' / 'You have been assigned "Design system".' / 10秒钟前; rendered, no raw { { }} (3/3 runs)
    i18n.notification-localized-and-clears · r6 A1 fail pass browser-live · click-through -› /_console/apps/com.example.showcase/showcase_task/record/reMCi2SelaviCzWW (in_review): record:alert reads 待评审 / 此任务正在评审中——请先确认工作成果,再标记为完成。; breadcrumb 任务, stages 待规划…已完成, actions 标记为完成/记录工时/归档, sections 概览/排期/详细信息, fields zh; only record values English (2/2 fresh zh loads)
    i18n.notification-localized-and-clears · r6 A2 pass pass browser-live · row click fired POST /api/v1/notifications/read {"ids":["oK79VvROVhsWXL3x"]} -› 200 {success:true,data:{readCount:1}} on the live server; GET /api/v1/notifications unreadCount 1 before -› 0 after (repeated XK8nPH1jGhZqVe9W, en KYNpUhnorBX0sk4M)
    i18n.notification-localized-and-clears · r6 A3 pass pass HTTP-live · after page.reload GET /api/v1/notifications unreadCount 0; rows oK79VvROVhsWXL3x read:true (createdAt 08:54:08.751Z), no newer unread rows
    i18n.notification-localized-and-clears · r6 A4 pass pass browser-live · same task reassigned to en control (sys_user.locale null, English console 'Open inbox', tab 'Notifications 1'): row 'New task assigned: Design system' / 'You have been assigned "Design system".' — identical to zh recipient's; producer type task.assigned (flow notify node)
    i18n.notification-localized-and-clears · r6 N0 pass pass HTTP-live · mark-as-read answered 200 on the served Hono route (not 404/405)
    i18n.notification-localized-and-clears · r6 N1 pass pass browser-live · English inline title on the zh session observed and not filed — the contract (inline title, no template)
    i18n.notification-localized-and-clears · r6 N2 pass pass HTTP-live · PUT /api/v1/meta/translation/r6_notif_zh {locale:'zh-CN',objects…,notifications:{…}} -› 422 INVALID_METADATA 'Unrecognized key(s) on this translation: notifications … retired object-first dialect'; GET after -› 404 (nothing stored)

    i18n.surface-matrix (wave 4: #29151)

    item · rev clause w4 w5 layer · text evidence
    i18n.surface-matrix · r13 A0 pass pass browser-live · admin switched via avatar menu (PATCH sys_user {locale:'zh-CN'}); per variant: nav 能力地图/任务/紧急任务/图表画廊; list 标题·项目·负责人·状态·优先级·截止日期·进度·操作; options 评审中/高/紧急; New form 新建任务/任务信息/标题…截止日期; action 重算工时+toast; urgent emptyState 暂无紧急任务+message; sections 概览/排期/详细信息, 联系方式/工作信息/状态/备注, 这是谁; widget 任务数与平均进度; pages 连接智能体+subtitle, CC 活跃项目…总预算; settings 本地化/区域/财务/badge 组织; Studio 基础信息/访问控制/记录共享模型(OWD); sys_user 用户/全部用户·3; dates 后天/14天前/10分钟前; feed (A10)
    i18n.surface-matrix · r13 A1 pass pass HTTP-live · GET /api/v1/i18n/translations/zh-CN 200 read before the browser (4206 leaves); expectations cited from it: objects.showcase_task._views.urgent.emptyState, _sections.{task,overview,schedule,details}, showcase_contact._sections.*, _actions.showcase_recalc_estimate, dashboards.showcase_chart_gallery.widgets.combo_count_vs_progress.title, pages.connect_agent, pages.showcase_command_center.components.cc_k1..6, settingsCommon.sourceLabels
    i18n.surface-matrix · r13 A2 pass pass browser-live · untranslated (absent from zh tree) render source: account Edit 'Sales Region','Customer Since','Loyalty Tier','LinkedIn URL','CSAT Score', options Technology/Active/AMER/Bronze; task description 'A unit of work inside a project.'; dashboard title 'Chart Gallery', widgets 'Total Tasks'/'Tasks by Status'; no dotted literal, no empty cell
    i18n.surface-matrix · r13 A3 pass pass browser-live · same stored values: list cells 已完成/待办/待规划/评审中/进行中 + 中/高/紧急; New-form selects default 待规划 / 中; list filter 筛选→添加筛选→状态→选择值 options [待规划,待办,进行中,评审中,已完成]
    i18n.surface-matrix · r13 A4 pass pass browser-live · showcase_task list header row: 标题 · 项目 · 负责人 · 状态 · 优先级 · 截止日期 · 进度 · 操作 — 100% zh-CN (screenshot read)
    i18n.surface-matrix · r13 A5 pass pass browser-live + component-jsdom · live: task record → 重算工时 → POST /api/v1/actions/showcase_task/showcase_recalc_estimate 200 at +274ms, toast '已重算Design system的工时。' at +376ms. Confirm half pin-only on stock (knownGaps[3]): ui pins page-header-action-i18n + record-quick-actions.actionText-i18n at 589049b → 2 files, 7/7 passed (--maxWorkers=1)
    i18n.surface-matrix · r13 A6 pass pass HTTP-live · GET /api/v1/i18n/locales 200 {"success":true,"data":{"locales":[{"code":"en","label":"en","isDefault":true},{"code":"zh-CN","label":"zh-CN","isDefault":false}]}} = supportedLocales ['en','zh-CN'], exactly two
    i18n.surface-matrix · r13 A7 pass pass HTTP-live · /i18n/translations/zh-CN non-empty (4206 leaves), 14 groups served (objects, apps, dashboards, datasets, pages, globalActions, reports, flows, positions, permissions, books, messages, settingsCommon, settings) ⊂ ServedTranslationDataSchema's 16; safeParse (spec dist @589049b0) success, 4206 leaves in = out; 0 dotted-key-shaped values
    i18n.surface-matrix · r13 A8 pass pass HTTP-live + browser-live · /i18n/labels/showcase_task/zh-CN → title 标题, project 项目, assignee 负责人, status 状态, priority 优先级, due_date 截止日期, progress 进度 = the 7 rendered list headers one-to-one
    i18n.surface-matrix · r13 A9 pass pass HTTP-live · /i18n/translations/zz-ZZ → 200 {"success":true,"data":{"locale":"zz-ZZ","translations":{}}}; /i18n/labels/showcase_task/zz-ZZ → 200 {"success":true,"data":{"object":"showcase_task","locale":"zz-ZZ","labels":{}}}
    i18n.surface-matrix · r13 A10 pass pass browser-live + HTTP-live · row written this boot before any switch (admin locale null, no Accept-Language: PATCH showcase_task yMHhbgGfTm3lhijB progress 42 → sys_activity RemLExTXxJUD8X-C); en reader home 'Dev Admin Updated Task "Design system": Progress'; zh reader home 'Dev Admin 更新了 任务 "Design system":进度'; GET /data/sys_activity/RemLExTXxJUD8X-C zh-CN → 更新了 任务…, en/none → Updated Task…
    i18n.surface-matrix · r13 N0 pass pass browser-live · dotted-key regex over full body text of 14 zh-CN surfaces (task list/New/record, chart gallery, command center, connect_agent, sys_user, settings localization, home, Studio, contact/account record, capability map, project list) + Edit/New dialogs → 0 hits
    i18n.surface-matrix · r13 N1 fail pass browser-live + HTTP-live · showcase_contact Edit (zh) lead_score help line '由评分规则计算,不可手动编辑。'; served GET /meta/object/showcase_contact Accept-Language zh-CN → inlineHelpText '由评分规则计算,不可手动编辑。' (en → 'Computed by scoring rules — not user-editable.'); all addressable _sections render translated
    i18n.surface-matrix · r13 N2 pass pass HTTP-live · /i18n/locales answers {success, data:{locales}} — not a bare array (A6 body)
    i18n.surface-matrix · r13 N3 pass pass HTTP-live + browser-live · /labels/showcase_task/zh-CN and rendered header agree 7/7 (A8)
  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbZtZMhQkARKJtViA9U

    Per-clause verdicts (2/3): i18n.surface-matrix · integration-system · platform-core · records-forms. w4 is the clause's verdict in the item's wave-4 record (named per item); w5 is this run's verdict, every clause driven live at 589049b0 (pins were run as well where an item has one, and score only the layer they drive).

    integration-system.email-template-render (wave 4: #29151)

    item · rev clause w4 w5 layer · text evidence
    integration-system.email-template-render · r13 A0 pass pass HTTP-live · GET /meta/email_template/showcase_task_done_email 200 (registry, _provenance package); sys_email_template total 0; api flow qa_mail_full {title:'QA Full Task',project:'Apollo'} → sys_email subject '✅ Task done: QA Full Task', body_text 'The task QA Full Task on project Apollo was marked done.', body_html substituted, status sent, 0 '{ {' residue
    integration-system.email-template-render · r13 A1 pass pass HTTP-live · qa_mail_notitle → sys_notification_delivery channel email status 'dead' error 'MISSING_VARIABLES: title' attempts 1, no sys_email row; qa_mail_noproj → sent, subject '✅ Task done: QA NoProj', body_text 'The task QA NoProj on project was marked done.'
    integration-system.email-template-render · r13 A2 pass pass HTTP-live · PUT /meta/email_template/qa_inactive_tpl (active:false) 200; qa_mail_inactive → delivery status 'dead' error 'TEMPLATE_INACTIVE: qa_inactive_tpl', no sys_email row
    integration-system.email-template-render · r13 A3 pass pass HTTP-live · zh-CN member saved into com.example.showcase.custom; one send to 4 recipients: zh-CN → '✅ 任务完成:QA I18n Three'; zh → '✅ 任务完成:QA I18n Three' (variant expansion); ja-JP → '✅ Task done: QA I18n Three' (fallback en→en-US); admin (no locale) → en-US. zh-CN-only bundle: zh → '仅中文 Z1', ja-JP → dead 'TEMPLATE_NOT_FOUND: qa_zhonly_tpl (locale=ja-JP)'
    integration-system.email-template-render · r13 A4 pass pass HTTP-live · qa_from_tpl {fromOverride:{name:'QA Sender',address:'qa-sender@example.com'}, replyTo:'qa-reply@example.com', bodyHtml only} → sys_email from 'QA Sender ‹qa-sender@example.com›', reply_to 'qa-reply@example.com', body_text 'Hello FromT world' derived; GET item has no bodyText key; string fromOverride → 422 INVALID_METADATA 'expected object, received string'
    integration-system.email-template-render · r13 A5 pass pass HTTP-live · declared half on writable scratch copy runs/R4/email/app: source subject → 'SRC-EDIT-1 …', os compile --output COPY/dist/objectstack.json, listener PID 16216 unchanged → by-name + list + next send 'SRC-EDIT-1 Task done: srcone'; repeated SRC-EDIT-2, same PID. Admin half: en-US override saved into com.example.showcase.custom (overrides {package,basedOn:'0.1.0'}) → send 'ADMIN-EDIT Task finished: adminnow'; cold reboot (source moved to SRC-EDIT-3, artifact recompiled) → by-name/list/send 'ADMIN-EDIT …afterboot'
    integration-system.email-template-render · r13 A6 pass pass build · EmailTemplateDefinitionSchema (spec dist): {content} → bodyHtml invalid_type + unrecognized_keys 'Unrecognized key(s) on this email template: content. Did you mean content → bodyHtml?'; {body} → unrecognized_keys naming body (no prescription; not an alias) + bodyHtml required; name 'BadName' → invalid_format 'name must be dotted snake_case'
    integration-system.email-template-render · r13 A7 fail fail HTTP-live · every bound held (admin 200 top-level {id,status:'sent',messageId}; row from configured sender, sent_by admin, org = active org; anon 401; string body 400 INVALID_REQUEST; bad to/cc/bcc/replyTo strings 400 VALIDATION_FAILED; member 403; from/sentBy/headers/org/identityMail 403, sys_email 41→41 and sys_identity_email 0→0) BUT to {address:'ok@example.com',name:42} → 500 EMAIL_SEND_FAILED 'a.name?.trim is not a function'
    integration-system.email-template-render · r13 N0 pass pass HTTP-live · after each declared-source reload and each admin override (incl. after cold reboot) the by-name read, the list read and the next sys_email render agreed (SRC-EDIT-1, SRC-EDIT-2, ADMIN-EDIT ×2)
    integration-system.email-template-render · r13 N1 pass pass HTTP-live · all 45 sys_email rows scanned: 0 carry '{ {' or '}}'; undeclared { {nope}}, spaced { { who }}, { {deep.path}} → 'Hole [] [‹b›W‹/b›] [D]' (html escaped in bodyHtml); triple-brace hole refused at save 422 email-template-hole-placement
    integration-system.email-template-render · r13 N2 pass pass HTTP-live · POST /api/v1/email/send {to,subject,bodyHtml} → 400 {code:'VALIDATION_FAILED',error:'at least one of text or html is required'} — not filed

    integration-system.connector-auth-kind-application (wave 4: #29151)

    item · rev clause w4 w5 layer · text evidence
    integration-system.connector-auth-kind-application · r6 A0 pass pass HTTP-live (network: echo upstream) · 6 scratch rest instances -› echo :41755, each dispatched by a runtime flow (POST /api/v1/automation start-›connector_action request, then /trigger 200). none: no authorization/x-api-key; bearer: 'Bearer qa-bearer-SECRET-R3-111'; basic: 'Basic cWE6cWEtYmFzaWMtU0VDUkVULVIzLTMzMw==' = base64 qa:‹pw›; api-key: x-api-key ‹key›; query: ?api_key=‹key›, no key header; headerName X-QA-Key: x-qa-key only
    integration-system.connector-auth-kind-application · r6 A1 pass pass HTTP-live · GET /meta/connector/showcase_qa_auth_bearer auth {type:'bearer',credentialRef:'QA_BEARER_TOK'}; basic {type:'basic',username:'qa',credentialRef:'QA_BASIC_PW'}; the echo captured the env VALUES; credential-refs lists each ref status:set source:env
    integration-system.connector-auth-kind-application · r6 A2 pass pass HTTP-live + boot log · boot with QA_BEARER_TOK unset came up (health 200). Log WARN "[Automation] connector instance 'showcase_qa_auth_bearer' (provider 'rest') is degraded: credential 'QA_BEARER_TOK' is not set. Set it under Setup → Connector credentials (PUT …/credential-refs/QA_BEARER_TOK) or as the environment variable…; …no restart." Descriptor state degraded, same reason, actions []. Dispatch -› 400 FLOW_FAILED naming it. Echo 6-›6
    integration-system.connector-auth-kind-application · r6 A3 fail pass build (os compile, scratch copy) · oauth2 control (client_credentials, tokenUrl, clientId, credentialRef) rc=0, in artifact. rc=2 refusals: no grant -› 'auth.grant: Invalid discriminator value. Expected authorization_code | client_credentials'; clientSecret -› auth unrecognized, credentialRef guidance; accessToken -› runtime-token guidance; bearer token -› unrecognized; oauth1 -› auth.type; inline authentication -› #7990 text
    integration-system.connector-auth-kind-application · r6 A4 pass pass HTTP-live · full bodies of GET /automation/connectors (10,387 B), /meta/connector (5,871 B), /credential-refs, /meta (1.24 MB), /automation/_status and 6 per-instance /meta/connector reads: 0 hits for all three values, 'SECRET-R3' or the basic base64. username 'qa' present. Boot log 0 hits
    integration-system.connector-auth-kind-application · r6 N0 pass pass HTTP-live · same greps as A4: no resolved secret in any listing, meta or descriptor body. A credential-ref PUT stored value (STORED-R3) also absent from listing, credential-refs and meta. sys_connector_credential not exposed via the data API (404 OBJECT_API_DISABLED)
    integration-system.connector-auth-kind-application · r6 N1 fail pass build (os compile, scratch copy) · every inline secret (oauth2 clientSecret/accessToken, bearer token), grant-less oauth2 and oauth1 refused rc=2. Inline authentication refused rc=2 on both a catalog descriptor and a provider-bound instance (ADR-0097 §3 text). The well-formed oauth2 control builds clean, as ADR-0143 D1 requires
    integration-system.connector-auth-kind-application · r6 N2 pass pass HTTP-live · unset-ref boot did not fail. The instance is listed degraded with a reason naming QA_BEARER_TOK. No upstream call was made without the credential (echo unchanged for bearer). PUT of the ref -› ready, DELETE -› degraded again, no restart

    platform-core.home-admin-cluster-links (wave 4: #28981)

    item · rev clause w4 w5 layer · text evidence
    platform-core.home-admin-cluster-links · r6 A0 pass pass browser-live · UnifiedSidebar adminItems = 8 (sys-settings, sys-apps, sys-marketplace, sys-datasources, sys-users, sys-orgs, sys-roles, sys-config) = step-0 list, no diff. Each URL: no 'Page not found', no 'No Apps Configured', no record view for 'system'; landings: Settings hub, Applications, App Marketplace (page renders; catalog 'could not be reached' banner = offline catalog), Datasources, sys_user list, sys_organization list, Studio Position catalog, Settings hub
    platform-core.home-admin-cluster-links · r6 A1 pass pass browser-live · /apps/com.objectstack.setup/system -› …/system/settings in one hop (alias /apps/setup adds only the app-id canonicalisation hop); SettingsHub 11 entries (Localization, Company, Branding, Mail, SMS, Authentication, File Storage, AI & Embedder, Knowledge, Data Lifecycle, Feature Flags): each click -› …/system/settings/‹ns›, heading rendered, form controls present, 0 not-found/errors
    platform-core.home-admin-cluster-links · r6 A2 fail pass browser-live · users -› /apps/com.objectstack.setup/sys_user, organizations -› …/sys_organization (lists render); roles and positions -› /_console/studio/~all/metadata/position?scope=environment ('Position' catalog), permissions -› /_console/studio/~all/metadata/permission?scope=environment ('Permission Set'); one redirect after the alias canonicalisation, no stop on /apps/setup/metadata/…
    platform-core.home-admin-cluster-links · r6 A3 pass pass browser-live · member (invite-first, role member, own context, get-session = member): /home has no sidebar, no admin words, no link to /apps/setup|/system|/metadata|/studio; Help = ['All documentation','Online documentation','About']. In showcase app: no admin entry or link; Help = ['This app's docs','All documentation','Keyboard shortcuts','Online documentation','About']
    platform-core.home-admin-cluster-links · r6 N0 pass pass browser-live · all 8 admin target URLs and all 11 settings-hub entries: 0 'Page not found', 0 record view for an object 'system' (no landing URL ends in /system); landings were real pages (Settings, Applications, App Marketplace, Datasources, sys_user, sys_organization lists, Studio Position catalog, 11 settings pages)
    platform-core.home-admin-cluster-links · r6 N1 pass pass browser-live · users, organizations, roles, positions, permissions under /apps/setup/system/ each redirected (traces end on …/sys_user, …/sys_organization, /studio/~all/metadata/position?scope=environment x2, /studio/~all/metadata/permission?scope=environment); none answered 404/Page not found
    platform-core.home-admin-cluster-links · r6 N2 pass pass browser-live+HTTP-live · member's own session: /home no sidebar, no admin words, no link to /apps/setup|/system|/metadata|/studio; Help menu documentation/About only (+Keyboard shortcuts inside app); showcase app shell same. Server half: member Setup URLs render 'You don't have access to this app', GET /meta/app/com.objectstack.setup 403 for a member (captured on boot 41770, same build). Typed-URL /system/apps page for a member is an off-clause probe finding, not a shell entry

    records-forms.list-view-capabilities (wave 4: #29172)

    item · rev clause w4 w5 layer · text evidence
    records-forms.list-view-capabilities · r8 A0 pass pass browser-live · All 11 variants driven live, each with its own trace/screenshot: saved-view-filter (A1), quick-search (A2), sort (A4), server-pagination (A3), saved-view-switcher (In Progress/Task List/Grid/Bulk Actions/Legacy Row Actions/Done/Urgent picked from list-view-switcher-menu), inline-edit (A5), export (A6), visualization-switcher (A7), bulk-actions (A8), row-actions (A9), conditional-formatting (Field Zoo: Specimen - Full tr bg rgba(37,99,235,0.08), Specimen - Minimal none)
    records-forms.list-view-capabilities · r8 A1 pass pass HTTP-live · pin saved-view-filter.spec.ts passed at 589049b (200 half); live: In Progress -› GET /api/v1/data/showcase_task?...&filter=[["status","equals","in_progress"]] 200 total 2 (Ingest pipeline, Build homepage); header 'In Progress · 2', count bar '2 records'
    records-forms.list-view-capabilities · r8 A2 pass pass HTTP-live · Accounts search box 'huaning' -› GET /api/v1/data/showcase_account?top=50&search=huaning 200 total 1 [华宁科技]; direct GET ?search=huaning same row Ac7ihvTuV26aOVIV; Contacts '张' -› search=张 total 1 [张伟]
    records-forms.list-view-capabilities · r8 A3 pass pass HTTP-live · 55 inquiries via createMany 201 (total 3-›58). Inquiries list: GET ?top=50 -› 50 rows total 58; Next -› GET ?top=50&skip=50 -› 8 rows; union 58 ids, 58 distinct, none missing; 0 data requests without top
    records-forms.list-view-capabilities · r8 A4 pass pass HTTP-live · Task List (showcase_task.tabular) GET ...&sort=-estimate_hours 200, 10 rows, server order 60,40,30,24,20,18,16,16,12,8; no pageerror
    records-forms.list-view-capabilities · r8 A5 pass pass HTTP-live · pin inline-edit-polish-2572.spec.ts passed (PATCH half, Project record page; a re-run crashed the page under memory pressure). Live: Grid view dblclick Estimate of 'SEO migration plan' 88-›89, Save All (1) -› PATCH /api/v1/data/showcase_task/LjUowtHfrnfwcrBW {"estimate_hours":89} 200; full-row API diff = estimate_hours + updated_at only; cell reads 89 after reload
    records-forms.list-view-capabilities · r8 A6 pass pass HTTP-live · pre-grant: Display footer has no Export entries, export route 403 EXPORT_NOT_PERMITTED. Grant: PUT /api/v1/meta/permission/qa_task_export 200 + sys_user_permission_set 201. Footer then lists exactly Export as CSV / XLSX / JSON; each GET .../export?format=‹f›&filter=[[status,equals,in_progress]] 200; csv/xlsx/json each hold exactly Ingest pipeline + Build homepage
    records-forms.list-view-capabilities · r8 A7 pass pass browser-live · pin user-filters.spec.ts viz case passed (whitelist half). Live: Layout dropdown = Grid, Kanban, Gallery, Calendar, Timeline, Gantt. Kanban/Gallery/Timeline/Gantt/Grid each render all 10 seeded titles; Calendar Nov 2026 shows the 8 in-month titles of its 9-row date-windowed request; no pageerror
    records-forms.list-view-capabilities · r8 A8 fail pass HTTP-live · per-record: Mark Done over 2 not-done rows -› 2 x POST /api/v1/actions/showcase_task/showcase_mark_done {recordId} 200, 'Succeeded 2 / 2'. Aggregate: Recalculate Selection over 3 rows -› ONE POST /api/v1/actions/showcase_task/showcase_recalc_selection {params:{_selectedIds:[3]}} 200, flow output 448/208/120 = re-read rows, 'Succeeded 3 / 3'
    records-forms.list-view-capabilities · r8 A9 pass pass browser-live · Legacy Row Actions row menu: Mark Done, Reassign…, Quick View, Recalculate Estimate (Quick View once). Recalculate Estimate -› POST /api/v1/actions/showcase_task/showcase_recalc_estimate {recordId} 200 (flow ran); Quick View opens its Component Gallery dialog, analytics queries 200
    records-forms.list-view-capabilities · r8 N0 pass pass HTTP-live · overlay showcase_task.done filter done equals 'maybe' (needs ensure-customization + ?package=com.example.showcase.custom + overrides, see accuracy); Done view GET ...filter=[["done","equals","maybe"]] -› 400; grid 0 rows, 'This view's query was rejected'; overlay DELETEd, filter back to status=done
    records-forms.list-view-capabilities · r8 N1 pass pass browser-live · overlay showcase_task.urgent userFilters {element:'tabs', tabs:[QA InProg Tab]}: Urgent object list renders no tab chip (text count 0, 2 rows, request filter priority=urgent only); console warns the userFilters are ignored on an object list view (ADR-0047); overlay DELETEd
    records-forms.list-view-capabilities · r8 N2 pass pass HTTP-live · every export request carried filter=[["status","equals","in_progress"]]; each of the 3 files holds 2 rows, not the 10-row table; an export with a refused filter answers 400, not the table

    records-forms.adhoc-filter-sort-builder (wave 4: #29172)

    item · rev clause w4 w5 layer · text evidence
    records-forms.adhoc-filter-sort-builder · r8 A0 pass pass HTTP-live · in_progress + FilterBuilder Estimate (h) › 10 AND Progress ‹ 80 (keeps both rows): GET .../showcase_task?...&sort=-due_date&filter=["and",[["status","equals","in_progress"]],["and",["estimate_hours","›",10],["progress","‹",80]]] 200 total 2; API read: both in_progress, estimate 60/40, progress 55/45
    records-forms.adhoc-filter-sort-builder · r8 A1 pass pass HTTP-live · SortBuilder Due Date, Newest → Oldest -› request carries sort=-due_date; response order Ingest pipeline (11-23) then Build homepage (11-13), descending over 2 rows; an asc run carried sort=due_date and the reverse order
    records-forms.adhoc-filter-sort-builder · r8 A2 pass pass browser-live · with search 'pipeline' added: sidebar Accounts, then Tasks (URL drops query), then In Progress. Screenshot: filter badge 2, sort 1, search 'pipeline'; FilterBuilder holds Estimate (h) › 10 + Progress ‹ 80, SortBuilder Due Date Newest → Oldest; re-issued request carries the same merged filter + sort=-due_date + search=pipeline (2/2 runs)
    records-forms.adhoc-filter-sort-builder · r8 A3 fail pass browser-live · one ui.list.showcase_task row, user_id = admin id; LS key list-state:‹adminId›:showcase_task (search under list-filters:‹adminId›:...). Account-menu Log out -› /login?redirect=/apps/.../view/showcase_task.in_progress (no uf_*); B signs in on that page, lands on the bare view, first request carries only the stored status filter, toolbar empty, B has no pref row and no LS key (2/2 runs)
    records-forms.adhoc-filter-sort-builder · r8 A4 pass pass HTTP-live · pin user-filters.spec.ts Triage tabs + Workbench dropdown cases passed. Live: Triage Urgent tab -› ?uf__tab=urgent, filter [priority,equals,urgent] total 2; fresh context same URL issues identical request + ids. All Projects chips Planned+Active -› ?uf_status=planned%2Cactive, filter [or,status=planned,status=active] total 3; fresh context identical request + 3 ids
    records-forms.adhoc-filter-sort-builder · r8 N0 pass pass HTTP-live · every toolbar-filtered request kept [["status","equals","in_progress"]] inside an 'and' with the toolbar conditions; no row outside in_progress returned
    records-forms.adhoc-filter-sort-builder · r8 N1 pass pass browser-live · overlay gives showcase_task.urgent userFilters element 'tabs' (QA InProg Tab): no chip and no tab role on the object list, 2 rows, request filter priority=urgent only; console logs the ADR-0047 suppression; object-list URLs carried only uf__filter/uf__sort/uf__search, never uf__tab; overlay DELETEd
    records-forms.adhoc-filter-sort-builder · r8 N2 fail pass browser-live · same browser, account-menu Log out then B signs in on the login page that opened: B lands on the bare view URL, its requests carry no A condition/sort/search, toolbar shows none (2/2 runs); B's own pref read returns 0 rows
    records-forms.adhoc-filter-sort-builder · r8 N3 pass pass HTTP-live · added FilterBuilder row Priority / Equals with no value ('Select value'): requests issued meanwhile carried only the 2 complete conditions, no ["priority","=",""] node; count stayed 2

    studio-authoring.draft-publish-lifecycle (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.draft-publish-lifecycle · r11 A0 pass pass HTTP-live · PUT /meta/dashboard/qa_lifecycle_probe?mode=draft&package=com.qa.rtwo -› 200 {success:true,version:hmac-sha256:f4e03bd5…,seq:1,state:'draft'}; GET /meta/dashboard/qa_lifecycle_probe as admin -› 404 RESOURCE_NOT_FOUND; as auditor.demo (end user) -› 404
    studio-authoring.draft-publish-lifecycle · r11 A1 pass pass HTTP-live · POST …/qa_lifecycle_probe/publish -› 200 'Published draft … [seq=3]'; GET -› 200 {type:'dashboard',name:'qa_lifecycle_probe',item:{widgets:[1, title 'REV1 Tasks by Status']}}; pre-publish read was 404
    studio-authoring.draft-publish-lifecycle · r11 A2 pass pass HTTP-live · draft 2 PUT -› 200 state draft seq 4; GET while pending -› 'REV1 Tasks by Status'; publish -› 200 seq 5; GET -› 'REV2 Tasks by Status'
    studio-authoring.draft-publish-lifecycle · r11 A3 pass pass HTTP-live · with draft 2 staged, PUT ?mode=draft with If-Match: superseded draft-1 token -› 409 {code:'METADATA_CONFLICT',currentVersion:hmac-sha256:8ffead32…}; the staged REV2 draft (not the 'STALE write' body) is what the next publish served
    studio-authoring.draft-publish-lifecycle · r11 A4 pass pass HTTP-live · staged qrtwo_pd_one/two + app qrtwo_app + object r2badobj (no prefix): POST /packages/com.qa.rtwo/publish-drafts -› HTTP 200 body data.success:false outcome 'refused' publishedCount 0 failed [r2badobj NAMESPACE_PREFIX]; pd_one, pd_two, app all 404 after; bad draft discarded, re-POST -› 200 data.success:true publishedCount 3, one commitId; both dashboards serve
    studio-authoring.draft-publish-lifecycle · r11 A5 pass pass HTTP-live · draft with stray widget key object -› 422 INVALID_METADATA 'widgets.0 [unrecognized_keys]'; publish -› 404 NO_DRAFT; live GET still 'REV2 Tasks by Status'
    studio-authoring.draft-publish-lifecycle · r11 A6 pass pass browser-live · Studio /_console/studio/com.qa.rtwo/interfaces › QA Dash: header chip 'Draft · 2 unpublished', Changes pill '2', 'Design · includes unpublished changes' (screenshot); after REST per-ref publish + reload: no Draft line, pill '1' (the app draft); after publish-drafts + reload: no Draft line, 'Changes' with no pill, 'No drafts pending publish'
    studio-authoring.draft-publish-lifecycle · r11 A7 pass pass HTTP-live · GET …/history -› 200 events v1 create seq1, v2 publish seq3 'publish draft', v3 create seq4, v4 publish seq5, ascending, actor bDbAnx2w… (admin) on each
    studio-authoring.draft-publish-lifecycle · r11 A8 pass pass HTTP-live · GET …/diff?from=2&to=4 -› 200 added [] removed [] changed [{path:'widgets',from:[…'REV1 Tasks by Status'…],to:[…'REV2 Tasks by Status'…]}]; label/name not listed. from=1&to=2 -› changed [] (scores nothing)
    studio-authoring.draft-publish-lifecycle · r11 A9 pass pass HTTP-live + browser-live · POST …/rollback {toVersion:1} -› 200 restoredFromVersion 1; GET -› 'REV1 Tasks by Status'; /_console/apps/qrtwo_app/dashboard/qa_lifecycle_probe renders the REV1 bar widget (behind the first-visit currency modal; no REV2 text). Bad toVersion {}, 'abc', 0, -1, 1.5, '1' -› each 400 INVALID_REQUEST; 999 -› 404 VERSION_NOT_FOUND
    studio-authoring.draft-publish-lifecycle · r11 A10 fail pass HTTP-live · GET …/audit -› 8 rows, every actor bDbAnx2w… (admin): save allowed ×2, publish allowed ×2, save denied metadata_conflict ×1, save denied invalid_metadata ×1 (source protocol.saveMetaItem, lower-case), rollback allowed ×2; 0 rows for 2 NO_DRAFT publishes and 6 INVALID_REQUEST/VERSION_NOT_FOUND rollbacks
    studio-authoring.draft-publish-lifecycle · r11 A11 pass pass HTTP-live · POST …/rollback {toVersion:4} with X-Actor: someone_else_entirely -› 200; audit rollback row and history v6 'revert to version 4' both actor bDbAnx2w… (admin); 'someone_else_entirely' occurs 0 times in /audit + /history
    studio-authoring.draft-publish-lifecycle · r11 N0 pass pass HTTP-live · end user auditor.demo GET pre-publish -› 404 (also ?state=draft -› 404); REV1 served while REV2 was pending
    studio-authoring.draft-publish-lifecycle · r11 N1 pass pass HTTP-live · every publish 200 (per-ref ×2, publish-drafts) followed by a GET serving the new body
    studio-authoring.draft-publish-lifecycle · r11 N2 pass pass HTTP-live · recording note honoured: every forensics read on the live os dev REST server :41741, never a simulated dispatch
    studio-authoring.draft-publish-lifecycle · r11 N3 pass pass HTTP-live · rollback to 1 served REV1, rollback to 4 served REV2, rollback to 1 again served REV1
    studio-authoring.draft-publish-lifecycle · r11 N4 pass pass HTTP-live · open-ledger note honoured, not adjudicated: namespace abort answered HTTP 200 with data.success:false and left all three names 404, as described
  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbZtZMhQkARKJtViA9U

    Per-clause verdicts (3/3): studio-authoring · ux-conventions. w4 is the clause's verdict in the item's wave-4 record (named per item); w5 is this run's verdict, every clause driven live at 589049b0 (pins were run as well where an item has one, and score only the layer they drive).

    studio-authoring.expression-editors (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.expression-editors · r8 A0 pass pass browser-live · F Formula inspector: record.est_hours * 1.1 -› 'Valid CEL · Result type: Number'; record.a + record.b -› 'Result type: Unknown — the engine cannot prove a single type. Wrap operands in double() / int() / string()…'. Engine validate_expression formula: inferredType number / unknown.
    studio-authoring.expression-editors · r8 A1 pass pass HTTP-live · edit f_formula to record.est_hours * 1.2 -› autosave PUT /meta/object/qrx_job 200 with returnType number; draft + live (after publish-drafts) f_formula returnType number (was text); f_line opened in inspector, not edited, keeps returnType text in the PUT body, draft and live; the unprovable a+b edit PUT carried no returnType.
    studio-authoring.expression-editors · r8 A2 fail fail browser-live · 8 of 9 probe tiers equal the engine; the formula-field USING record.f_formula › 5 reads 'Valid CEL' (aria-invalid null) and autosaves PUT 200 at ~1.6 s, error tier only after the re-read (~1.9 s), while the engine refuses it (publish 422 rls-predicate-unenforceable, INVALID_FIELD virtual formula field). 3/3 fresh loads.
    studio-authoring.expression-editors · r8 A3 pass pass browser-live · Notes › Visible when (screenshot-confirmed): previous. / record. -› 8 field options (organization_id…queue, cap 8); record.est -› [est_hours]; previous.s -› [status]; current_user. / parent. -› none; bare est -› none; accepting inserted record.est_hours; bare est_hours › 5 -› aria-invalid true + 'Write record.est_hours.' (engine validation: bare-reference).
    studio-authoring.expression-editors · r8 A4 fail fail browser-live · non-pushdownable USING record.est_hours * 2 › 10, parse-fault USING record.status == and CHECK record.est_hours *: error finding + perm-autosave-held, 0 PUTs in 7 s. Formula-field USING: no held status, PUT /meta/permission/qrx_job_editor?mode=draft…&surface=studio 200 carrying the using; › 7 PUTs again (200); a sibling CHECK edit is then held (0 PUTs). 3/3 fresh loads.
    studio-authoring.expression-editors · r8 A5 pass pass browser-live · Test dialog: record.status == 'open' + {status:open} -› Allowed, + {status:closed} -› Denied; record.est_hours * 2 + {est_hours:4} -› 'Non-boolean result 8'; record.owner_id == current_user.id + owner u1/u2, user u1 -› Allowed / Denied. Direct @objectstack/formula ExpressionEngine.evaluate of same pairs: true/false/8/true/false.
    studio-authoring.expression-editors · r8 A6 fail fail HTTP-live · parity table 8/9 (+ f_line inferred type unknown = engine unknown): formula-field RLS USING differs before the first autosave (editor ok + PUT 200 vs engine publish 422 rls-predicate-unenforceable); all other probes equal (see A2).
    studio-authoring.expression-editors · r8 A7 pass pass plugin-unit · cd ui && pnpm exec vitest run --maxWorkers=1 packages/app-shell/src/views/metadata-admin/celAuthoring.test.ts at 589049b: Test Files 1 passed, Tests 40 passed (incl. describe 'celAuthoring · graceful degradation', 5 cases). test oracle; jsdom/unit.
    studio-authoring.expression-editors · r8 N0 fail fail browser-live · same divergence as A2/A6: formula-field USING editor 'Valid CEL' + PUT 200 vs engine 422 rls-predicate-unenforceable.
    studio-authoring.expression-editors · r8 N1 pass (wave-3 fail) pass browser-live · parse-fault RLS USING and CHECK held, 0 PUTs; parse-fault formula record.est_hours * aria-invalid, 0 PUTs; parse-fault rule record.est_hours › banner 'Not saved yet: an expression on the field “Notes” has an error to fix…', 0 PUTs in 5 s; draft f_formula unchanged.
    studio-authoring.expression-editors · r8 N2 pass pass browser-live · current_user. / parent. list none of qrx_job's fields; bare est withheld from completion; bare est_hours flagged error in formula and rule.

    studio-authoring.packaged-automation-studio-lock (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.packaged-automation-studio-lock · r7 A0 fail pass browser-live · precondition GET /packages showcase writable:false (API + in-page read before pillar). studio-rail-add 'New automation' present; dialog names no target, so driven: Save as draft sent POST /packages/com.example.showcase/ensure-customization 201 + PUT /meta/flow/qa_r5_new_r2?mode=draft&package=com.example.showcase.custom 200 (draft discarded after). Node drag (Δ0,0), select, inspector fill attempt + 4s past debounce: 0 non-GET API requests (2 loads)
    studio-authoring.packaged-automation-studio-lock · r7 A1 pass pass HTTP-live · GET /meta/flow/showcase_urgent_task_alert md5 4dbbe460… before = after (draft read c6dc6665… = after); forged PUT ?mode=draft&package=com.example.showcase 403 code ITEM_LOCKED, ?package=com.example.showcase 403 ITEM_LOCKED, ?mode=draft 403 NOT_OVERRIDABLE, no query 403 NOT_OVERRIDABLE (flat {error,code}); messages name clone/toggle (ITEM_LOCKED) and override-from-pair/toggle (NOT_OVERRIDABLE); traces held no base flow write
    studio-authoring.packaged-automation-studio-lock · r7 A2 fail pass browser-live+HTTP-live · auto-ledger-switch aria-checked true 'Switched on for this deployment', not disabled, no walled reason; click -› POST /automation/showcase_urgent_task_alert/toggle {enabled:false} 200 {enabled:false}, reading off; second click {enabled:true} 200, reading on; no PUT /meta/flow or draft save (only a sys_user_preference PATCH, the recent-packages list); definition md5 unchanged (3 loads)
    studio-authoring.packaged-automation-studio-lock · r7 A3 pass pass browser-live · Notify Assignee node selected (Customize not pressed): 17/17 inspector controls disabled; fill timed out (not editable), value stays 'alert'; after reload + re-select 17/17 still disabled with original values; 'Read-only' badge + 'Read-only package — switch to or create a writable package to edit.' shown (2 loads)
    studio-authoring.packaged-automation-studio-lock · r7 N0 pass pass HTTP-live · no 2xx on any flow write into com.example.showcase: 4 forged PUTs 403, new-name PUT into base 422 WRITABLE_PACKAGE_REQUIRED; the only 2xx flow write was the New draft into com.example.showcase.custom
    studio-authoring.packaged-automation-studio-lock · r7 N1 pass pass browser-live · inspector edit never took (inputs disabled), nothing changed across reload — nothing silently vanished

    studio-authoring.dashboard-widget-and-filter-types (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.dashboard-widget-and-filter-types · r5 A0 pass pass browser-live · all 20 types authored in Studio (Add widget › type › Dataset › Add field), 0 via API; published /_console/apps/rtwd_app/dashboard/rtwd_board: 21 widgets, 0 role=alert, 21 dataset queries 200. bar/column 3 bars 510x350, h-bar 3, line 6 months, area 6, pie 3 sectors, donut 3, funnel 3, scatter 18 points, treemap 3, sankey 9 links/6 nodes, combo 6 bars+line 2 axes, radar 3 axes; table 3 rows+Total 18/262/39.50; pivot 3x3+totals; gauge 18, solid-gauge 262, metric 18, kpi 39.50, bullet 262
    studio-authoring.dashboard-widget-and-filter-types · r5 A1 pass pass browser-live · after screenshots: Add-widget menu 20 = gauge, solid-gauge, metric, kpi, bullet, bar, horizontal-bar, column, line, area, pie, donut, funnel, scatter, treemap, sankey, combo, radar, table, pivot; Type picker 20 = Bar Chart … Pivot Table (same 20 ChartTypeSchema members)
    studio-authoring.dashboard-widget-and-filter-types · r5 A2 pass pass browser-live · filter bar screenshot: 'Status Sel: All' select, 'Region Text' text input, 'All time' date-range picker (15 presets incl. Custom…), 'Score Num' number input, 'Project Lookup: All' select listing the 5 showcase projects (server GROUP BY over showcase_project)
    studio-authoring.dashboard-widget-and-filter-types · r5 A3 pass pass browser-live (network) · each change re-issued exactly 20 rtwd_ticket_metrics queries (every widget but the unbound one), all carrying runtimeFilter: select {status:'open'} gauge 18-›6; text north+Enter {region:{$contains:'north'}} 6; date Last 90 days {opened_on:{$gte:'2026-08-05',$lte:'2026-11-03'}} 12; number 45+Enter {score:45} 1; lookup Website Relaunch {project:'2NEfGQan…'} 4. widget_6 has explicit inspector bindings, same mapping. Unbound widget_21 (filterBindings all five false) 0 queries, stayed '18 Tickets'. Reset restored 18
    studio-authoring.dashboard-widget-and-filter-types · r5 A4 fail pass browser-live (network) · (a) Label edit before Add item -› PUT 200, survives reload; (b) Add item 0 PUT, Description edit while held 0 PUT, Field fill -› one PUT 200 carrying item + description; (c) reload while held -› beforeunload dialog, Leave -› 0 PUT, held item + label edit discarded. #29006 shape: optionsFrom.object alone on a complete filter -› 0 PUT, banner 'Not saved yet: Options From on item #1 … needs a value', Description+Label edits 0 PUT, guard on; labelField fill -› one PUT 200 carrying all three
    studio-authoring.dashboard-widget-and-filter-types · r5 N0 pass pass browser-live · no type drew an empty or zero-height frame; every grouping type showed ›=3 buckets (line/area/combo 6) over 18 rows

    studio-authoring.access-permission-set-lifecycle (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.access-permission-set-lifecycle · r7 A0 pass pass browser-live · Fixture com.qa.rseven (published qrs_project/qrs_task, private OWD, owners admin/phone.demo/auditor). Typing 'Project Editor' into the New dialog derived Identifier 'project_editor'. Save as draft → PUT …/permission/project_editor?mode=draft&package=com.qa.rseven&surface=studio 200; toast 'Permission set “Project Editor” created'; draft read 200 label 'Project Editor', _packageId com.qa.rseven
    studio-authoring.access-permission-set-lifecycle · r7 A1 pass pass browser-live · Draft-held id: notice 'This package already has a permission set with the identifier “project_editor”: Project Editor. Open it, or choose another identifier.', 0 non-GET requests, draft version hmac 351f8b8f… unchanged. After publish (POST publish-drafts 200; live objects.qrs_project {allowRead, allowEdit}): same notice, 0 non-GET requests, live read byte-identical before/after, draft 404 NO_DRAFT
    studio-authoring.access-permission-set-lifecycle · r7 A2 pass pass browser-live · Rail render confirmed by screenshot. 'Proj' → [Project Editor]; 'qrs_vie' → [Task Viewer]; 'editor' → [Project Editor]; 'zzqq' → 'No matches for "zzqq"' + 'Clear search'. Empty package before the first create: main 'No permission sets in this package yet.'; rail 'No permission sets yet — click “+” above to create one'
    studio-authoring.access-permission-set-lifecycle · r7 A3 fail pass browser-live · com.example.showcase › Access › showcase_contributor: 'Read-only' chip (title 'Read-only package — switch to or create a writable package to edit.') and header note '…The package is read-only: its permission sets can be viewed here, but not changed.'; 192 matrix checkboxes, 0 enabled; 0 enabled controls on System capabilities / Row-level security / Tab visibility. + offered: Save as draft 'QR7 Pair Set' → POST /api/v1/packages/com.example.showcase/ensure-customization 201 then PUT …/qr7_pair_set?mode=draft&package=com.example.showcase.custom 200. Pair draft read 200, _packageId com.example.showcase.custom; base draft 404. A second ensure answered 200 created:false
    studio-authoring.access-permission-set-lifecycle · r7 A4 pass pass HTTP-live · Forged PUT …/showcase_contributor?mode=draft&package=com.example.showcase (label HACKED, objects {}) → 403 ITEM_LOCKED. PUT …/qr7_forged_new?mode=draft&package=com.example.showcase → 422 WRITABLE_PACKAGE_REQUIRED. showcase_contributor live read identical before/after (sha 1f41cbf3…); qr7_forged_new live 404 / draft 404 NO_DRAFT
    studio-authoring.access-permission-set-lifecycle · r7 A5 pass pass browser-live · Header: 'New similar' (access-new-similar) is enabled; no Customize or studio-customize. Create copy → POST /api/v1/security/permission-sets/showcase_contributor/clone 200, body exactly {"name":"qr7_contrib_copy","label":"QR7 Contributor Copy"}. Strip 'Saved the copy qr7_contrib_copy in package com.example.showcase.custom…'. The URL opens ?surface=permission:qr7_contrib_copy with that set's header. The pair read holds objects and fields identical to the source. Repeat → 409; the dialog stays open showing the server's RESOURCE_CONFLICT message verbatim. Pins: StudioDesignSurface.accessNewSimilar-28573 9/9, permission-set-clone.test.ts 39/39
    studio-authoring.access-permission-set-lifecycle · r7 N0 pass pass HTTP-live · The create over the published project_editor was refused in the dialog with no request; the live set kept objects.qrs_project {allowRead:true, allowEdit:true} (byte-identical read)

    studio-authoring.no-entry-property-matrix (wave 4: #29002)

    item · rev clause w4 w5 layer · text evidence
    studio-authoring.no-entry-property-matrix · r5 A0 pass pass HTTP-live · all 26 objects: draft and live keys byte-equal; views/forms/flows/dataset/report/dashboard/app/permission sets/position stored and served. Refusal rows refused as written (unknown datasource 422, titleFormat 422 / publish refused, drawer + split keys 422). conditionalFormatting condition stored as CEL envelope {dialect,source}.
    studio-authoring.no-entry-property-matrix · r5 A1 fail fail browser-live · every row shows its effect, including the 4 W4 rows (searchable, lockedBy, nullLabel; titleFormat now a refusal row), except field.sortable:false: the column still sorts from the grid header (sends sort=nosort) and the Sort picker offers it. 2/2 fresh loads on 2 routes.
    studio-authoring.no-entry-property-matrix · r5 A2 fail fail browser-live · titleFormat (422 title-format-retired, publish refused) and drawerSide/drawerWidth (422 ADR-0049) are refused now. New: field.sortable:false is stored, served (live fields.nosort.sortable false) and inert: sortability projection says sortable true, ?sort=nosort 200, the grid and picker sort by it.
    studio-authoring.no-entry-property-matrix · r5 N0 fail fail browser-live · PD#10 class still present, on one new key: field.sortable:false declared, accepted by the draft door, served by publish, ignored by the projection, the server, the grid header and the list Sort picker. The 4 W4 keys (F-1/F-2/F-3/F-5) no longer show it.

    ux-conventions.write-feedback (wave 4: #28981)

    item · rev clause w4 w5 layer · text evidence
    ux-conventions.write-feedback · r10 A0 fail pass browser-live · 13 of 13 landed writes gave visible feedback on the surface that started them (each paired with a server read): create 201, inline PATCH 200, bulk 2×DELETE 200 (reads 404), kanban PATCH 200 (card in To Do + live region), Mark Done 200, Invite 200, Approve 200, import job 201, autosave PUT 200 (2 loads), publish 200, discard DELETE 200, settings PUT 200, member create 201. Studio autosave now toasts 'Draft saved: QA Widget' besides the top bar's 'Saved 09:50 AM'
    ux-conventions.write-feedback · r10 A1 fail pass browser-live · Record-level rows name the record, item or count: 'Task "QA R7 adm create 13788" created', '1 row saved', 'Deleted 2 Task records', 'Moved QA R7 kanban card to To Do.', 'Build homepage marked done.', 'Invitation created for qa.r7.4482@example.com…', 'Approved "EXP-2001"', 'Imported 2 rows.' / '2 created' / '2 updated', 'Draft saved: QA Widget', 'Published 1 item in this package', 'Draft discarded: QA Widget'. Singleton: 'Settings saved'. No bare Done/Success, no snake_case
    ux-conventions.write-feedback · r10 A2 pass pass browser-live · Mark Done declares { { record.title | default:'Task' }} marked done. and showed 'Build homepage marked done.' (exact interpolation). Invite User: the server answered link_only, so the action's declared outcomeMessages.link_only was shown verbatim with result.email: 'Invitation created for qa.r7.4482@example.com. Mail is not configured on this server, so no email went out. Use Copy Invite Link on the invitation to share it.' (successMessage 'Invitation sent to { { result.email }}' is the fallback since #27843)
    ux-conventions.write-feedback · r10 A3 pass pass browser-live · (a) required-empty submit: console-block shape. 0 write requests; field errors 'Title is required' / 'Project is required'; no toast (admin and member). Rule leg via API: PATCH /api/v1/data/showcase_task/AHt53HW8AaPEjK8F {status:'done'} on a backlog task → 400 VALIDATION_FAILED, fields[0].code invalid_transition, 'Invalid task status transition.'; status stays backlog. The UI picker from Backlog offers only [Backlog, To Do]. Member Mark Done → 403 PERMISSION_DENIED, error toast 'You do not have permission to perform this action…'
    ux-conventions.write-feedback · r10 A4 pass pass browser-live · Grid view: double-clicking the Priority cell opened the editor; Escape and a click away sent 0 non-GET requests, and no 'modified' / 'Save All' text appeared (control: a real change shows '1 row modified')
    ux-conventions.write-feedback · r10 A5 pass pass HTTP-live · Create-mode job imp_mw0i7cddw8gkpjg8 (2 created) → Undo import (native confirm accepted) → POST …/undo 200 → both rows gone; history reads 'Undone'. Update-mode job imp_mw0i8fucwc2spb1x (match Title; estimate 3/4 → 30/40) → Undo → 200 → estimates back to 3/4
    ux-conventions.write-feedback · r10 N0 pass pass browser-live · No success toast for the refused writes (required-empty, member 403). The invalid_transition 400 is API-only (no console surface can send it)
    ux-conventions.write-feedback · r10 N1 pass pass browser-live · Same evidence as A4: no write request and no modified state
    ux-conventions.write-feedback · r10 N2 pass pass browser-live · Every success toast is paired with a 2xx and a server read showing the change. The refusals (403, 400 Invite-duplicate probe, client-blocked submits) showed only error feedback

    ux-conventions.disabled-control-reason (wave 4: #28981)

    item · rev clause w4 w5 layer · text evidence
    ux-conventions.disabled-control-reason · r4 A0 fail fail browser-live · Hover tooltip (pointer approached in steps, held 1.1 s) shows the reason for: Archive, Studio Publish, validation editor (20 controls), field inspector display-patch (35), flow node inspector (16), flow edge inspector (7), list Reassign…; import Next has an adjacent banner. FAIL: /studio/~all/metadata/{object,flow}/ item page default inspector (24 / 1 disabled controls): no tooltip, no adjacent text
    ux-conventions.disabled-control-reason · r4 A1 fail fail browser-live · Pass: each control above sits in a tabindex=0 wrapper (span or role=group) that Tab reaches, with aria-describedby set to non-empty text (Archive at stop 75+, Reassign at stop 122, field rail 18 groups, flow node/edge groups). FAIL: on the ~all item page, Name/Label/Plural label/Icon/Description/Exposure posture/... and flow Type are disabled, with no focusable wrapper, no aria-describedby and no title
    ux-conventions.disabled-control-reason · r4 A2 fail fail browser-live · Specific reasons: Archive 'Mark the task done first.' (author disabledReason); 'Read-only package — switch to or create a writable package to edit.'; display-patch 'This field belongs to the installed package: you can change only its Label, Description, Help text, Placeholder, Option labels…'; import 'Can’t continue — required field(s) not mapped: Project (project)…'; Reassign 'This action runs on a single record — select a row first.'. ~all item page default inspector: none
    ux-conventions.disabled-control-reason · r4 N0 pass pass browser-live · Archive on done task 'App wireframes' is enabled: no aria-describedby, no title, parent has no tabindex, no tooltip on hover. Display-patch enabled Label/Description/Help text/Placeholder/option Label: no describedby, not inside [data-disabled-reason]. The enabled flow switch's title/description explains a deployment-only toggle, not a disabled reason
    ux-conventions.disabled-control-reason · r4 N1 pass pass browser-live · recorded: member phone.demo (org_member) has no row selection, bulk delete or inline edit on the All Tasks or Grid views (absent, not disabled); Mark Done is absent on a done task (visible predicate). Only Archive renders disabled for the member, with its reason
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbZtZMhQkARKJtViA9U

    Close-out (RUNNER extraction obligation, checklist-test §5). Every FAIL has a disposition; each probe finding that passed the pm-dispatch filing gate (class ①, dedupe incl. closed) is extracted with a QA-source: line.

    Extracted: #29892 · ai.mcp-run-action-exposure-gate · acceptance[4]
    Extracted: #29891 · integration-system.email-template-render · acceptance[7]
    Extracted: #29896 · studio-authoring.no-entry-property-matrix · acceptance[1], acceptance[2], negative[0]
    Extracted: #29897 · ux-conventions.disabled-control-reason · acceptance[0], acceptance[1], acceptance[2]
    Extracted: #29884 · studio-authoring.api-written-keys-survive-studio-save · observed beside the clauses (#29015 re-check)
    Extracted: #29894 · api-backend.error-sentences-and-unique-violation-wire-code · observed beside the clauses
    Extracted: #29895 · i18n.notification-localized-and-clears · observed beside the clauses
    Parked-on: #27482 · studio-authoring.expression-editors · acceptance[2], acceptance[4], acceptance[6], negative[0] stale spec against the #26858 ruling; open card awaits a ruling (re-point vs a validate-only door)
    Parked-on: #27377 · checklist-accuracy findings and fixture gaps (record body sections of those names)

    Dispositions: R-1 stale spec (re-point card #29892; wave 4 parked it on #27377, which closed without the re-point). R-2 product defect, the #29152 clause still failing on the non-string display-name case #29250 did not reach. R-3 stale spec carried by open #27482, no new card (one existing card carries it). R-4 product defect on a row wave 4 recorded passing. R-5 product defect on a surface wave 4 did not read. R-6 (#29015 re-check, off-clause) product defect, independently verified, triaged p1 on #29884. The off-clause security finding has no card and no detail here; it went privately to the dispatching PM session.


    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