Skip to content

QA run · priority:P0 (24/24) · 8e3d4a54 · 2026-11-13 · 21 PASS / 3 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #33087

Closed

Description

@objectstack-fleet

qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

Slice A-P0 of qa#3's round 2 part A at 8e3d4a5 (the standing smoke after four of the five p0 blockers landed and the fifth was taken off the list).

Environment fingerprint

  • Framework objectstack-ai/objectstack main @ 8e3d4a54 (replaces a596c03; nothing at a596c03 is scored). Fresh tree: pnpm install --frozen-lockfile, pnpm build --concurrency=2, pnpm console:build, showcase compiled once (09:07–09:10Z). The console served by /_console/ is byte-identical to packages/console/dist.
  • Boots: objectstack dev --ui --seed-admin --no-compile --no-watch -p PORT -d file:/tmp/qa3/A/<runner>/<db> with OS_PORT / OS_STORAGE_LOCAL_ROOT exported, stopped by process group, one boot per runner at a time. Installs and fixture apps from scratch copies; examples/app-showcase/.objectstack/installed-packages absent throughout; git status clean at the end. Ports 41210–41249.
  • Special boots: walled isolated and group (no-active-org, walled-environment-metadata); a scratch Postgres (datasource-credential-refusal-matrix); a v17 database written by npm create-objectstack@17.7.0 with every dependency pinned to 17.7.0 and booted once by its own os dev --seed-admin (v18-boot-refusals, ownership ceremony; golden v17.db sha256 b090628a…78cd8); the cli-18-prerelease-db recipe restore under --dev (ceremony A5).
  • Execution: Tier 1 pins run centrally (sequential vitest --maxWorkers=1 per package, 19 groups, cli e2e under OS_TEST_TIERS=nightly): 3,403 tests, 3,400 passed, 0 failed, 3 skipped by design (rls-multitenant ×2 — describe.skipIf(!organizationsAvailable), the organizations runtime is not resolvable in the open workspace; activation-ledger-package-key ×1 — it.skipIf memory driver). Tier 2 by 3 runner subagents (effort high): api (12 items), browser/mixed (8), cli (4, beside the upgrade slice).
  • Timestamps (UTC): build 09:07–09:10; pins 09:12–09:30; api runner 09:12–09:28 (setup ≈5 min, test ≈17); browser runner 09:11–10:01 (setup ≈12, test ≈38); cli runner 09:11–10:00 shared with A-UPG (setup ≈30 incl. the 17.7.0 install, test ≈55).

Scope

Selector priority:P0 at 8e3d4a5 → 24 items, all judged. Revisions (all equal to the resolver's): rls-both-sides 11 · write-path-guards 9 · crud-permission-matrix 13 · owd-sharing-matrix 9 · anonymous-deny-surfaces 5 · no-active-org-session-semantics 10 · walled-environment-metadata-operator-only 8 · query-contract-matrix 9 · packaged-action-disabled-dispatch 13 · packaged-flow-disable-durable 10 · default-organization-boot-invariant 4 · seed-integrity 7 · mcp-stdio-fail-closed 9 · dev-boot-contract 9 · organization-ownership-ceremony 9 · v18-boot-refusals 9 · account-app-entry 9 · console-login 5 · nav-surfaces-render 10 · builtin-apps-nav-render 13 · crud-roundtrip 18 · first-run-loop 13 · boot-health 14 · datasource-credential-refusal-matrix 9.

Derived item verdicts

verdict items
FAIL (0) —
PARTIAL (3) access-security.rls-both-sides (A5 D11 spot-check blocked(dependency #7463), as in round 1; the rest of A5 passed) · access-security.crud-permission-matrix (A5 guest anchor blocked(dependency #23267), as in round 1) · access-security.no-active-org-session-semantics (A0 producer (1), the member-less user, blocked(fixture): knownGaps[0], no HTTP door mints one on a walled boot since #25626; everything on producer (2) passed on both walled boots)
PASS (21) every other item

#32955 re-drive (round 1 FAIL #32953, approvals.account-app-entry [3], [4])

Fixed at 8e3d4a5 (8004529). A non-admin invited contributor (showcase_contributor + showcase_finance, no setup app) who is a pending approver on two fresh flow:showcase_invoice_signoff requests: GET /api/v1/meta/object/sys_approval_request → 200 on curl and on every in-page load; Approve / Reject render from the inbox row, from a cold-loaded ?request= link and from a cold load of the record URL; "Object Not Found" never appears. Approve moves progress 0 of 2 → 1 of 2, drops the persona from pending_approvers and turns viewer.can_act false; the buttons are gone after a reload.

Fails — reproduction rule and disposition

None.

Off-clause probes (rule 8)

Every runner kept a probe budget after each item (2–5 min); per-item minutes and doors are in the probe log (record-A-P0-clauses-1.md). No product defect found; no card drafted.

Observed and not carded (low, for the seat to weigh): the stock boot logs a [sharing-rule] warning that showcase_share_new_inquiries_with_field_ops expands to no recipients; on the single posture admin/create-user answers membershipCreated:false while a sys_member row (role member) exists immediately afterwards; os dev over a v17 copy upgrades it in place with a backup (the documented ADR-0131 D10 development exception, not a finding); a plugin driver's credentials sit in cleartext in sys_metadata at rest while every read door redacts them (the boundary ruled in #21921).

Parked-on #32908

Fixture gaps: a member-less user on a walled boot (no-active-org A0 producer (1)); the D11 private write-up (#7463); a guest-anchor binding door (#23267); the cli-18-prerelease-db recipe does not re-date rotation shards, so its sys_activity__r20261027 shard (66 rows) now falls outside the 7-day window and the first refused boot drops it by designed retention (restore through fixture-as-of); marketplace catalog content blocked(environment) by the egress proxy (named "Failed to load marketplace" state, as the knownGap expects).

Per-item checklist-accuracy notes follow.

access-security.no-active-org-session-semantics

  • steps[8] says the member "creates a showcase_account and a showcase_project in A": a baseline member (showcase_member_default) cannot create either (403 PERMISSION_DENIED with A active). The run had the operator create them in A; the member's own create was the showcase_task, as the clause requires. The step should name who creates the parents or grant the member a set that can.
  • A re-sign-in after the clear starts a session with an active organization again (the member's next session read activeOrganizationId = A), so a run must clear per session.

access-security.walled-environment-metadata-operator-only

  • The org owner already holds organization_admin in its own organization automatically (sys_user_permission_set row reason "auto-org-admin-grant: granted from membership grade 'owner'"), so the persona in this run held manage_metadata (fixture row) plus organization_admin — still not platform admin. Not a defect; noted so the persona's grants are explicit.
  • install-local leg not driven live (knownGaps[1]: scored from the pins; it would need a scratch-copy boot).

approvals.account-app-entry

  • A3/A4 app segment: the request page opens under com.example.showcase, not the inbox's com.objectstack.account. This is by design since objectstack#32667 (ApprovalsInboxPage.tsx requestPagePath → objectHostAppSegment: the app that lists the request's record object, showcase_invoice → Showcase). A4.verify's "/apps//sys_approval_request/record/" reads as the component route's appId — stale wording; scored on the clause (lands on that request's page, replaced in history, parity wit…
  • A6: the picker offers "简体中文" and stores locale zh-Hans; the item says "中文(中国) … code zh-CN". Wording stale; the clause's strings all hold.
  • The persona is the inbox SUBMITTER as well as an approver (its own invoice, per fixtures.requires for A5), so the request page also renders Recall/Reply; harmless, noted.

cli.dev-boot-contract

  • steps[6]/A2: with OS_HOME exported (the per-boot isolation many runners use), the nothing-chosen Database line is file:$OS_HOME/data/objectstack.db, not the project's .objectstack/data/objectstack.db (measured: file:/tmp/qa3/A/ap0c/home-dev/data/objectstack.db). The clause's "" covers it, but the step should say "with OS_HOME unset" so a runner does not misread the line.

cli.organization-ownership-ceremony

identity-auth.default-organization-boot-invariant

  • The admin/create-user answer carries membershipCreated:false together with organizationId: org_mwerd5s8ogmxyh1c, yet a sys_member row (role member) exists for that user immediately after — the membership is made by another path (the single-posture default-org binding), not by create-user itself. Not a clause defect (the clause reads sys_member), but a reader of the create-user body alone would conclude the user has no membership.
  • Boot log also warns [sharing-rule] active business-unit rule expands to NO recipients … sys_business_unit_member rows written by seed replay … are not organization-stamped (rule showcase_share_new_inquiries_with_field_ops, organization null) — outside this item's clauses (sys_business_unit_member is not sampled by A3), noted for the record.

integration-system.datasource-credential-refusal-matrix

platform-core.builtin-apps-nav-render

  • Counts moved: Setup now serves 48 leaves (steps[5] says 46) and 11 settings namespaces — access_policy (nav_settings_access_policy, "Access policy — This organization's sign-in restrictions") is new beside the 10 steps[3] lists. Recount, not a finding.
  • Marketplace catalog content: blocked(environment) — egress proxy, the named error renders (knownGap).

records-forms.crud-roundtrip

  • After Create the console lands on the new record page, not the list; acceptance[3]'s "post-reload screenshot of the list at create" needs an explicit list navigation + reload (run r1's reload was of the record page; r2 did it properly). Minor step wording.
  • Industry defaults to Technology on the create form, so steps[1]'s "fill industry" is satisfied by the default unless another value is picked.

studio-authoring.first-run-loop

  • The en leg used display name 'Repair Center Pkg' (zh leg 维修中心); the id/namespace the clauses read are identical (com.example.repairs / repairs).
  • Picklist option names in the zh type picker: 下拉选择 (Picklist); the author-entered labels (Status, Received…) stay as typed, so the zh end-user list shows "In Repair" — the authored label, correct.

Activity

  1. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

    Probe log (rule 8)

    • access-security.rls-both-sides: 5 min — as B against A's private note: analytics cube count (200, SQL carries owner_id = $1, count 1 = own note only), POST /query where id=A's (200, 0 rows), $select on by-id (400 VALIDATION_ERROR — unsupported on that door), updateMany (207 RECORD_NOT_FOUND), per-object /batch update (207 RECORD_NOT_FOUND), cross-object /batch update (404) and delete (403), deleteMany (207 PERMISSION_DENIED),…
    • access-security.write-path-guards: 3 min — createMany wrapper body (400 VALIDATION_FAILED); admin's own lead_score PATCH also stripped (readonly is static, admin is not isSystem); nothing found.
    • access-security.crud-permission-matrix: 2 min — denied DELETE on a nonexistent private-note id answers the same 403 as an existing one (no existence oracle; recorded under rls-both-sides); delegate sys_user_position edit with only reason (200). Nothing found.
    • access-security.owd-sharing-matrix: 4 min — C2 inserting a line under C1's invoice → 403 "insert on 'showcase_invoice_line' requires edit access to its master record"; C2 creating an invoice with owner = C1's email → 403 PERMISSION_DENIED (invoice_owner_on_create); C2 re-parenting its own line onto C1's invoice → 403 (update requires edit on the master), admin re-read: line not moved, C1's invoice carries only its own line. Nothing…
    • access-security.anonymous-deny-surfaces: 3 min — anonymous PUT on /meta write door (401 rest-flat); analytics as a member over showcase_private_note is recorded under access-security.rls-both-sides probes; nothing found here.
    • access-security.no-active-org-session-semantics: 3 min — member re-sign-in restores an active org (note write 201 in A); the org-less member reading sys_exchange_rate → 403 (object permission); nothing found.
    • access-security.walled-environment-metadata-operator-only: 2 min — owner GET of the refused flow (404, no existence leak beyond the operator's); nothing found.
    • ai.mcp-stdio-fail-closed: 3 min — OS_MCP_STDIO_API_KEY set to whitespace and to a valid key with one extra char: both exit 1 "did not resolve to a valid identity"; admin-API-key revoke attempt (403, as fixtures say). Found nothing.
    • api-backend.query-contract-matrix: 3 min — case-sensitivity boundaries ('ORTH', 'con' → 0 on the case-exact operators, $icontains folds); like with a leading wildcard; mid-set date comparand; nothing found.
    • api-backend.packaged-action-disabled-dispatch: 3 min — contributor: activation door 403 "requires the manage_metadata capability", ledger read 403, data-API create on sys_metadata_activation 405 OBJECT_API_METHOD_NOT_ALLOWED (admin too); toggle of an undeclared action 404 RESOURCE_NOT_FOUND; enabled:"false" string 400 VALIDATION_FAILED. Nothing found.
    • approvals.account-app-entry: 3 min — /system/approvals?request= parity (same landing); cold load of the record URL in a fresh tab (buttons present); persona meta/app?id=setup (empty). Nothing found.
    • automation.packaged-flow-disable-durable: 2 min — runs?limit=200 → 400 VALIDATION_FAILED "expected a whole number <= 100" (the list caps at 100); contributor toggle → 403 "Enabling or disabling an automation flow requires the manage_metadata capability"; same-boot kill switch also held (runs 2 → 2). Nothing found.
    • cli.dev-boot-contract: 4 min — -d :memory: (boots, Database :memory:), the env-URL tier, OS_DATABASE_DRIVER=mingo spelling, SIGTERM vs SIGINT on --fresh. Found nothing.
    • cli.organization-ownership-ceremony: 4 min — os migrate comment-reactions --apply with stdin closed and no --yes (refused, exit 1, checksum unchanged); os migrate apply --allow-destructive before the reactions conversion (the reactions gate is first, as steps[3] says); reset token reuse not tried (token consumed). Found nothing beyond the notes.
    • identity-auth.default-organization-boot-invariant: 2 min — sys_organization list (exactly one row); seeded demo users (usr_showcase_phone_demo, usr_showcase_auditor_demo) also carry member rows in the default org; nothing found.
    • integration-system.datasource-credential-refusal-matrix: 4 min — runtime POST with a legacy alias (passwd) → 400 "Unrecognized key(s) … passwd" (current parse refuses aliases); echo PATCH of the alias row → 200 and the withheld alias keys carried forward unchanged at rest (restoreRedactedConfig); anonymous create → 401. Nothing found.
    • platform-core.boot-health: 2 min — GET /api/v1/ready repeated after later traffic (200); /_console/ served chunk vs dist bytes compared (identical); nothing found.
    • platform-core.seed-integrity: 2 min — $count=true vs bare $top=1 total agree; Specimen — Minimal f_time '09:05:30' authored canonical; nothing found.
    • platform-core.console-login: 4 min — in-shell sidebar link as the in-app leg (r2, redirect param keeps the exact list route, re-login restores it); sign-out then bearer-only fetch → 401; wrong password: no cookie minted. Nothing found. (An attempted mutation probe POST /data/showcase_task {name} answered 400 INVALID_FIELD — my wrong field, not a finding.)
    • platform-core.nav-surfaces-render: 3 min — /apps/com.example.showcase/page/no_such_page → in-shell "Page Not Found — The page "no_such_page" could not be found. It may have been removed or renamed."; /apps/no_such_app/showcase_task → full-page "This app can't be opened — The server did not return this app for your account. [Retry] [Back to home]" (no shell, named state, not blank). Nothing found.
    • platform-core.builtin-apps-nav-render: 3 min — member GET /api/v1/meta/app/setup (403) and /bundle (403) beside the ?id= read; admin settings control 200; nothing found.
    • platform-core.v18-boot-refusals: 5 min — (1) os serve --dev over a second v17 copy: refused, --dev spelled (above). (2) os dev --no-compile --no-watch -d file:COPY3 over a third v17 copy: the guard refuses, then os dev runs the documented ADR-0131 D10 development exception ("⟳ … os dev upgrades it in place, keeping a copy first…", backup at /tmp/qa3/A/ap0c/r1/backups/organization-ownership-, warns "sys_audit_log: the pla…
    • records-forms.crud-roundtrip: 3 min — $search on a CJK fragment (华宁) returns seed + new row; row-action delete path (vs detail menu) — same dialog and result. Nothing found.
    • studio-authoring.first-run-loop: 3 min — forged write onto an EXISTING showcase object (ITEM_LOCKED) beside the new-object probe (WRITABLE_PACKAGE_REQUIRED); en-default meta read of the zh-published object (Owner/Queue labels). Nothing found.

    Per-clause tables

    access-security.rls-both-sides · rev 11 · PARTIAL

    clause verdict evidence
    A0 pass A created "A note 1/2 mwerh0bm" (201), B "B note mwerh0bm" (201). A's list titles = [A note 2, A note 1], B's id absent; B's list = [B note], neither A id present (server row sets)
    A1 pass A GET /data/showcase_private_note/ → 404 {"code":"RECORD_NOT_FOUND"} (not-found-shaped denial)
    A2 pass A PATCH {"body":"forged"} → 404 RECORD_NOT_FOUND; admin before/after reads byte-identical (body "orig-B note mwerh0bm")
    A3 pass admin GET list (200) contains all three ids from both owners
    A4 pass objectstack verify --rls: probe-persona line ── 21 PROVEN (21 consistent, 0 HOLES) · 3 NOT PROVEN (0 member-visible, 0 probe-blocked, 3 skipped); total line ══ all personas: 38 PROVEN (38 consistent, 0 HOLES) · 226 NOT PROVEN (131 member-visible, 62 probe-blocked, 33 skipped) [unit: one object × persona probe]; …
    A5 blocked(dependency: #7463 — the D11 spot-check needs the private write-up, knownGaps[0]) other half done: each of the 3 skipped rows names a benign reason — showcase_project_membership "no plain-text probe field", showcase_ext_customer / showcase_ext_order "external read-only object (federated datasource showcase_external; no inserts)". Hand spot-check of showcase_project_membership as a baseline member: …
    N0 pass forged PATCH answered 404 and the row is unchanged on admin re-read
    N1 pass procedural — D11 verdict recorded here only (owd-sharing-matrix A4 cross-references this row)
    N2 pass procedural — this record cites the item and clause, not the bare handle

    access-security.write-path-guards · rev 9 · PASS

    clause verdict evidence
    A0 pass admin POST showcase_contact {"name":"RO probe …","lead_score":10} → 201; body droppedFields [{"object":"showcase_contact","fields":["lead_score"],"reason":"readonly"}], header X-ObjectStack-Dropped-Fields lead_score;reason=readonly; GET back lead_score null
    A1 pass PATCH {"lead_score":99} → 200 with the same droppedFields (reason readonly); before/after GET lead_score null → null (stock posture is strip, not ERR_READONLY_FIELD_REJECTED)
    A2 pass Alice POST private_note {"title":"planted …","owner_id":Bob} → 403 PERMISSION_DENIED "'owner_id' on 'showcase_private_note' is system-managed — creating a record owned by another user requires the transfer grant (allowTransfer or modifyAllRecords)"; admin $count of title 'planted …' = 0
    A3 pass Alice self-owner insert (owner_id = her id) 201, persisted owner = Alice; PATCH owner_id=Bob 403 PERMISSION_DENIED ("changing record ownership on update requires the transfer grant"); PATCH owner_id=null 403 PERMISSION_DENIED ("release only your own record, back to its queue…"); owner re-reads Alice's id
    A4 pass Alice POST /createMany with bare array of 3 (no owner_id) → 201; all three rows read back owner_id == Alice's id. {"records":[…]} wrapper → 400 VALIDATION_FAILED "the body must be a JSON array of record objects"
    A5 pass admin invoice X set paid (200); PATCH tax_rate 20 → 200, droppedFields reason readonly_when, re-read 5 (unchanged); PATCH line quantity/unit_price/product → 200, droppedFields [quantity, unit_price, product] readonly_when, re-read 1/10/original product. Mixed updateMany [paid line, open line] quantity 8 → 200: results…
    A6 pass updateMany [compliant 11, violating −5, compliant 13] with options.continueOnError true → 207 total 3 / succeeded 2 / failed 1, results[1] VALIDATION_FAILED "Qty must be ≥ 0; Amount must be ≥ 0"; re-reads 11 / 3 (unchanged) / 13. Default-stop contrast [21, −5, 23] → 207 succeeded 1 failed 2, results[2] NOT_ATTEMPTED; …
    N0 pass admin (privileged): forge insert owner_id=Bob 201 (owner = Bob), transfer Alice's note to Bob 200 (owner = Bob), disown owner_id null 200 (owner null)
    N1 pass (pin) pins green at 8e3d4a5: owner-anchor-and-bulk-writes.dogfood.test.ts 12✓, showcase-static-readonly.dogfood.test.ts 9✓, showcase-readonly-when-parent.dogfood.test.ts 9✓, engine-insert-static-readonly-strip.test.ts 21✓, engine-readonly-strip-caller-values.test.ts 34✓

    access-security.crud-permission-matrix · rev 13 · PARTIAL

    clause verdict evidence
    A0 pass access-matrix.json recounted: 45 entries = 41 showcase_* + 4 delegate × sys_*. 164 showcase cells, 87 effective-allow (9 of them baseline-only flips) all 2xx with the effect re-read (created row GET 200, patched field re-reads the new value, deleted row GET 404). Delegate: sys_business_unit / _member / sys_user read 2…
    A1 pass all 77 effective-deny showcase cells 403 with body.code PERMISSION_DENIED (code census {"403 PERMISSION_DENIED":77}); delegate withheld cells: sys_business_unit create/edit/delete, sys_business_unit_member create/edit/delete, sys_user edit → 403 PERMISSION_DENIED; sys_user create and delete → 405 OBJECT_API_METHOD_NOT…
    A2 pass every denied create: admin $filter on the object's marker field for the unique mark returned 0 rows
    A3 pass auditor vs contributor on foreign rows: private_note (made by a plain member), inquiry, invoice, invoice_line (admin-made): auditor by-id 200 and present in list on all four; contributor by-id 404 RECORD_NOT_FOUND and absent from list on all four
    A4 pass ops PATCH the admin's announcement title → 200, re-read shows "MAMA ops …"; member_default identical PATCH → 403 PERMISSION_DENIED, re-read still "MAMA ops …"
    A5 blocked(dependency: #23267) per knownGaps[2] — no door binds a set to the guest anchor (ADR-0138 D3 item 5, ruling C); a member holding guest_portal reads inquiries via the baseline (the sweep's guest_portal × showcase_inquiry read cell answered 200 as the union predicts); REST anonymous create/read answer 401 (anonymous-deny-surfaces A0, this b…
    A6 pass verdict matrix dimension: 41 rows × 4 verbs = 164 cells + 4 delegate rows × 4 = 16 cells; every set and every row driven; 0 mismatches against the baseline-unioned expectation (artifact /tmp/qa3/A/ap0a/crud-verdicts.json)
    N0 pass first denied cell per set re-run as admin: auditor×invoice create 201, client_liaison×client_brief delete 200, contributor×invoice delete 200, executive×inquiry edit 200, guest_portal×announcement create 201, manager×contact create 201, member_default×account create 201, ops×invoice create 201 (plus the admin payload …
    N1 pass no withheld cell answered 2xx

    access-security.owd-sharing-matrix · rev 9 · PASS

    clause verdict evidence
    A0 pass private (showcase_private_note): A's note absent from B's list; B by-id 404 RECORD_NOT_FOUND; B PATCH 404, admin re-read body "orig"; A own read 200, own PATCH 200
    A1 pass public_read (showcase_announcement, admin-owned): B list contains it, B by-id 200; B forged PATCH 403 PERMISSION_DENIED, admin re-read title unchanged
    A2 pass ops PATCH of the same foreign announcement 200, admin re-read shows "OWD pr ops …"
    A3 pass public_read_write (showcase_project, admin-created {name,status:'planned',account}: 201): C1 GET 200, PATCH name 200 and persisted; A (member_default) identical PATCH 403 PERMISSION_DENIED, name unchanged
    A4 pass controlled_by_parent: C1 invoice {owner: C1 email, non-churned account, draft} 201 + line 201; C2 list excludes the line, by-id 404, PATCH 404 with admin re-read unchanged; C1 list contains it, by-id 200, PATCH own 200 persisted. (D11 cross-reference: the private-handle verdict is carried once on access-security.rls-b…
    A5 pass auditor (viewAllRecords on showcase_invoice_line) list contains C1's line, by-id 200
    A6 pass all four models driven: private / public_read / public_read_write / controlled_by_parent, per-persona verbs above
    N0 pass B's direct forged PATCH on the public_read announcement → 403, row unchanged
    N1 pass C1 runs the identical line probes and is served (list contains, by-id 200, PATCH 200) — C2's denial is derivation, not a broken route

    access-security.anonymous-deny-surfaces · rev 5 · PASS

    clause verdict evidence
    A0 pass anonymous: GET /data/showcase_private_note 401, GET /meta 401, POST /actions/showcase_task/showcase_mark_done/anon-probe-id 401, GET /automation/_status 401, POST /batch {operations:[]} 401, GET /security/explain 401, analytics POST /query 401, POST /sql 401, GET /meta 401 (plus PUT /meta/object/qa_anon_probe 401) — e…
    A1 pass POST /api/v1/actions/showcase_task/showcase_mark_done/anon-probe-id → 401 {"success":false,"error":{"code":"UNAUTHENTICATED",…,"httpStatus":401}}, not 404/400
    A2 pass each body matches exactly one family: rest-flat {"error":"UNAUTHENTICATED","code":"UNAUTHENTICATED","message":"Authentication is required to access this endpoint."} for /data /meta /batch /security (and the meta PUT); dispatcher-wrapper {"success":false,"error":{code,message,httpStatus:401}} for /actions /automati…
    A3 pass anonymous: /analytics/query and /analytics/sql with the valid body {cube:'showcase_task',measures:['count']} 401, with the malformed {filters:{done:true}} 401 (not 400), /analytics/meta 401 — all dispatcher-wrapper UNAUTHENTICATED. Signed-in member: query 200 (rows [{count:10}]), sql 200 (sql echo), meta 200 (cube lis…
    A4 pass invited member: GET /data/showcase_private_note 200 {"records":[],"total":0}, GET /meta 200 (types list)
    A5 pass anonymous GET /api/v1/forms/contact-us → 200 serving the showcase_inquiry form (slug contact-us), not 401
    A6 pass one verdict per variant recorded above: data, meta, actions, automation, batch, security-explain, analytics ×3 (+ malformed ×2)
    N0 pass anonymous DELETE /api/v1/automation/showcase_reassign_wizard → 401 dispatcher-wrapper UNAUTHENTICATED; admin GET /api/v1/automation/showcase_reassign_wizard afterwards 200 (flow still registered)

    access-security.no-active-org-session-semantics · rev 10 · PARTIAL

    clause verdict evidence
    A0 partial isolated, producer (2): GET /data/showcase_account 200 0 rows, /data/showcase_task 200 0 rows (rows exist: operator reads 58 accounts across orgs). group, producer (2): accounts 200, 15 rows, every organization_id = org A (the member's only membership), the row of org X (non-member) absent; tasks 11 rows all org A — t…
    A1 pass showcase_task create (member entitled to the verb) with no active org → 403 PERMISSION_DENIED "[Security] Access denied: 'showcase_task' is scoped to an organization, and this session has no active organization — so this insert has no organization to place the record in. Join or select an active organization and retry…
    A2 pass admin-side (direct read of the run's own DB) NULL-or-empty organization_id counts: isolated showcase_account 0→0, showcase_task 0→0 (totals 57→57, 40→40); group task 0→0 (total 31→31), task/account/project/private_note 0/0/0/0 → 0/0/0/0; no row produced (operator and member read-backs 0)
    A3 pass isolated: org owner of qa-org-b holding the org-scoped organization_admin set (+ manage_metadata via fixture row), cleared its active org: accounts 200 0 rows, tasks 200 0 rows (another tenant's rows exist — 58 accounts on the operator's read); account write 403, private-note write 403 naming the missing organization;…
    A4 pass isolated: operator GET /data/sys_audit_log?user_id= 200 = the raw telemetry-store rows exactly (8 rows: login A, update, update, login A, update, logout tenant_id NULL, login A, update); member's own read 403 PERMISSION_DENIED. group: operator read login A, login A, logout tenant_id NULL (raw adds the later lo…
    A5 pass the no-org session: get-session 200, GET /meta 200, GET /meta/object/showcase_account 200, GET /data/sys_user 200; no 5xx anywhere (0 "Unhandled error" / 500 lines in either walled log)
    A6 pass three-way: sharing — no rule administration and an org-scoped grant confers no rows (A3); Layer 0 wall + write path — reads empty (isolated) / union of memberships (group), writes refused naming the missing org, nothing NULL-stamped (A0–A2, A7); audit ledger — rows written for every login/logout, read through object p…
    A7 pass group: member of A (non-empty membership set) with no active org POST /data/showcase_task {title, project: A's project} → 403 PERMISSION_DENIED (the message above), not 5xx (no SystemWriteOrganizationRequiredError in the log); NULL-or-empty organization_id count 0 → 0; no row produced. (The project/account in A were c…
    N0 pass no cross-tenant read or write by the org-less org-scoped holder
    N1 pass no HTTP-created row stamped NULL (counts unchanged)
    N2 pass audit rows with NULL tenant_id are readable by the operator through object permission (ADR-0131 D7) — not invisible
    N3 pass no 500 / unhandled rejection on read, write or audit paths
    N4 pass every tenant-scope refusal names the missing active organization; the only generic 403s are verbs the persona lacks even with an active org (showcase_account create by member_default — measured 403 while active too)
    N5 pass code PERMISSION_DENIED (ledgered)

    access-security.walled-environment-metadata-operator-only · rev 8 · PASS

    clause verdict evidence
    A0 pass (pin) pins green at 8e3d4a5: meta-migrate-stored-walled-refusal.test.ts 6✓, packages-walled-write-refusal.test.ts 32✓, automation-walled-definition-write.test.ts 12✓, meta-walled-catalog-refusal.test.ts 19✓, wall-environment-metadata-refusal.dogfood.test.ts 23✓, wall-catalog-refusal.dogfood.test.ts 17✓, wall-package-doors-…
    A1 pass owner (manage_metadata, not operator) on the walled boot: PUT /meta/object/qa_walled_probe 403 FORBIDDEN; PUT /meta/permission/showcase_member_default 403 FORBIDDEN; POST /meta/view/showcase_task.in_progress/publish 403 FORBIDDEN; DELETE /packages/com.example.showcase 403 PERMISSION_DENIED; POST /security/permission-s…
    A2 pass operator's identical PUT /meta/object/qa_walled_probe on the walled boot → 200 "Saved object 'qa_walled_probe' (env-wide, state=active)", read-back 200, then DELETE 200 → 404; single-posture admin PUT of the same body on port 41210 → 200, read-back 200, DELETE 200 → 404
    N0 pass no environment-metadata write by the org owner succeeded on the walled boot

    ai.mcp-stdio-fail-closed · rev 9 · PASS

    clause verdict evidence
    A0 pass OS_MCP_STDIO_ENABLED=true, key unset: exit 1, 0 bytes on stdout, health 000 afterwards; stderr "✗ Plugin com.objectstack.mcp failed to start - rollback complete: [MCP] The stdio transport is enabled (OS_MCP_STDIO_ENABLED / autoStart) but OS_MCP_STDIO_API_KEY is not set. stdio must run under a real identity — mint an A…
    A1 pass osk_unknown: exit 1, not served, "[MCP] OS_MCP_STDIO_API_KEY did not resolve to a valid identity (unknown / revoked / expired / owner-less). Refusing to start stdio (ADR-0101)."; revoked member key (after step 7): exit 1, not served, "[MCP] OS_MCP_STDIO_API_KEY is refused (API_KEY_REVOKED): This API key (osk_uj9nRs2l……
    A2 pass --log-level info stderr: "INFO [MCP] stdio transport principal-bound to OS_MCP_STDIO_API_KEY identity e3mgB4UzhjDVakWpZ3rnOE7bsGw9ivGo (RLS/FLS/tenant applied)" (= the contributor's userId); liaison session: "… identity vaCJhar3GZAJicUqbwLffn3jUKPsPuKQ …". stdout carried only JSON-RPC lines (0 non-JSON lines)
    A3 pass RLS (contributor, showcase_invoice, limit 50): MCP query_records ids [cwKVhA8F_K4KJUv8] = REST GET (member key) ids, 24 = 24 fields; MCP get_record INV-1003 → isError "Record "6lPg_joV3Kfer_AL" not found"; REST member GET → 404 RECORD_NOT_FOUND; admin key REST list 13 rows incl. 6lPg_joV3Kfer_AL, admin GET 200. FLS …
    A4 pass tools/list carries aggregate_records (12 tools). Member MCP count by status {"rows":[{"status":"draft","n":1}],"totalGroups":1} = member REST POST /api/v1/data/showcase_invoice/query [{"status":"draft","n":1}]; admin REST draft 3, paid 5, sent 5 — member counts strictly lower on every bucket it cannot fully see
    A5 pass liaison MCP aggregate sum(budget) → isError {"error":{"code":"PERMISSION_DENIED","message":"[Security] Field read denied: not permitted to aggregate [budget] on 'showcase_project'","status":403}}, no number; groupBy budget → same refusal naming [budget]; count_distinct(spent) → same naming [spent]; controls: count g…
    A6 pass same live session: pre-revoke query_records returned the member's row; PATCH /api/v1/data/sys_api_key/qIen_XBx8dys6hT2 {"revoked":true} with the ADMIN API KEY → 403 PERMISSION_DENIED "Changing an API key requires a signed-in session…" (nothing written); with the admin's signed-in session bearer → 200; next query_recor…
    A7 pass no stdio, no key, os start … --log-level info: health 200; "INFO [MCP] Transport not auto-started (HTTP is served per-request at /api/v1/mcp regardless). Set OS_MCP_STDIO_ENABLED=true or autoStart …"
    N0 pass unknown, blank, tampered and revoked keys all refuse to start (exit 1, nothing listening); no anonymous serving
    N1 pass A3: MCP rows = REST rows per member
    N2 pass A4: member MCP draft 1 ≠ admin 3, paid/sent absent for member
    N3 pass A5: refused, no number
    N4 pass A6: post-revocation read served no row and carried code API_KEY_REVOKED

    api-backend.query-contract-matrix · rev 9 · PASS

    clause verdict evidence
    A0 pass 24 operator variants, each in 3 spellings ($-object body, AST body, GET ?$filter AST) — id-set equality with the local expectation on all 72: $eq(9) $ne(7) $gt/$gte(9) $lt/$lte(5) $in(14) $nin(2) $between 5M–50M(10) $contains 'orth'(1) / 'ORTH'(0, case-exact) $notContains 'o'(8) $startsWith 'Con'(1) / 'con'(0) $endsWi…
    A1 pass GET ?$orderby=name&$top=3&$skip=2 = [Fabrikam, Globex, Hooli] = rows 3–5 of the local name sort; POST {orderBy name asc, limit 4, offset 6} and {top 2, offset 10} equal their local slices
    A2 pass GET ?$select=name,status and POST {fields:['name','status']}: every record's key set exactly {name,status} (no id added); POST showcase_invoice {fields:['account.name']} → 400 INVALID_FIELD, message names expand
    A3 pass full annual_revenue sequence asc (GET $orderby) and desc (POST orderBy, GET $orderby desc) equal the local sort (nulls first asc / last desc); $orderby=no_such_field and orderBy no_such_field → 400 INVALID_SORT, no records key
    A4 pass admin-made invoice owned by the contributor with contact Nora West: contributor POST /query {expand:{contact:{object:'showcase_contact',fields:['name']}}} → 200, own invoice row present, contact NOT expanded (raw id only, no name); admin same query → contact {"name":"Nora West"}. Contributor direct GET of the contact …
    A5 pass POST /showcase_task/query {where:{status:{$nin:'done'}}} → 400 VALIDATION_FAILED, fields[0].field query.where.status.$nin, message 'Operator "$nin" on field "status" requires an array of values…'; AST ['status','not_in','done'] → 400 VALIDATION_FAILED, fields[0].field query.where, message names $nin, status and an ARR…
    A6 pass ?$pageSize=5 → 400 UNSUPPORTED_QUERY_PARAM listing $top, $skip, $orderby, $select, $count, $search, $searchFields, $filter, $expand; ?not_a_field=x → 400 INVALID_FIELD; where {not_a_field} (POST) and $filter on it (GET) → 400 INVALID_FIELD
    A7 pass showcase_task {status:{$in:[]}} → 200, 0 rows; {status:{$nin:[]}} → 200, 13 rows = full visible set (13)
    A8 pass one row per variant recorded above; no operator skipped (text/number/date/select/null all covered on account)
    N0 pass {"limit":"ten"} → 400 VALIDATION_FAILED (query.limit invalid_type); {"not_a_query_key":{…}} → 400 INVALID_FIELD; neither returned rows
    N1 pass ?$select= / ?$select=%20 / ?$select=, / ?select= / ?fields= → 400 VALIDATION_ERROR "Query parameter '' names no field"; ?$top=2&$count=maybe, ?$count=1, ?count= → 400 VALIDATION_ERROR "'' must be true or false"; POST {fields:[]} and {select:''} → 400 VALIDATION_FAILED fields[0] on the key; {limit:2…

    api-backend.packaged-action-disabled-dispatch · rev 13 · PASS

    clause verdict evidence
    A0 pass stock boot: no ledger row for showcase_mark_done (absence = active). POST /actions/_activation/showcase_task/showcase_mark_done {"enabled":false} → 200; ledger row {metadata_type action, name showcase_mark_done, package_id com.example.showcase, active false}, KEY SET active,created_at,created_by,id,metadata_type,name,…
    A1 pass showcase_action_param_gallery disabled, body {recordId bogus, params:{bogus_key:1}} → 409 ACTION_DISABLED; re-enabled, identical request → 400 VALIDATION_ERROR "Invalid action params: Action param "p_text" is required; Unknown action param "bogus_key" — not declared on this action"
    A2 pass every disabled trace (mark_done, gallery) is 409 with the bogus recordId in play and no side effect; corroboration: enabled mark_done on the same bogus id → 404 RECORD_NOT_FOUND (boot 3)
    A3 pass showcase_zoo_perm_missing enabled → 403 PERMISSION_DENIED "…requires capability [showcase.restricted_ops] — caller is missing [showcase.restricted_ops]"; disabled → identical 403; byte-compare equal (no 409 leak)
    A4 pass showcase_portfolio_snapshot disabled (POST /actions/_activation/global/showcase_portfolio_snapshot 200); MCP tools/call run_action {actionName} → result.isError true, content {"error":{"code":"ACTION_DISABLED","message":<the same sentence as the REST door>,"status":409}}; re-enabled → ok true, result {accounts 16, p…
    A5 pass after the cold restart (boot 3 log: [ObjectQLPlugin] Activation ledger: 2 packaged action(s) are switched off … 'showcase_bulk_reassign', 'showcase_mark_done'): mark_done → 409 ACTION_DISABLED; re-enable 200 → Mark Done on the real task 200, task re-reads done true / progress 100; ledger row same id WVeNET-BfAeUn-I3…
    A6 pass showcase_bulk_reassign (type flow) disabled → POST /actions/showcase_task/showcase_bulk_reassign → 409 ACTION_DISABLED with the turned-off sentence (names no action); showcase_reassign_wizard runs list identical before/after (empty), no ledger row for the wizard; still 409 after restart, re-enabled after
    N0 pass no disabled action answered 400 or a record answer
    N1 pass 403 wins over the switch (A3); no ACTION_DISABLED on any action with no ledger row (stock gallery answered its param 400 before any flip — the S3 enabled leg)
    N2 pass anonymous POST /actions/showcase_task/showcase_mark_done → 401 UNAUTHENTICATED (dispatcher wrapper)

    approvals.account-app-entry · rev 9 · PASS

    clause verdict evidence
    A0 pass framework SHA 8e3d4a5; pnpm console:build run by the dispatcher on this checkout, ready line @objectstack/console:build:dist: ✓ @objectstack/console dist ready (85648 KB) from ui/apps/console, with the tracked sdui.manifest.json (/tmp/qa3/A-console.log, 09:10Z); GET /_console/ on this boot loads assets/index-BBs…
    A1 pass avatar menu → Profile → /_console/apps/com.objectstack.account/component/account/profile_card → sidebar Inbox › Approvals → /_console/apps/com.objectstack.account/component/approvals/inbox; screenshot: "Approvals Inbox — Review and act on approval requests.", tabs My Pending (2) / Submitted by me / All, search box, Ne…
    A2 pass persona session GET /api/v1/meta/app → 200 names [showcase_app, account] (no setup); account nav 8 leaves incl. nav_account_approvals; GET /api/v1/meta/app?id=setup → 200 {"items":[]}; same session rendered the inbox (A1)
    A3 pass #32955 fix verified. persona GET /api/v1/meta/object/sys_approval_request → 200 (curl and every in-page load, 4×200 in the trace). Row click → /console/apps/com.example.showcase/sys_approval_request/record/areq… (both runs); screenshot: "Invoice Dual Sign-off (parallel approval) · Approval Request", As submitted…
    A4 pass fresh tab cold-load /_console/apps/com.objectstack.account/component/approvals/inbox?request=areq_7a05df5e-… → /_console/apps/com.example.showcase/sys_approval_request/record/areq_7a05df5e-…, history.length 2 (replace, no extra entry), page shows that request (QA-20261113093217) with Approve + Reject; same id via /_co…
    A5 pass inbox row record link href = /console/apps/com.objectstack.account/showcase_invoice/record/zB5tlZdUuLjxo49 (keeps the inbox's com.objectstack.account segment; both runs); click (S7) landed on that exact URL and rendered the invoice record (QA-20261113093217); persona GET /api/v1/data/showcase_invoice/zB5tlZdUuLjxo49…
    A6 pass avatar menu › Preferences › Language (globe button) offers "English ✓" / "简体中文" → 简体中文; GET /api/v1/auth/me/localization → locale "zh-Hans"; screenshot: nav entry 待我审批, header 审批中心, tabs 待我审批 (1) / 我发起的 / 全部, status badge 待审批, hint 快捷键:j/k 移动 · Enter 打开 · x 选择 · a 通过 · r 拒绝
    N0 pass no component-not-found / blank pane on either run
    N1 pass Approvals entry present for the non-admin (rendered + nav_account_approvals in meta/app) while setup is absent ({"items":[]})

    automation.packaged-flow-disable-durable · rev 10 · PASS

    clause verdict evidence
    A0 pass baseline urgent showcase_task {title, project, priority:'urgent'} 201 → runs 1 → 2 (completed). POST /automation/showcase_urgent_task_alert/toggle {"enabled":false} → 200; GET /data/sys_metadata_activation: exactly one row {id xvO60HP1Yg_L2oYl, metadata_type flow, name showcase_urgent_task_alert, package_id com.exampl…
    A1 pass POST /automation/showcase_urgent_task_alert/trigger → 409 FLOW_DISABLED, message exactly "This flow is turned off for this deployment. An administrator can turn it on again in its package's Automations in Studio, or copy it there under a new name." — no sys_metadata_activation, no ADR
    A2 pass boot 3 log (info): [Automation] Activation ledger: 1 packaged flow(s) are switched off for this installation and were left unbound — 'showcase_urgent_task_alert'.; GET /automation/_status → {enabled:false, bound:false, status active, triggerType record_change}
    A3 pass post-restart urgent create 201; runs list 2 → 2 (no new run) after 3 s
    A4 pass toggle {"enabled":true} → 200; ledger re-read: SAME id xvO60HP1Yg_L2oYl, active true, updated_at 09:22:44.808Z (created_at unchanged) — still present; _status enabled/bound true; final urgent create → runs 2 → 3, newest run completed for the new task
    N0 pass 2xx toggle wrote its row (A0); no "IN PROCESS ONLY" warning in the boot-2 log
    N1 pass restart did not re-arm: enabled/bound false and no run from the post-restart create
    N2 pass re-enable updated the row in place (same id, active true)
    N3 pass the ledger refusal carries the turned-off sentence, not "Flow 'showcase_urgent_task_alert' is disabled"

    cli.dev-boot-contract · rev 9 · PASS

    clause verdict evidence
    A0 pass S0 objectstack dev --ui --seed-admin -p 41235 -d file:/tmp/qa3/A/ap0c/dev/data.db (default compile into the copy's own dist): boot log "🎯 Environment ID: env_local / 📦 Artifact: dist/objectstack.json / 🗄️ Database: file:/tmp/qa3/A/ap0c/dev/data.db"; health 200 after ~15 s; GET /api/v1/ready 200 `{"success":true,"…
    A1 pass restart on the same DB with --no-compile --no-watch --admin-password changed99: POST /api/v1/auth/sign-in/email admin123 → 200, changed99 → 401 {"code":"INVALID_EMAIL_OR_PASSWORD"} (judged by auth responses only)
    A2 pass Database lines per tier: explicit -d file:/tmp/qa3/A/ap0c/dev/data.db → that URL; explicit -d :memory: → :memory: (health 200); env OS_DATABASE_URL=file:/tmp/qa3/A/ap0c/dev/envtier.db → that URL; config-declared default (dsproj: datasource qa_home sqlite config.filename: '.objectstack/data/qa_home.db' + da…
    A3 pass --fresh: "🧪 Fresh OS_HOME: /tmp/objectstack-dev-h9yURl", "🗄️ Database: file:/tmp/objectstack-dev-h9yURl/data/objectstack.db" (same unified filename); dir held data/, uploads/, dev-crypto-key while up; after SIGINT ls → No such file or directory; same for a second run stopped by SIGTERM (/tmp/objectstack-dev-U5J3…
    A4 pass instance B (-p 41235 while A held 41235): "➜ API: http://localhost:41236/" and "↪ server bound to port 41236 (requested 41235)"; GET :41236/api/v1/health 200
    A5 pass touch src/ui/actions/index.ts (2 min newer than dist/objectstack.json), server down, --no-compile: "⚠ dist/objectstack.json is OLDER than your sources — this boot serves the STALE build. / newest source: src/ui/actions/index.ts (2m newer than the artifact) / fix: run objectstack build or start with --compile."…
    A6 pass dev/empty (no config, no --artifact): exit 1; stdout "✗ Config file not found in /tmp/qa3/A/ap0c/dev/empty"; stderr "Run in a directory with objectstack.config.ts, pass --artifact <path
    A7 pass Playwright, after health 200: /_console/ → /_console/login, title ObjectOS; screenshot showed the "Welcome back" sign-in card (Email, Password, Sign In, "Sign in as dev admin" under "Local development instance"); form sign-in → POST /api/v1/auth/sign-in/email 200 → /_console/home
    N0 pass default (nothing chosen) DB: created showcase_category "qa-ap0c persist probe" (201), restarted with nothing chosen, filtered read → total 1, same id QeidYLdxaQ9wPkVD
    N1 pass --database-driver memory → exit 2, oclif "Expected --database-driver=memory to be one of: sqlite, sqlite-wasm, postgres, mysql, mongodb, turso"; OS_DATABASE_DRIVER=mingo → exit 1, refused before any Database line (0 Database lines), naming os dev --fresh and :memory:; -d memory:// → parent printed "🗄️ Datab…
    N2 pass see legacy row
    N3 pass see A4: the printed URLs name 41236, the actual port

    cli.organization-ownership-ceremony · rev 9 · PASS

    clause verdict evidence
    A0 pass (pin) pins green at 8e3d4a5: organization-ownership.apply-flags.test.ts 7✓, organization-ownership-apply.test.ts 117✓, organization-ownership.occupancy.integration.test.ts 5✓, organization-ownership.dialect.test.ts 19✓, apply-organization-ownership-gate.integration.test.ts 5✓, comment-reactions.integration.test.ts 7✓, orga…
    A1 pass after c1's refused os serve (exit 1): bare os migrate organization-ownership --database-url file:c1.db --out plan.json exit 0, "✓ Plan written … — no row or table in the database was changed"; plan kind organization-ownership-plan, ceremony ADR-0131 D10, inventoryDigest 650f0635…a23260b4, 154 table entries ("1…
    A2 pass c1 --apply --plan-file plan.json --backup-taken --out apply.json --json exit 0; report complete true, blocking 0, steps attribution, authored-conversion, metadata-promotion, template-promotion, settings-move, sibling-moves, declared-webhooks, declared-rule-copies, rewrite, mirror-deletion, history-renumber, column-d…
    A3 pass unmigrated c4 production os serve: exit 1, 0 "Server is ready"/listening lines, "✗ This database carries no organization-ownership marker: … Run 'os migrate organization-ownership --out PLAN' … 'os migrate organization-ownership --apply --plan-file PLAN --backup-taken'", 0 hits 17.x release (no --dev on the produc…
    A4 pass fresh c5 (v17, no v18 boot): os migrate apply --allow-destructive --yes --json first refuses refused_comment_reactions_unconverted (exit 1, created [] applied [], sha = golden); os migrate comment-reactions --apply --yes twice exit 0 ("Converted 0 reaction(s) … recorded as verified"); then apply --allow-destruct…
    A5 pass n5 first os serve --dev: exit 1, no ready line, "This database carries no organization-ownership marker … sys_flow_credential, sys_secret still carry …" with --dev ceremony commands. Plan --dev exit 0; apply --dev exit 0, complete true, blocking 0 (sibling-moves moved 10, jobsRewritten 2; declared-webhooks mirrorsDe…
    N0 pass every refusing arm left the DB unchanged: apply-without-plan (sha equal), drop gate ×2 (sha equal + 0-row diff), comment-reactions --apply without --yes (c6 sha b090628a before = after, exit 1 "Re-run with --yes"); the bare plan changed no checksum (A1)

    identity-auth.default-organization-boot-invariant · rev 4 · PASS

    clause verdict evidence
    A0 pass (pin) pins green at 8e3d4a5: ensure-default-organization.test.ts 57✓, identity-objects-default-organization.pin.test.ts 4✓
    A1 pass boot log: 09:12:14.987Z INFO [default-org] created the Default Organization (org_mwerd5s8ogmxyh1c) precedes 09:12:22.897Z INFO [default-org] promoted the platform admin to owner of the default organization; sys_organization row {id org_mwerd5s8ogmxyh1c, slug default, created_at 09:12:14.984Z} vs first sys_user adm…
    A2 pass sys_member?organization_id=org_mwerd5s8ogmxyh1c (200): admin role owner; the admin/create-user member (create-user 200, mustChangePassword:true, organizationId org_mwerd5s8ogmxyh1c) role member; an invited sign-up role member. get-session: admin activeOrganizationId org_mwerd5s8ogmxyh1c; member (after change-pas…
    A3 pass GET /api/v1/data/sys_business_unit (200): all 5 seeded rows (bu_acme, bu_field_ops, bu_west_coast, bu_east_coast, bu_hq_finance) organization_id = org_mwerd5s8ogmxyh1c, none NULL
    N0 pass no seeded sys_business_unit row with NULL organization_id

    integration-system.datasource-credential-refusal-matrix · rev 9 · PASS

    clause verdict evidence
    A0 pass doors enumerated from packages/spec/src/migrations/entries/semantic (17.datasource-config-inline-credential-refused, -url-userinfo-, -placeholder-, 18.-url-query-credential-, -mongo-options-credential-, -options-nested-credential-spelling-, 17.connector-inline-authentication-publish-refused) + the retired turso encryp…
    A1 pass POST /api/v1/datasources qa_ext_pg {driver postgres, config host/port/database/username, top-level secret} → 201 status "ok" (secret not echoed); POST /api/v1/datasources/qa_ext_pg/test → 200 {ok:true, latencyMs:25, tableCount:1}; again 200 after the reboot
    A2 pass full bodies grepped (admin): GET /api/v1/datasources, /datasources/qa_ext_pg, /meta/datasource/qa_ext_pg, /meta/datasource (list), /data/sys_metadata?type=datasource → no PG password, no canonical S3cret value. Shapes agree: admin door config {host,port,database,username} + hasSecret true + redactedConfigKeys []; meta…
    A3 pass admin door /datasources/qa_ext_pg → hasSecret: true; /datasources/qa_nosecret_pg (same config, no secret; status error "client password must be a string") → hasSecret: false, redactedConfigKeys [] — distinguishable, set one not null
    A4 pass planted with server stopped: qa_alias_pg config passwd/pwd/token/jwt/auth_token/authtoken (6 distinct values) and qa_turso_enc {url:file:…, encryptionKey}; boot logs metadata_spec_invalid for both (refuses them as current-spec); reads: admin door config {host,port,database,username}, redactedConfigKeys [auth_token, au…
    A5 pass plugin driver acme-warehouse via POST (201, status error "no driver factory supports driver 'acme-warehouse'") with url acme://qa:…@host, password, authToken, apiSecret: admin door config {url:"acme://qa@acme.example:9000/db", apiSecret:}, redactedConfigKeys [authToken, password, url]; metadata door and da…
    A6 pass GET /datasources/qa_ext_pg → config (no withheld keys) → PATCH /datasources/qa_ext_pg {config: } → 200 status ok; PUT → 405 Allow: DELETE, GET, HEAD, PATCH; after: hasSecret true, credentialsRef handle unchanged (sys_secret:sec_61c6… = before), POST …/test → 200 {ok:true, tableCount:1}
    A7 pass every read door as Mei (non-admin) → 403, anonymous → 401, on all 13 paths pre- and post-plant; none reveals any value; anonymous POST /api/v1/datasources → 401
    N0 pass 0 canonical/alias/PG cleartext in any of the 78 read responses (only the boundary apiSecret, admin-only)
    N1 pass every door refuses at build AND at runtime (A0)
    N2 pass admin and metadata doors redact identically (A2/A4/A5)
    N3 pass alias spellings redacted (A4)
    N4 pass plugin driver's password/authToken/URL-userinfo redacted on both doors (A5)
    N5 pass echoed config PATCH kept the stored secret (A6); the mask literal is not served by any door (knownGaps[4]), not driven
    N6 pass every refusal names the replacement (external.credentialsRef / connection form secret field; placeholder → literal value or binder)

    platform-core.boot-health · rev 14 · PASS

    clause verdict evidence
    A0 pass curl …/api/v1/health → 200 on poll 35 (0.5 s cadence); time-to-healthy 17.7 s from the setsid launch
    A1 pass after the banner's last line printed, curl …/api/v1/ready → 200
    A2 pass Flows banner: 33 flow(s) 18 bound to triggers (record_change, schedule, time_relative, api) · 8 draft; exactly two unbound lines, both under ℹ (not ⚠), both the deployment-policy class ('time_relative' → showcase_task_due_reminder; 'schedule' → showcase_scheduled_digest; "package-authored scheduled work is off ……
    A3 pass GET /api/v1/meta/app (no query) → names [showcase_app, setup, account]; GET /api/v1/meta/app?id=showcase_app → 200 {type, items[1]}, item keys name,label,icon,branding,active,isDefault,navigation,_packageId,_packageVersion,_provenance,_diagnostics; 51 nav entries = 25 page + 17 object + 5 report + 3 dashboard leaves +…
    A4 pass grp_data diff vs examples/app-showcase/src/ui/apps/index.ts (lines 45–66): source 12 ids, served 12, missing [] extra [] — showcase_project, _task, _account, _contact, _invoice, _expense_report, _product, _team, _category, _business_unit, _field_zoo, _cascade
    A5 pass GET /_console/ → 200 content-type: text/html; charset=utf-8, 24124 bytes, loads assets/index-BBsDDmqN.js (byte-identical to packages/console/dist, sha256 e917fddb…)
    N0 pass Seeds row Seeds: com.example.showcase 138 rows (plain, no ⚠, no N ok / M errors, no lost links); no SeedLoader/Seeder/dropped line in the boot window
    N1 pass health 200 AND 0 ERROR lines in the boot window (window ended at the first non-health request, after the banner)

    platform-core.seed-integrity · rev 7 · PASS

    clause verdict evidence
    A0 pass Expected re-derived from ShowcaseSeedData (src/data/seed/index.ts imported via tsx: 20 datasets, 138 rows, matches the step-1 baseline and dist/objectstack.json). GET /api/v1/data/?$top=1&$count=true as admin → 200 on all 20; total == authored on all 20 (account 14, contact 32, inquiry 3, product 4, project 5, ta…
    A1 pass Specimen — Full read (200): 36 authored fields diffed; every one equal except f_password, which reads •••••••• (expected-masked by declaration, not a mismatch). f_multiselect ['red','green'] as a set, f_lookup = Northwind's id LVOXRxfyimyeQeop, f_lookups = [Northwind, Contoso] ids, f_master_detail = Website Relaunch…
    A2 pass Boot 1 banner Seeds: com.example.showcase 138 rows (plain, no ⚠, no error/lost-link count); info line [Seeder] Seed loading complete {"inserted":138,"updated":0,"skipped":0,"errored":0}; no dropped record(s) line in either boot log
    A3 pass Restart on the same DB: [Seeder] Seed loading complete {"inserted":0,"updated":0,"skipped":138,"errored":0}; second sweep 20/20 equal to authored, diff against sweep 1 empty
    A4 pass expected-absent: Specimen f_user null, f_users null, f_secret null; showcase_task.cover null on all 10 seeded tasks — recorded as expected-absent, not findings
    N0 pass no nav-visible seeded object reads 0 (smallest is showcase_preference 1, the authored count)
    N1 pass no count grew on restart (diff empty)
  2. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

    Per-clause tables (2)

    platform-core.console-login · rev 5 · PASS

    clause verdict evidence
    A0 pass form sign-in (#login-email / #login-password, Sign In) → POST /api/v1/auth/sign-in/email 200 → landed /_console/home; screenshot: ObjectOS header + search + avatar "DA", first-run checklist card, "Good morning, Dev Admin.", Your apps → Showcase tile, My work; no sidebar (by design). Both halves present: cookie better-…
    A1 pass after page.reload(): still /_console/home; first authed requests GET /api/v1/auth/get-session 200, /auth/me/permissions 200, /discovery 200; in-page GET /api/v1/meta/app?id=showcase_app → 200 items[1]; no redirect to login
    A2 pass in-page POST /api/v1/auth/sign-out → 200 {"success":true}; same-page GET /api/v1/meta/app?id=showcase_app (bearer still in localStorage) → 401 {"code":"UNAUTHENTICATED"}; leg-a trace: GET /api/v1/meta/object?package=com.example.showcase 401, /meta/app/showcase_app/bundle 401, /data/showcase_task?top=0 401 (r2)
    A3 pass (a) in-app nav, no reload — r1 clicking the Showcase tile on Home → /_console/login?redirect=%2Fapps%2Fcom.example.showcase, screenshot: "Welcome back / Sign in to continue your work" form, no shell; r2 clicking sidebar "Tasks" inside the Showcase shell → /_console/login?redirect=%2Fapps%2Fcom.example.showcase%2Fshowc…
    A4 pass re-login from the login form → r1 landed /_console/apps/com.example.showcase/page/showcase_capability_map (the app's landing for redirect=/apps/com.example.showcase), r2 landed /_console/apps/com.example.showcase/showcase_task (exact route); screenshot r2: Tasks / All Tasks · 10 grid with rows; authed calls get-sessio…
    N0 pass wrong password → POST sign-in/email 401 {"message":"Invalid email or password","code":"INVALID_EMAIL_OR_PASSWORD"}, no Set-Cookie header, context cookies [] afterwards, URL stays /_console/login; visible alert "Invalid email or password. After several failed attempts, sign-in is paused for a while. Forgot your passwor…
    N1 pass no stale-shell after expiry: both legs redirected to login on 401 (see A3); 0 pageerror across both runs

    platform-core.nav-surfaces-render · rev 10 · PASS

    clause verdict evidence
    A0 pass automated.ref run AGAINST THIS BOOT: SMOKE_API_URL=http://localhost:41220 pnpm -C examples/app-showcase exec playwright test --config=playwright.config.ts e2e/showcase-smoke.spec.ts at main 8e3d4a5, 09:17:14→09:19:04Z: "Running 50 tests using 1 worker … 50 passed (1.8m)", exit 0; spec named (bulk-capability-gate /…
    A1 pass diff of GET /api/v1/meta/app?id=showcase_app leaves vs SURFACES (normalised to page/…, dashboard/…, report/…, reports, object, object/view/…, filter slices): served 50 leaves (25 page, 17 object, 5 report incl. nav_report_index → …/reports, 3 dashboard) + 1 separator nav_sep_reports; SURFACES 50; served-not-in-SURFACE…
    A2 pass /_console/apps/com.example.showcase/object_that_does_not_exist (loaded twice, fresh nav): screenshot shows Showcase header + full sidebar (50 links) and main "Object Not Found — The object "object_that_does_not_exist" does not exist in the current configuration. Check your app navigation settings or select a different…
    A3 pass none encountered: no render failure in the 50-surface suite or the probes, so the ErrorBoundary ("Something went wrong") was never needed; 0 occurrences of it on any probed route
    N0 pass from the suite run (50 passed; its main-content assertion refuses an empty )
    N1 pass from the suite run (its wait treats data-boot-splash / the splash lines as not-yet-rendered)
    N2 pass bad route kept the shell (header + nav) with a named state; no white page
    N3 pass from the suite run (bounded boundingBox poll; all 5 chart surfaces green)

    platform-core.builtin-apps-nav-render · rev 13 · PASS

    clause verdict evidence
    A0 pass admin walk of every sidebar link after expanding groups: Setup 48/48 and Account 8/8 rendered a titled surface (e.g. System Overview, Packaged actions, Users · All Users · 3, Organization → Default Organization record, Position holders list, Approvals Inbox My Pending 4, Settings hub + 11 namespace forms, Audit Log, D…
    A1 pass GET /api/v1/meta/app (admin) → [showcase_app, setup, account]; ?id=setup → setup.app (requiredPermissions ["setup.access"]) groups group_overview(2)/apps(3)/people_org(7)/access_control(6)/approvals(4)/configuration(16)/diagnostics(3)/integrations(6)/advanced(1, expanded:false) = 48 leaves, filled by contributions (Po…
    A2 pass as Mei: /_console/apps/setup → data-testid app-access-denied "You don't have access to this app — This app exists, but your account is not authorized to open it. Ask an administrator to grant you access. [Back to home]" (no header/shell, not blank); /_console/apps/account → opens /apps/com.objectstack.account/componen…
    A3 pass Mei GET /api/v1/meta/app → [showcase_app, account]; forged GET /api/v1/meta/app?id=setup → 200 {"type":"app","items":[]}; forged GET /api/v1/meta/app/setup → 403 PERMISSION_DENIED; /api/v1/meta/app/setup/bundle → 403
    A4 pass member ?id=setup → 200 {"items":[]} (whole-app refusal, nav_api_keys + manage_platform_settings URLs unreachable). CONTROL only: admin vs member account nav 8 vs 8, diff [] (presence gates only)
    A5 pass Setup payload: nav_organizations (requiresService org-scoping) ABSENT on single-org; nav_business_units (requiresObject sys_business_unit) PRESENT and renders (Org Chart); nav_jwks absent (removed #7544); account entries all requiresObject/requiresService present
    A6 pass nav_sso_providers absent (OS_SSO_ENABLED unset — knownGap); sys_verification / sys_device_code entries absent; no pruned entry rendered as a dead list
    N0 pass none of 56 destinations blank / failed-to-load (Marketplace = named honest error, knownGap)
    N1 pass member denied Setup on the wire (A3) and in the UI (A2); Studio absent for everyone
    N2 pass no permission-gated entry served to the member (no setup app; account carries none)
    N3 pass member opens /_console/apps/setup/system/settings/localization → "You don't have access to this app" (app-access-denied, header 0, sidebar 0, no shell). Wire: GET /api/settings/NS as Mei → 403 SETTINGS_FORBIDDEN for all 11: localization, company, branding, access_policy (requires 'setup.access'), auth, mail, sms, stor…

    platform-core.v18-boot-refusals · rev 9 · PASS

    clause verdict evidence
    A0 pass (pin) pins green at 8e3d4a5: organization-ownership-boot-guard.test.ts 30✓, organization-ownership-boot-guard.kernel.test.ts 10✓, authz-identity-objects-boot-refusal.test.ts 3✓
    A1 pass exit 1; 0 listening/ready lines; printed refusal: "✗ This database carries no organization-ownership marker: 17.x or an 18.0 pre-release (18.0.0-next.*) created it, … sys_flow_credential, sys_job, sys_job_queue, sys_metadata, sys_metadata_commit, sys_metadata_history, sys_migration, sys_migration_journal, sys_secret s…
    A2 pass node:sqlite snapshot (WAL checkpointed, both files) before/after: 69 pre-existing primary tables, row-by-row diff over pre-existing columns = 0 differences (64 rows before and after); telemetry sibling 9 tables/56 rows, 0 differences; 64 tables created by the additive sync (showcase_, sys_approval_, sys_audit_log, s…
    A3 pass live rig (stack with one object qarig_thing + plugins: [new SecurityPlugin()], --preset minimal), os serve --dev --no-server --preset minimal exit 1: "✗ SecurityPlugin cannot boot: its authorization store reads sys_user, sys_member, and this kernel does not register sys_user, sys_member. Without them every authe…
    N0 pass the v18 serve exited before listening, with 0 row changes and no marker (A1/A2) — not served, not half-migrated

    records-forms.crud-roundtrip · rev 18 · PASS

    clause verdict evidence
    A0 pass sidebar Accounts → New (/showcase_account?form=new) → Account Name = os-qa-ap0b-094036, Lifecycle → Active, Industry left at its Technology default, website empty → Create: POST /api/v1/data/showcase_account body {"industry":"technology","status":"active","sales_region":"amer","name":"os-qa-ap0b-094036"} → 201 id mBRj…
    A1 pass record page Edit → Annual Revenue 1234567 → Update: PATCH /api/v1/data/showcase_account/mBRj2MFKO0fo_J2c body {"annual_revenue":1234567} → 200; full-row before/after diff = only annual_revenue null→1234567 and updated_at; every other key identical
    A2 pass r1 detail ⋯ menu (Refresh/Add to favorites/Share/Delete) → Delete → dialog 'Delete Account (Success Overlay) "os-qa-ap0b-094036"? Are you sure…' → Delete: DELETE …/mBRj2MFKO0fo_J2c 200; filtered GET → total 0. r2 via row ⋯ (Edit/Delete) → same dialog → filtered GET total 0
    A3 pass r2: list /showcase_account after full reload shows row 14 "os-qa-ap0b-r2-094329 · Active · Technology · —" among 17 records (screenshot); after delete + reload the name is gone (0 cells), r1 likewise 0 after reload
    A4 pass ?related=timeline → tabs Overview / Activity 2; Changes chip selected (screenshot): "Annual Revenue — → $1,234,567.00 · Dev Admin · just now" and "Owner — → Dev Admin; Account Name — → os-qa-ap0b-094036; Industry — → Technology; Lifecycle — → Active; Sales Region — → AMER · Dev Admin · just now" — option labels, forma…
    A5 pass POST name "os-qa-ap0b-094036-华宁" → 201; filtered GET returns it, name bytes end e58d8ee5ae81 (华宁 UTF-8) — byte-identical; $search=os-qa-ap0b-094036-华宁 → 1 hit; $search=华宁 → [华宁科技, os-qa-ap0b-094036-华宁]; list screenshot renders the CJK name
    A6 pass admin clone POST …/mBRj2MFKO0fo_J2c/clone {overrides:{name:"…-clone"}} → 201 {object,id:M3WPeIiXDjjiCFOO,sourceId:mBRj2MFKO0fo_J2c,record}; source-vs-clone diff = id, created_at, updated_at re-derived, name = override; business values (industry, status, sales_region, annual_revenue 1234567) copied. Owner half (substit…
    A7 pass premise: member GET /api/v1/data/showcase_invoice/kAuOU5ucO9LT8iAy (INV-1003) → 404; member POST …/kAuOU5ucO9LT8iAy/clone → 404 {"code":"RECORD_NOT_FOUND","error":"Record kAuOU5ucO9LT8iAy not found in showcase_invoice"}; admin invoice count 15 before and 15 after
    N0 pass New → Create with Account Name empty (both runs): form shows "Account Name is required", no POST issued (0 captured)
    N1 pass clones re-derive id/audit/autonumber and drop the readonly column (A6); RLS-invisible clone refused 404 (A7)
    N2 pass substitution recorded: POST showcase_task {"title"} (project required, no default) → 400 VALIDATION_FAILED fields [{field:project, code:required}]; also showcase_account {status} without name → 400 VALIDATION_FAILED fields [{field:name, code:required}]
    N3 pass POST showcase_account tax_id "12345" → 400 VALIDATION_FAILED fields [{field:tax_id, code:invalid_format, message:"Tax ID must look like 12-3456789."}]; filtered re-read 0 rows

    studio-authoring.first-run-loop · rev 13 · PASS

    clause verdict evidence
    A0 pass both legs: New Package dialog (Display name, Advanced → package-form-id-input = com.example.repairs, mdf-namespace auto 'repairs', version 0.1.0) → package-form-submit → POST /api/v1/packages 201, surface moved to /_console/studio/com.example.repairs/data with no reload; studio-pkg-chip reads the new name, and the swi…
    A1 pass after Publish → review sheet "Pending changes / 待发布的变更 · 1 item · 1 object · Repair Ticket repairs_repair_ticket New · Includes + Name name + Status status" → Publish all / 全部发布 → POST /api/v1/packages/com.example.repairs/publish-drafts 200, toast "Published 1 item in this package · Members can use it once a permissio…
    A2 pass Studio Records grid → add-record-button → "Create Repair Ticket" form (Name, Status picklist) → POST /api/v1/data/repairs_repair_ticket {"name":"Broken laptop","status":"in_repair"} 201; GET /api/v1/data/repairs_repair_ticket → [{name:"Broken laptop", status:"in_repair"}] both legs (en HlP8tquHOFgJphlx, zh 4sC42l0xBEU…
    A3 pass Create app dialog (App name Repair Center → identifier repair_center, "Add this package's objects as menu items" checked by default) → Save as draft → Interfaces pillar shows nav item Repair Ticket (nav_repairs_repair_ticket, the checkbox seeded it, confirmed not built) → Publish all → toast "Published 1 item…". Studi…
    A4 pass each leg's boot log carries exactly one "✓ Server is ready" (grep -c = 1 for st-en/boot.log and st-zh/boot.log), no restart line; nothing restarted between the package create and the end-user list; zero code written; git status --porcelain of the tree empty after both legs
    A5 pass forged authoring writes into the read-only showcase package, both legs: PUT /api/v1/meta/object/showcase_qa_probe?mode=draft&package=com.example.showcase&surface=studio → 422 WRITABLE_PACKAGE_REQUIRED "Cannot create object/showcase_qa_probe in package 'com.example.showcase': that package is read-only (provided by code…
    N0 pass both forged writes refused server-side, nothing persisted
    N1 pass app in the launcher, label chip rendered, both legs
    N2 pass every clause held on BOTH legs
  3. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

    Close-out 2026-11-13T10:39Z.

    No FAIL. Parked-on: #32908 · the three PARTIALs (dependencies #7463, #23267; walled-boot fixture gap)

  4. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

    Closed 2026-11-13T10:42Z: the standing smoke at 8e3d4a5 holds (no FAIL); close-out above.

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