Repository navigation
Description
QA run record for selector area:platform-core (bare area platform-core in the title for the roll-up), 18.0 release sweep, plan B (Tier 1 + Tier 2 driven, Tier 3 deferred). Text only — no screenshots; reproduction rules below.
Environment fingerprint
- framework:
5b14b7f3ad8e2e0786d228772f473d07b36b2477(objectstack-ai/objectstack, pinned — no pull) - console: vendored objectui build stamped
4016370af3c9c9dafec2bc5299afd8e8079701b8(=.objectui-sha), built withpnpm objectui:build; objectui-only pins run in a checkout at that same sha - app: examples/app-showcase, canonical boot
OS_PORT=P objectstack dev --ui --seed-admin -p P -d file:RUN/data.dbwith per-item OS_HOME / OS_STORAGE_LOCAL_ROOT; package-installing items booted from a scratch copy outside the repo - ports: each runner its own range (41100–42319), one file DB per item; seed: stock showcase (132 rows) +
--seed-admin - run window: 2026-10-19 12:26–13:45 UTC; host: 4 vCPU / 15 GB container shared by up to 7 parallel runners (load average peaked ~40; two boots timed out under load and were re-run serially — no verdict rests on a starved run)
- runner: one dispatching session + parallel runner sub-agents (not sequential); every persona identity proven server-side (
GET /auth/get-session) before each cell
Scope
Selector area:platform-core → 34 runnable items (node scripts/checklist-select.mjs area:platform-core --json). No planned items; none hidden as blocked. The 4 unpinned P0 items (boot-health, seed-integrity, console-login, builtin-apps-nav-render) are also driven by the separate priority:P0 gate card — independent second judgement by design.
| item | priority | surface | revision | tier | method | item verdict |
|---|---|---|---|---|---|---|
platform-core.boot-health |
P0 | mixed | 7 | 2 | manual browser+API | PASS |
platform-core.seed-integrity |
P0 | api | 3 | 2 | manual API | PASS |
platform-core.console-login |
P0 | browser | 4 | 2 | manual browser+API | PASS |
platform-core.nav-surfaces-render |
P0 | browser | 6 | 1 | pin (incl. objectui pins at the pin sha) + browser | PASS |
platform-core.metadata-registry-serving |
P1 | api | 2 | 1 | Tier 1 pin + live legs | PASS |
platform-core.metadata-authoring-roundtrip |
P1 | mixed | 3 | 1 | Tier 1 pin + live legs / pin (incl. objectui pins at the pin sha) + browser | PASS |
platform-core.builtin-apps-nav-render |
P0 | browser | 6 | 2 | manual browser+API | PASS |
platform-core.settings-hub-roundtrip |
P1 | mixed | 4 | 2 | manual browser+API | PASS |
platform-core.package-lifecycle-enable-disable |
P1 | mixed | 1 | 2 | manual browser+API | FAIL |
platform-core.notification-center |
P1 | mixed | 2 | 2 | manual browser+API | PASS |
platform-core.shell-nav-personalization |
P2 | browser | 4 | 3 | deferred | NOT-RUN |
platform-core.app-management-toggle |
P2 | mixed | 3 | 3 | deferred | NOT-RUN |
platform-core.docs-audience-gate |
P1 | api | 1 | 2 | manual API | PARTIAL |
platform-core.interrupted-migration-boot-report |
P2 | mixed | 2 | 1 | Tier 1 pin + live legs | PASS |
platform-core.manifest-install-contract |
P1 | api | 3 | 2 | manual API | PASS |
platform-core.activation-ledger-registration-home |
P1 | api | 5 | 1 | Tier 1 pin + live legs | PASS |
platform-core.activation-ledger-row-contract |
P2 | api | 4 | 1 | Tier 1 pin + live legs | PASS |
platform-core.packaged-object-extend-only |
P2 | api | 2 | 3 | deferred | NOT-RUN |
platform-core.marketplace-install-local-lifecycle |
P1 | api | 4 | 2 | manual API | PASS |
platform-core.marketplace-console-honesty |
P2 | browser | 5 | 3 | deferred | NOT-RUN |
platform-core.runtime-config-boot-read |
P1 | api | 1 | 2 | manual API | PASS |
platform-core.docs-portal-render |
P1 | browser | 2 | 2 | manual browser+API | PASS |
platform-core.url-overlay-contract |
P1 | browser | 1 | 1 | pin (incl. objectui pins at the pin sha) + browser | PASS |
platform-core.keyboard-shortcut-surface |
P2 | browser | 4 | 3 | deferred | NOT-RUN |
platform-core.home-admin-cluster-links |
P1 | browser | 3 | 2 | manual browser+API | PASS |
platform-core.lifecycle-retention-sweep |
P1 | mixed | 4 | 1 | Tier 1 pin + live legs | PASS |
platform-core.seed-mode-matrix |
P1 | api | 2 | 2 | manual API | PASS |
platform-core.theme-mode-persistence |
P2 | browser | 2 | 3 | deferred | NOT-RUN |
platform-core.console-installability-indicators |
P2 | browser | 1 | 3 | deferred | NOT-RUN |
platform-core.settings-audit-secret-fingerprint |
P1 | api | 2 | 1 | Tier 1 pin + live legs | PASS |
platform-core.v18-boot-refusals |
P0 | cli | 1 | 1 | Tier 1 pin + live legs | FAIL |
platform-core.protocol-18-handshake-and-stored-replay |
P1 | mixed | 1 | 1 | Tier 1 pin + live legs | PARTIAL |
platform-core.settings-organization-isolation-and-platform-rung |
P1 | api | 1 | 1 | Tier 1 pin + live legs | PARTIAL |
platform-core.serve-composition-v18 |
P2 | cli | 1 | 1 | Tier 1 pin + live legs | PARTIAL |
Tier 1 pin evidence: every file named in the area's automated.ref fields was run once, batched per package at the subject sha (RUNNER rule 6). Framework pins: 122 files, all green (cli 61 + nightly-tier e2e 2, core 72, metadata/-core/-protocol 393, objectql 274, platform-objects 44, plugin-audit 172, plugin-hono-server 38, plugin-security 192, plugin-sharing 69, dogfood 448 passed / 1 skipped [the attachments cross-tenant (g) dependency marker — scores no clause here], rest 86, runtime 291, service-automation 49, service-settings 117, service-storage 279, spec 42). A pin was cited only for the clauses it actually asserts; every clause it does not assert was driven live or is marked below.
Per-clause verdicts
by = runner; evidence is server truth (API / log / test output) in one line; UI clauses describe what the checked screenshot showed.
platform-core.boot-health (rev 7) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | D | curl /api/v1/health → 200 {"status":"ok","version":"18.0.0-next.5"}. Time to healthy was 23 s from spawn. |
| acceptance[1] | pass | D | GET /api/v1/ready → 200 {"status":"ready","state":"running"}. |
| acceptance[2] | pass | D | Banner: Flows: 32 flow(s) 19 bound … · 8 draft. Its only two non-ok lines are `ℹ 1 flow declares a 'time_relative' / 'schedule'… |
| acceptance[3] | pass | D | GET /_console/ → 200 text/html; charset=utf-8. GET /api/v1/meta/app?id=showcase_app → 200 with 1 item, keys `name,label,ico… |
| acceptance[4] | pass | D | Served grp_data has 12 entries (nav_projects … nav_cascade, from showcase_project to showcase_cascade). Diffed against `src/ui/ap… |
| negative[0] | pass (not triggered) | D | No SeedLoader rejection or dropped-record line in the log. |
| negative[1] | pass (not triggered) | D | No ERROR lines in the boot window. |
platform-core.seed-integrity (rev 3) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | A | Expected counts come from running tsx on the real ShowcaseSeedData export: 19 seed sets, 132 rows. I then called GET /api/v1/da… |
| acceptance[1] | pass | A | I read Specimen — Full with GET /api/v1/data/showcase_field_zoo?$filter={"name":"Specimen — Full"} and diffed it field by field a… |
| acceptance[2] | pass | A | Both boots (first and restart) print Seeds: com.example.showcase 132 rows in the plain form, with no ⚠ and no error or lost-lin… |
| acceptance[3] | pass | A | I restarted against the same DB and swept again. The per-object counts are identical to the first sweep (empty diff). All 132 (ob… |
| acceptance[4] | pass | A | These are expected to be empty, and they are. Specimen Full and Minimal: f_user null, f_users null, f_secret null. All 10 s… |
| negative[0] | pass (not triggered) | A | No nav-visible object has a count of 0. |
| negative[1] | pass (not triggered) | A | Counts did not grow on restart. |
platform-core.console-login (rev 4) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | D | Both runs landed on /_console/home. The screenshot shows the header, "Good afternoon, Dev Admin.", the "Your apps" launcher (Sh… |
| acceptance[1] | pass | D | Reloaded on /apps/com.example.showcase/page/showcase_capability_map. The first authed request after reload was `GET /api/v1/aut… |
| acceptance[2] | pass | D | POST /api/v1/auth/sign-out → 200 {"success":true}. The same bearer, still in localStorage, then got `GET /api/v1/meta/app?id=… |
| acceptance[3] | pass | D | On a full navigation after expiry, the console redirected to /_console/login?redirect=%2Fapps%2Fcom.example.showcase, and the s… |
| acceptance[4] | pass | D | After re-login from the redirect, the console returned to the same route (`/apps/com.example.showcase/page/showcase_capability_ma… |
| negative[0] | pass | D | Fresh context, wrong password: POST /auth/sign-in/email → 401 INVALID_EMAIL_OR_PASSWORD. The UI showed a red alert "Invalid e… |
| negative[1] | pass (not triggered) | D | The 401 and the redirect were both observed. |
platform-core.nav-surfaces-render (rev 6) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | J | Final full run smoke-full2.log: 49 passed (2.7m), exit 0. All 7 chart surfaces were green: Delivery Operations, Chart Galle… |
| acceptance[1] | pass | J | GET /api/v1/meta/app?id=showcase_app returned 50 leaves: 49 destinations plus 1 separator. The diff against SURFACES (`nav-diff… |
| acceptance[2] | pass | J | /_console/apps/com.example.showcase/object_that_does_not_exist, run twice on fresh loads: the screenshot shows the shell (sideb… |
| acceptance[3] | pass (none encountered) | J | No surface rendered the ErrorBoundary fallback ("Something went wrong") in any run: boundary count 0 on the bad-route probes. The… |
| negative[0] | pass | J | No surface had a blank ‹main›; the suite's mainText.length › 0 assertion held on all 49. |
| negative[1] | pass | J | Both splash assertions (marker plus both English lines) held on 49/49 in the final run. In the starved run the marker check corre… |
| negative[2] | pass | J | Bad route keeps the shell. |
| negative[3] | pass | J | The spec polls boundingBox up to SURFACE_BUDGET_MS (spec lines ~221). There is no fixed sleep followed by a single read. |
platform-core.metadata-registry-serving (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | GET /api/v1/meta (admin bearer) → 200, entries=34. All 19 kinds named in the steps are present (object, view, page, dashboard, ap… |
| acceptance[1] | pass (pin) | G | packages_qa_dogfood.log, file meta-types-create-seed.dogfood.test.ts: "carries the dashboard create seed (widgets: [])", "carri… |
| acceptance[2] | pass | G | GET /api/v1/meta/types → 200 {types, entries(34)}. The body is byte-identical to GET /meta, which the pin "answers the same bod… |
| acceptance[3] | pass | G | GET /meta/view/showcase_task → 200 item keys list, form, listViews, formViews. listViews includes in_progress, board, gantt (14… |
| acceptance[4] | pass | G | GET /meta/view → 36 items covering the task, project, inquiry, business_unit, contact and field_zoo families (one family per file… |
| acceptance[5] | pass | G | GET /meta/object/showcase_account → loyalty_tier select with options bronze/silver/gold/platinum, linkedin_url url, csat_score nu… |
| acceptance[6] | pass | G | PATCH /data/showcase_account/9zPt7L8kd-U61KLc {loyalty_tier:'gold',csat_score:88} → 200. GET → loyalty_tier=gold, csat_score=88… |
| negative[0] | pass | G | No registered type is missing from /meta, and seeds match the pin. |
| negative[1] | pass | G | All 3 overlay fields are in /meta and writable through /data. |
| negative[2] | pass | G | Unauthenticated GET /api/v1/meta → 401 UNAUTHENTICATED, so the registry is not leaked. |
platform-core.metadata-authoring-roundtrip (rev 3) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | POST /packages {manifest:{id:'com.qa.g-author',…type:'app'}} → 201 source=authored, writable=true. PUT /meta/view/showcase_task… |
| acceptance[1] | pass | G | Before publishing, GET …/published → 404. POST /packages/com.qa.g-author/publish-drafts → 200 outcome=published, publishedCount=1… |
| acceptance[3] | pass | G | PUT /meta/view/showcase_task.os_qa_view_g2?mode=draft&package=com.example.showcase → 422 code:"WRITABLE_PACKAGE_REQUIRED" ("tha… |
| acceptance[4] | pass | G | PUT /meta/object/showcase_task (served body, label changed) → 403 NOT_OVERRIDABLE. The label still reads "Task" afterwards. Sid… |
| acceptance[5] | pass | G | PUT /meta/view/showcase_task.os_qa_bad_g1 {…columns:42} in both draft and active mode → 422 INVALID_METADATA "failed spec valid… |
| negative[0] | pass | G | Both deny-side attempts were refused (422 and 403). |
| negative[1] | pass | G | The malformed body was not stored. |
| negative[2] | pass | G | The published view appears in the console switcher after reload. |
| acceptance[2] (Studio record page issue… | pass (hand-driven, vendored console at 42102) | J | At /_console/apps/showcase_app/metadata/page/new I set the Label, set Page type list→record (combobox #mdf-type), picked … |
platform-core.builtin-apps-nav-render (rev 6) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | D | Setup 42/42 and Account 8/8, walked by following every sidebar link's real href. That was 28 links plus 14 more after expandi… |
| acceptance[1] | pass | D | ?id=setup has 9 group anchors (group_overview … group_advanced), all filled. Every SETUP_NAV_CONTRIBUTIONS id is served except … |
| acceptance[2] | pass | D | Mei on /_console/apps/setup, /apps/com.objectstack.setup and /apps/com.objectstack.setup/sys_user/view/all_users: each scre… |
| acceptance[3] | pass | D | Forged requests with Mei's bearer: GET /api/v1/meta/app?id=setup → 200 {"items":[]}. GET /api/v1/meta/app/setup → 403 `… |
| acceptance[4] | pass | D | As Mei, ?id=setup → {"items":[]}, so nav_api_keys and the manage_platform_settings settings URLs are unreachable. Control onl… |
| acceptance[5] | pass | D | Gated-entry table: nav_organizations (requiresService org-scoping) is ABSENT, and /discovery services have no org-scoping. nav_… |
| acceptance[6] | pass | D | nav_sso_providers is absent from the served payload (SSO not wired, per the knownGap). No nav entry renders a dead "failed to loa… |
| negative[0] | pass (not triggered) | D | No blank or placeholder destinations. |
| negative[1] | pass (not triggered) | D | The member was refused on the wire (403 or empty). |
| negative[2] | pass (not triggered) | D | The member's account nav has no permission-gated entries. Setup was not obtainable. |
platform-core.settings-hub-roundtrip (rev 4) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | E | Before: GET /api/settings/localization timezone {value:"UTC",source:"tenant"} (dev seed). PUT {"timezone":"Asia/Tokyo"} → 200… |
| acceptance[1] | pass | E | GET /api/v1/auth/me/localization returned timezone:"UTC" before the PUT and "Asia/Tokyo" after it. After the env-lock restart… |
| acceptance[2] | pass | E | GET /api/v1/data/sys_setting_audit?filter=namespace=localization → row `key:timezone, scope:tenant, action:set, source:api, actor… |
| acceptance[3] | pass | E | Screenshot before: Default country "US" had no badge (source default). The UI edit went out as `PUT /api/settings/localization {"… |
| acceptance[4] | pass | E | Restart with OS_LOCALIZATION_TIMEZONE=Europe/Paris → GET `{value:"Europe/Paris",source:"env",locked:true,lockedReason:"Set via en… |
| acceptance[5] | pass | E | Screenshot: Default timezone shows the "Locked by env" badge with a greyed Europe/Paris input. Playwright reports isEditable=fals… |
| acceptance[6] | pass | E | POST /api/settings/mail/test {"provider":"log","to":"x@example.com"} → 400 SETTINGS_ACTION_FAILED, details.ok:false, message … |
| acceptance[7] | pass | E | PUT mail.smtp_password and ai.openai_api_key. sys_secret has rows sec_282ab7… and sec_f21b28… (aes-256-gcm, v2: ciphertext)… |
| acceptance[8] | pass | E | GET /api/settings/mail and /ai: the set secrets (smtp_password [password], openai_api_key [encrypted:true]) have value = 8×U+2022… |
| acceptance[9] | pass | E | Env-pinned OS_MAIL_SMTP_PASSWORD (password type) and OS_AI_ANTHROPIC_API_KEY (encrypted:true): each GET returns value=mask, sourc… |
| acceptance[10] | pass | E | Echoed mask sent in both shapes ({smtp_password:mask} and {values:{openai_api_key:{value:mask}}}, then the reverse) → 200 `{v… |
| acceptance[11] | pass | E | Anonymous GET /api/settings/localization → 401 {code:"UNAUTHENTICATED"}. Anonymous GET /api/settings → 401 UNAUTHENTICATED. N… |
| acceptance[12] | pass | E | All 11 namespaces (localization, company, branding, auth, mail, sms, storage, ai, knowledge, feature_flags, lifecycle) return 200… |
| negative[0] | pass | E | The effect clause was scored on auth/me/localization (consumer output), not on the form. |
| negative[1] | pass | E | All env-locked PUTs (3 keys) → 409. None returned 200. |
| negative[2] | pass | E | No plaintext was found in GET bodies, sys_setting, sys_platform_setting.value or the data.db bytes. |
| negative[3] | pass | E | The set secret is served as the mask; the key is present, not omitted. |
| negative[4] | pass | E | After the echoed-mask PUT, the decrypt still returns the original plaintext, not the mask. |
| negative[5] | pass, by proxy | E | The in-process path is not masked. I proved this by decrypting via LocalCryptoProvider with the boot's key file and the same co… |
| negative[6] | pass | E | The test action returns ok:false. |
| negative[7] | pass | E | Anonymous list → 401. |
platform-core.package-lifecycle-enable-disable (rev 1) — FAIL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | E | Before disable: GET /api/v1/data/qaprobe_widget 200 rows=3, /meta/object 200, /meta/view 200, /meta/app names include qaprobe_app… |
| acceptance[1] | pass | E | PATCH /enable → 200 status:installed. /data 200 rows=3, /meta/object and /meta/view 200, qaprobe_app is back in /meta/app with … |
| acceptance[2] | pass | E | DELETE /packages/com.qa.probe → 200 registryRemoved:true, deletedCount:3. Afterwards: /data 404 OBJECT_NOT_FOUND, qaprobe a… |
| acceptance[3] | pass | E | GET /packages/:id/commits: count=1 before the second publish. I added a color field and ran publish-drafts. Count is then 2; th… |
| acceptance[4] | pass | E | /meta/object/qaprobe_widget/published fields were name before the change and name,color after it. POST /packages/:id/commits/… |
| acceptance[5] | FAIL | E | Nav absent: pass (home screenshot has no QA Probe tile). Stale deep-link: fail. /_console/apps/showcase_app/qaprobe_widget stay… |
| acceptance[6] | pass | E | PATCH /packages/com.example.showcase/disable → 422 WRITABLE_PACKAGE_REQUIRED. DELETE com.example.showcase → 422. com.objectstac… |
| acceptance[7] | pass | E | Row count was 3 before disable and 3 after re-enable (w-a, w-b, w-c). |
| negative[0] | pass | E | /data returns 404 after disable. |
| negative[1] | pass | E | No orphaned metadata after the uninstall. |
| negative[2] | pass | E | Revert restored the prior shape. |
| negative[3] | pass | E | 422 on both read-only verbs. |
platform-core.notification-center (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | E | Payload (sys_inbox_message + sys_notification_receipt as fetched by the console): groups digest n=5 u=5 and approval.requested n=… |
| acceptance[1] | pass | E | Popover screenshot shows three tabs: "Notifications 2", "Approvals 3", "Activity feed". |
| acceptance[2] | pass | E | Screenshot first. "Scheduled project digest ×5" became aria-expanded=true after the row-button click and showed 5 member rows. "A… |
| acceptance[3] | pass | E | Clicking "Mark read" on the Approval-requested group sent one `POST /api/v1/notifications/read {"ids":["3Z3l4g4deu3Uu_E6","57PPkX… |
| acceptance[4] | pass | E | "Mark all read" sent POST /api/v1/notifications/read/all {}, which returned 200 readCount:6. The badge went 4 → 3 and the hea… |
| acceptance[5] | pass (see note) | E | The Approvals tab shows "3 pending approvals" with a "View approvals" button. sys_approval_request with status=pending returns 3 … |
| acceptance[6] | pass | E | Fresh context and reload. GET /api/v1/notifications?view=mine shows all 9 marked rows read:true. Only the newly delivered diges… |
| negative[0] | pass | E | The badge counted topics (5), not rows (8). |
| negative[1] | pass | E | Mark-read on a 3-member group sent one POST. |
| negative[2] | pass | E | Read state persisted across reload. |
| negative[3] | n/a | E | Cross-reference note only. |
platform-core.docs-audience-gate (rev 1) — PARTIAL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | A | Anonymous against qadocs_org_book, qadocs_gated_book, implicit com.qa.docsgate, com.objectstack.setup and com.example.showcase: e… |
| acceptance[1] | pass | A | Results by audience: public: showcase_manual and qadocs_public_book gave 200 for anonymous, phone, auditor and admin. org… |
| acceptance[2] | pass (half), unknown-shape half not-run | A | I ran npx vitest run src/system/book.test.ts -t audienceAllows in packages/spec: 4 passed, including "permission-set gate FAILS… |
| acceptance[3] | pass | A | Phone's tree of qadocs_public_book is [public, i18n, org, orphan]. Auditor's is the same plus gated_doc. Anonymous gets only [pub… |
| acceptance[4] | pass | A | qadocs_orphan_doc is in no book, but it does show in the Uncategorized group of qadocs_public_book for authenticated callers. The… |
| acceptance[5] | pass | A | GET /meta/book/com.qa.docsgate/tree: anonymous 401 UNAUTHENTICATED. Phone 200 with group all = [i18n, org, orphan, public] (gat… |
| acceptance[6] | pass | A | With Accept-Language: zh-CN, qadocs_org_book's entry is {"doc":"qadocs_i18n_doc","label":"QA 国际化文档"}; without the header it i… |
| negative[0] | pass (not triggered) | A | No non-public book served a tree to anonymous. |
| negative[1] | pass (not triggered) | A | The public book's anonymous tree contains only the docs it claims. |
| negative[2] | pass (not triggered) | A | Refusal bodies are generic strings plus a code, with no group or doc names. |
| negative[3] | pass (not triggered) | A | No 404 where a 401 or 403 was due. |
| negative[4] | pass (not triggered) | A | ?package=com.qa.docsgate&package=com.example.showcase returned 400 VALIDATION_ERROR "The "package" query parameter was suppli… |
platform-core.interrupted-migration-boot-report (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | boot2.log: `WARN Interrupted migration run 'qa-interrupted-1' (plan 'qa_plan', started …): 0 chunk(s) committed 1 chunk(s) with U… |
| acceptance[1] | pass | G | After the warning, health=200 and ready=200. The journal rows (id, run_id, seq, kind, plan_hash, chunk_index, detail, created_at)… |
| acceptance[2] | pass | G | Recovery-line counts: boot1 (empty journal) 0, boot2 2, boot3 (after the seq 2 run_done insert) 0. All three ran on the same co… |
| acceptance[3] | pass (pin) | G | packages_runtime.log: migration-recovery-plugin.test.ts "MigrationRecoveryPlugin — quiet degradation › skips silently when sys_mi… |
| acceptance[4] | pass (pin) | G | Same file: "boot scan › reports a scan FAILURE rather than reading it as "nothing found"" ✓ |
| negative[0] | pass | G | Booting wrote nothing to the journal. |
| negative[1] | pass | G | Pinned by both degradation tests. |
| negative[2] | pass | G | Silence was scored only after boot2 proved the scan active. |
platform-core.manifest-install-contract (rev 3) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | B | POST /api/v1/packages probe-old engines.protocol:'^17' → 422 OS_PROTOCOL_INCOMPATIBLE. Then GET /packages/com.example.qa-manife… |
| acceptance[1] | pass | B | `details:{requiredRange:"^17", rangeSource:"engines.protocol", protocolVersion:"18.0.0", targetMajor:17, migrateCommand:"objectst… |
| acceptance[2] | pass | B | prec-ok {protocol:^18, platform:^17} → 201. prec-no {protocol:^17, platform:^18} → 422 rangeSource engines.protocol. legacy… |
| acceptance[3] | pass | B | norange → 201 and reads back. wsrange workspace:* → 201 and reads back. Log line: `[protocol] package 'com.example.qa-manifest-… |
| acceptance[4] | pass | B | (a) probe-b ns qa_mfp → 422 NAMESPACE_CONFLICT: "Namespace conflict: namespace "qa_mfp" is already owned by package "com.e… |
| acceptance[5] | pass | B | GET /packages/com.example.leave → manifest.namespace:"leave". GET /packages/com.example.holiday → "timeoff". sqlite `sys_pack… |
| negative[0] | pass (not triggered) | B | no incompatible package returned 2xx, and none has a row. |
| negative[1] | pass (not triggered) | B | no-range and workspace:* were both admitted. |
| negative[2] | pass (not triggered) | B | the namespace conflict maps to 422, not 5xx. |
| negative[3] | pass (not triggered) | B | the conflict message names the namespace, the owner and the incoming id. |
| negative[4] | pass (not triggered) | B | the derived namespace is present in the echo, the GET and sys_packages alike. |
platform-core.activation-ledger-registration-home (rev 5) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass (pin) | G | dogfood packaged-activation-ledger-reach, describe "#12359 — actions and NO automation service": all 5 tests ✓ ("the automation s… |
| acceptance[1] | pass | G | Live stock boot :41703. POST /actions/_activation/showcase_task/showcase_mark_done {enabled:false} → 200. POST /automation/showca… |
| acceptance[2] | pass (pin) | G | Three tests: dogfood "the ledger object has exactly ONE owner, and it is the package that declares it" ✓. service-automation acti… |
| acceptance[3] | pass (pin) | G | platform-objects plugin.test (manifest triple) ✓ and dogfood "the table keeps the datasource binding it had before the registrar … |
| acceptance[4] | pass | G | platform-object-names.ts: sys_metadata_activation occurs once (line 62), inside the 'platform-objects': [ array that opens at… |
| negative[0..3] | pass | G | No 503. The anti-vacuity control is green. There was no boot-time ownership throw. The binding is unchanged per the pins. |
platform-core.activation-ledger-row-contract (rev 4) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | GET /data/sys_metadata_activation → 200 with 4 rows. POST, PATCH and DELETE each → 405 OBJECT_API_METHOD_NOT_ALLOWED "API opera… |
| acceptance[1] | pass | G | Row dump: one row per (metadata_type, name). With the server stopped, a duplicate INSERT ('action', 'showcase_mark_done') → `Inte… |
| acceptance[2] | pass | G | PRAGMA cols: id, created_at, updated_at, created_by, updated_by, metadata_type, name, package_id, active, __search. No organizati… |
| acceptance[3] | pass | G | After re-enabling all 4 switches: the same 4 ids (sCkgvTk7…, t41TqmJ9…, lgnPV3Up…, 5Xiyz1PF…), count 4 → 4, all active=True. |
| acceptance[4] | pass | G | The distinct metadata_type set is {action, flow, permission, position}. POST /security/_activation/capability/showcase.export_dat… |
| acceptance[5] | pass | G | The object source has "no lifecycle block on purpose" (line 48). The restart against the same DB gave a sqlite pre==post row tu… |
| negative[0..5] | pass | G | No 2xx on generic writes. No tenant column. Re-enable updated rows and deleted none. The duplicate was refused. The capability an… |
platform-core.marketplace-install-local-lifecycle (rev 4) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | B | Boot A (no OS_CLOUD_URL, :41201): GET /marketplace/install-local → 200 {items:[],total:0,storageDir}. GET /marketplace/pack… |
| acceptance[1] | pass | B | Boot A runtime/config features:{installLocal:true, marketplace:true}; Boot B {installLocal:true, marketplace:false}. All four… |
| acceptance[2] | pass | B | os package install /home/user/objectstack/examples/app-crm/dist/objectstack.json -r http://localhost:41202 --email/--password p… |
| acceptance[3] | pass | B | Admin: the entry carries installedBy:"KSD4…" and the envelope carries storageDir. Member (identity proven by get-session as q… |
| acceptance[4] | pass | B | Before purge: account 4 (3 seed plus the user row "QA User-Authored Account B", id 6h-gOWCxjoPOD6YB), contact 3, opp 12, lead 5, … |
| acceptance[5] | pass | B | Member, own session: POST install, DELETE, reseed and purge each → 403 FORBIDDEN naming the manage_metadata capability (e.g. "P… |
| acceptance[6] | pass | B | DELETE as admin → 200 `{manifestId, cleanups:[{name:"security.package-permissions",success:true,removed:0}], note:"Cached manifes… |
| negative[0] | pass (not triggered) | B | install-local answered 200 under off. |
| negative[1] | pass (not triggered) | B | the flags matched the routes on both boots. |
| negative[2] | pass (not triggered) | B | the user-authored row survived the purge. |
| negative[3] | pass (not triggered) | B | no 2xx for the member or anonymous on any of the 8 cells. |
| negative[4] | pass (not triggered) | B | purged rows did not return after the restart. |
| negative[5] | n/a | B | the browse upstream "fetch failed" (502) was not filed. It is an egress limit, blocked(environment) for upstream reachability onl… |
platform-core.runtime-config-boot-read (rev 1) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | A | Default boot (plugins include com.objectstack.cloud.connection, marketplace-proxy and `com.objectstack.runtime.runtime-config… |
| acceptance[1] | pass | A | I fetched GET /api/v1/studio/runtime-config anonymously at the same moment (parallel curl). It returned 200, and cmp shows the … |
| acceptance[2] | pass | A | Cloud-arm boot: anonymous 200, as above. Offline boot (OS_CLOUD_URL=off, same port and DB; the plugin list has no cloud.connect… |
| acceptance[3] | pass | A | On both boots, a case-insensitive grep of the body for token, secret, password, authorization, bearer, cookie, session, key, dsn … |
| negative[0] | pass (not triggered) | A | No 401 or 403 on any read. |
| negative[1] | pass (not triggered) | A | The alias has not drifted. |
| negative[2] | pass (not triggered) | A | No credential material in the body. |
| negative[3] | n/a | A | Flag agreement belongs to the marketplace item. I only saw that marketplace goes from true to false offline. |
platform-core.docs-portal-render (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | E | Admin API: /meta/book has 1 book (showcase_manual) and /meta/doc has 9 docs (8 showcase plus setup_overview from com.objectstack.… |
| acceptance[1] | pass | E | /docs/manual/showcase_index body rendered (screenshot). Its cross-link "documentation guide" (href /_console/docs/showcase_docs_g… |
| acceptance[2] | pass | E | goto /_console/docs/showcase_tour_data (200) ended at /_console/docs/manual/showcase_tour_data, which renders "Guided tour — Data… |
| acceptance[3] | pass | E | /_console/apps/com.example.showcase/docs lists only the 8 showcase docs under app-scoped hrefs, inside the Showcase shell. /_cons… |
| acceptance[4] | pass | E | The member was minted by invite-member then sign-up/email; get-session confirms qa-e-member@… with positions org_member/everyone.… |
| acceptance[5] | pass | E | Screenshots of the index, book landing (auto-opens the first doc), reader, app-scoped index (showcase and setup) and member pages… |
| negative[0] | pass | E | The member's tree is not wider than the member's API set. |
| negative[1] | pass | E | No entry returned 401/403 (the only 403 in the session was GET /meta/_drafts during the member's initial home load, not a docs … |
| negative[2] | pass | E | The flat URL redirected and did not 404. |
| negative[3] | pass | E | Screenshot-first reads were done after a 3.5 s settle. |
platform-core.url-overlay-contract (rev 1) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | J | Drawer: on the showcase_team list (hist 4), clicking "Platform" gave ?recordId=SyE2oWQkUm2t_w-v, hist 5, dialog=1 (screen… |
| acceptance[1] | pass | J | Tabs: list, then record (push), then 5 tab clicks (Invoices, Projects, History, Related, Invoices). The URL went to `?tab=rel… |
| acceptance[2] | pass | J | Fresh context, deep link …/showcase_account/record/p9fIbnZ0kZcphz__?tab=related:showcase_project opened with aria-selected tab … |
| acceptance[3] | pass | J | Projects tab, then the related list's "New", gave `?tab=related:showcase_project&form=new&formObject=showcase_project&formLink=ac… |
| acceptance[4] | pass | J | Account Fabrikam, Projects tab, then the "Compliance Audit" row gave `showcase_project/record/4JTlSri7ZjPQ6vnd?from=[{"o":"showca… |
| acceptance[5] | pass | J | /_console/forms/showcase_task.form?recordId=‹project id›&recordObject=showcase_project, run twice (R1, R5), rendered the error … |
| negative[0] | pass | J | Back never stepped through tabs or palette/shortcuts (acceptance[1] above). |
| negative[1] | pass | J | No /data read or write for the mismatched pair (acceptance[5]). |
| negative[2] | pass | J | Nothing violated on the driven surfaces, and a static scan of examples/app-showcase/src found no page or view repurposing `reco… |
| negative[3] | pass | J | Cold reload and fresh-context opens kept the overlay and the pre-link. |
platform-core.home-admin-cluster-links (rev 3) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | D | All 9 URLs land on real pages (screenshots reviewed): Settings hub, Applications (Showcase/Setup/Account), App Marketplace (named… |
| acceptance[1] | pass | D | /apps/setup/system trace: [/apps/setup/system, replaceState:/apps/setup/system, replaceState:/apps/setup/system/settings]. Th… |
| acceptance[2] | pass | D | Legacy URLs, each with one replaceState hop: users → /apps/setup/sys_user, organizations → /apps/setup/sys_organization, role… |
| acceptance[3] | pass | D | Plain member minted by invite then sign-up (qa-d-member@example.com). get-session showed positions [org_member, everyone]. `/… |
| negative[0] | pass (not triggered) | D | No RouteNotFound or system record view. |
| negative[1] | pass (not triggered) | D | All 5 legacy URLs redirect. |
| negative[2] | pass (not triggered) | D | No Administration entry for the member. Server half: the member gets ?id=setup → [] (see the builtin item). |
platform-core.lifecycle-retention-sweep (rev 4) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | Seeded rows: 5 sys_automation_run rows status=completed, created_at −40d, in data.telemetry.db. The control boot (OS_LOG_LEVEL=in… |
| acceptance[1] | pass | G | The survivors are exactly qa_run_paused and qa_run_running, which is the seeded non-terminal set. |
| acceptance[2] | pass (live retention + pin for the report half) | G | Live: qa_audit_old (−100d) survived the sweep. The "reported skipped archive-pending" half is not visible in the aggregate log li… |
| acceptance[3] | pass | G | Pin: "unguarded reap batching (#5194) › bounds the FIRST sweep over a large backlog, and drains the rest on later sweeps" ✓ plus … |
| acceptance[4] | pass | G | Re-seeded qa_run_killswitch (completed, −40d). The boot ran with OS_LIFECYCLE_DISABLED=1 and OS_LOG_LEVEL=info, and other INFO li… |
| acceptance[5] | pass | G | Spec object.zod.ts LifecycleClassSchema is record, audit, telemetry, transient, event (5). The engine has SYSTEM_LEDGER_LIFECYCLE… |
| idle heartbeat (steps[4]) | pass | G | Fresh info-level boot on the cleaned DB: `[lifecycle] sweep: 18 policy(ies) applied, ~0 rows reaped, 0 datasource(s) reclaimed, 0… |
| negative[0..4] | pass | G | The paused/running rows were not reaped. The audit row was not hot-deleted. Every absence was read after ≥60 s on an armed compos… |
platform-core.seed-mode-matrix (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | A | Publish 1 {A:a1, B:b1} gave seedApplied inserted 2, updated 0. I then PATCHed A.note to "runtime-edit" (A.updated_at 12:40:25.3… |
| acceptance[1] | pass | A | The update-mode publish {A:a2, D:d1} gave inserted 0, updated 1. Total stayed 3, before and after. A became a2. Filtering `{"na… |
| acceptance[2] | pass | A | The ignore-mode publish {A:a3, E:e1} gave inserted 1, updated 0. A still reads a2 with updated_at 12:40:33.255Z, both unchang… |
| acceptance[3] | pass | A | The insert-mode publish {A:a-ins} gave inserted 1. A second A-named row appeared (id 19_N…), so there are two "A" rows and tota… |
| acceptance[4] | pass (expected posture recorded) | A | I hand-inserted F with POST /api/v1/data, then published replace {G}, which gave inserted 1. F (30W7…) survives and G is insert… |
| acceptance[5] | pass | A | In spec/src/data/seed.zod.ts, SeedMode = z.enum(['insert','update','upsert','replace','ignore']) has 5 members (the enumSourc… |
| negative[0] | pass (not triggered) | A | I re-published the upsert draft unchanged twice. Replay 1 gave inserted 0, updated 1: it re-converged the newest A row (19_N…, … |
| negative[1] | pass (not triggered) | A | Update did not create D. Ignore did not rewrite A. |
| negative[2] | pass (honoured) | A | The replace clause is recorded as posture, not as a pass of scoped deletion. |
| negative[3] | pass (honoured) | A | I used only the scratch object, in its own isolated DB. |
platform-core.settings-audit-secret-fingerprint (rev 2) — PASS
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass | G | PUT /api/settings/mail {provider:'resend', api_key:V1, from_email} → 200. sys_setting_audit api_key action=set, encrypted=True, `… |
| acceptance[1] | pass | G | Offline search over both full ledger dumps: V1 plaintext no-hit, fnv32(JSON V1)=fnv32:95ea83c2 no-hit, sha256(V1) no-hit, 'sha256… |
| acceptance[2] | pass | G | Writes V1, V1, V2, V1 on both ledgers: b9fb35cf…, b9fb35cf…, f6c98912…, b9fb35cf…. Equal values give equal fingerprints and V2 di… |
| acceptance[3] | pass | G | PUT {provider:'log', api_key:null}: sys_setting_audit api_key action=reset, new_hash=None. sys_audit_log config_change metadata e… |
| acceptance[4] | pass | G | from_email rows carry fnv32:6d1d9810 and fnv32:c8753918 on both ledgers, which equals the offline FNV-1a of the quoted JSON. |
| acceptance[5] | pass (pin) | G | service-settings settings-audit-secret-digest.test: "legacy inline-adapter path with no provider records no fingerprint and repor… |
| negative[0..4] | pass | G | No unkeyed digest and no plaintext. Fallback and veto are pinned. The positive control is present. |
platform-core.v18-boot-refusals (rev 1) — FAIL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass (pin) | G | runtime organization-ownership-boot-guard.test.ts (17 tests ✓, incl. "⭐ a v17-shape database is refused, naming the command and e… |
| acceptance[1] | pass | G | os serve (production; OS_DATABASE_URL=file:‹copy›) against two planted v17-shape copies exits rc=1 after 7 s / 11 s. There … |
| acceptance[2] | FAIL | G | Checksums before and after the refused boot differ. Reproduced 3 times on fresh copies: pinshape adf7d2b6… → dafa44ed…, real 00ec… |
| acceptance[3] | blocked(fixture) | G | The step's "scratch config with services:{auth:false}" names no real stack key. I tried tiers:['core'] + `plugins:[new Security… |
| negative[0] | pass with caveat | G | The v18 boot did not serve the v17 DB: it exited before listening. Whether the additive DDL counts as "half-migrates" is exactly … |
platform-core.protocol-18-handshake-and-stored-replay (rev 1) — PARTIAL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass (pin) | G | runtime packages-install-protocol-incompatible.test (6 ✓). cli migrate-meta-protocol-range.test (19 ✓). spec conversions/stored.t… |
| acceptance[1] | pass | G | POST /api/v1/packages with engines.protocol "^17" → 422 OS_PROTOCOL_INCOMPATIBLE, details.migrateCommand "objectstack migrate m… |
| acceptance[2] | pass | G | Planted: PUT a canonical page qa_planted_page, stop, then sqlite UPDATE sys_metadata.metadata to template:'full-width' (retired… |
| acceptance[3] | blocked(fixture) | G | No ^18 artifact built before a retirement exists in this checkout, and making one needs an older prerelease build. Pinned only: "… |
platform-core.settings-organization-isolation-and-platform-rung (rev 1) — PARTIAL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass (pin) | G | settings-organization-isolation.pin (18 ✓), settings-read-door.pin (27 ✓), settings-admission-tenancy-posture (18 ✓), config-chan… |
| acceptance[1] | pass | G | As platform admin, PUT /api/settings/mail {from_email} (mail is scope 'global') → 200. GET /data/sys_platform_setting → 200 with … |
| acceptance[2] | blocked(environment) | G | A boot with OS_TENANCY_POSTURE=isolated is refused by 18.0 by design: `✖ FATAL: tenancy posture 'isolated' is not supported in 18… |
| acceptance[3] | blocked(environment) | G | Same cause: only the walled posture refuses, and the pin says "is not refused under the 'single' posture". Pinned: "is refused wh… |
platform-core.serve-composition-v18 (rev 1) — PARTIAL
| clause | verdict | by | evidence |
|---|---|---|---|
| acceptance[0] | pass (pin) | G | cli serve-config-boot-residual (14 ✓), serve-datasource-mapping-before-sync (2 ✓), serve-no-storage-boot-guard (5 ✓), console.not… |
| acceptance[1] | pass | G | Scratch packages[] config (2 package bodies + a top-level qag_note object/view), production serve → exactly 1 warn line: `[AppPlu… |
| acceptance[2] | pass | G | Scratch app with page qagorg_organization_detail (record, sys_organization, isDefault) → boot ready, 0 DEFAULT_RECORD_PAGE_CONFLI… |
| acceptance[3] | pass | G | Scratch host config: datasources primary (sqlite ./primary.db) + datasourceMapping [{default:true, datasource:'primary'}] + OS_DA… |
| acceptance[4] | pass | G | Host config (inline plugin, no OS_DATABASE_URL, no --dev, NODE_ENV unset, bin/run.js) → rc=1: `✖ This production serve has no sto… |
| acceptance[5] | blocked(environment) | G | Staging an unbuilt console means removing packages/console/dist from the shared tree, which this runner may not touch. A dist-les… |
Handoff — items not reached (NOT-RUN)
Tier 3 deferred by the 18.0 release-sweep plan (plan B: P2 items without an automated.ref are not driven this wave):
platform-core.shell-nav-personalization(P2, browser, rev 4) — not-runplatform-core.app-management-toggle(P2, mixed, rev 3) — not-runplatform-core.packaged-object-extend-only(P2, api, rev 2) — not-runplatform-core.marketplace-console-honesty(P2, browser, rev 5) — not-runplatform-core.keyboard-shortcut-surface(P2, browser, rev 4) — not-runplatform-core.theme-mode-persistence(P2, browser, rev 2) — not-runplatform-core.console-installability-indicators(P2, browser, rev 1) — not-run
Reproduction rules for each FAIL
platform-core.package-lifecycle-enable-disable · acceptance[5] — product defect (objectui console)
Clause: after disable, the package's nav is gone and a stale deep link lands on a named not-found inside the shell, never a dead page. Nav-absent half passes; the deep-link half fails. Reproduced 2/2 by the first runner and 2/2 by an independent RUNNER rule 7 verifier on a fresh boot (traps ruled out: 60 s settle with silent network after 1.1 s; console stamp = pin; direct navigation, no clicks on the failing surface).
- Boot showcase (
objectstack dev --ui --seed-admin);POST /api/v1/auth/sign-in/email {"email":"admin@objectos.ai","password":"admin123"}→ bearer. POST /api/v1/packages {"manifest":{"id":"com.qa.probe","name":"QA Probe","version":"0.1.0","type":"app","namespace":"qaprobe"}}→ 201.PUT /api/v1/meta/object/qaprobe_widget?mode=draft&package=com.qa.probe {"name":"qaprobe_widget","label":"QA Widget","sharingModel":"public_read_write","fields":{"name":{"type":"text"}}}→ 200;POST /api/v1/packages/com.qa.probe/publish-drafts {}→ 200;GET /api/v1/meta/object/qaprobe_widget→ 200.PATCH /api/v1/packages/com.qa.probe/disable {}→ 200status:disabled.GET /api/v1/meta/object/qaprobe_widget→ 404{"error":"Object 'qaprobe_widget' belongs to a disabled package…","code":"OBJECT_PACKAGE_DISABLED"}.- Sign in through
/_console/login, open/_console/apps/showcase_app/qaprobe_widget.- expected: Showcase shell with a named not-found / package-disabled state.
- actual: full-page "ObjectOS · Opening your console…" splash, still present at 60 s; network shows two 404
OBJECT_PACKAGE_DISABLED, then nothing. - control:
/_console/apps/showcase_app/no_such_object_xyz→ 404RESOURCE_NOT_FOUND→ named "Object Not Found" inside the shell.
Discriminator (verifier, Playwright response rewrite, status kept 404): wrapped body with the SAME code still hangs; flat or wrapped body with code RESOURCE_NOT_FOUND renders the named not-found. The code value decides, not the envelope. Mechanism (objectui at the pin and on objectui main 61ed5ad alike): isMetaItemAbsentError (data-objectstack) recognises only RESOURCE_NOT_FOUND, so the by-name scope read is classed failed; fetchScopeName drops it without re-render; the first-paint gate in AppContent.tsx never settles. Secondary framework observation (not the cause): the RestServer /meta/object/:name refusal is the flat {error, code} shape without the object key /data carries, while the runtime dispatcher answers the wrapped envelope — both are live ADR-0112 shapes; the #7557 changelog example shows only the wrapped one.
platform-core.v18-boot-refusals · acceptance[2] — assertion defect (not a product break)
Clause: "the refused boot did not alter the database", oracle build, checksum before == after.
- Build a real v17 database: npm
@objectstack/cli@17.7.0,os init,os dev --seed-admin -d file:v17.db, create 3 records, stop (sha256de915b8f…, 69 tables, 66 rows). - Copy it; from examples/app-showcase run
OS_DATABASE_URL=file:COPY OS_SECRET_KEY=64-hex objectstack serve -p PORT. - Result 3/3: rc=1 before listening, refusal names the 7 tables still carrying
organization_idandos migrate organization-ownership(acceptance[1] pass). sha256 afterba74b704…≠ before — expected by the clause: equal. - Row-level diff (pristine vs after, each table on its original columns): 0 tables differ, 66/66 rows identical, no new table holds a row, no column dropped. The change is the engine's additive schema sync (41 empty tables,
__searchcolumns, indexes). A 17.7.0 boot of the refused copy still serves the 3 original rows.
§5 disposition: assertion defect. The declared contract is row-level: runtime CHANGELOG ("judges … after the engine's schema sync (the column the rewrite step waits for is still added) … A refused boot leaves every row as it found it"), the guard's header, and the kernel pin that asserts the additive tables exist and hold no rows. negative[0] (served or half-migrated v17 DB) holds. Owed: re-point acceptance[2] to a row-level oracle (routed to the wave anchor, see below). acceptance[3] is blocked(fixture): the step's services:{auth:false} is not a real stack key.
RUNNER rule 7 (P0 fails / release-blocking findings)
platform-core.v18-boot-refusalsacceptance[2] (P0): independent agent re-derived from the captured evidence alone — FAIL against the clause as written, PASS against the declared contract — then re-ran on a realistic 17.7.0 database (3/3). Agreement: assertion defect, not release-blocking.platform-core.package-lifecycle-enable-disableacceptance[5] (P1, user-visible): independent agent re-derived FAIL from the evidence alone and reproduced 2/2; it corrected the first runner's causal story (code value, not envelope shape). Product defect in objectui; not a data or security issue.
Partial items — what was not proven
docs-audience-gateacceptance[2]: the unknown-future-audience-shape fail-closed branch is unreachable live (a save with an unknown shape is refused 422) and has no test; only the source line evidences it.protocol-18-handshake-and-stored-replayacceptance[3]: blocked(fixture) — no ^18 artifact built before a retirement exists in this checkout.settings-organization-isolation-and-platform-rungacceptance[2], [3]: blocked(environment) — 18.0 refusesOS_TENANCY_POSTURE=isolatedat boot (NOT_IMPLEMENTED, single only); pins pass.serve-composition-v18acceptance[5]: blocked(environment) — needs an unbuilt console, i.e. removing the shared tree's console dist.
Checklist-accuracy findings / fixture gaps (for the wave anchor card — not extracted)
v18-boot-refusals: re-point acceptance[2] to a row-level oracle (above); step 2services:{auth:false}is not a stack key; the literalORGANIZATION_OWNERSHIP_MIGRATION_REQUIREDis only onerror.code, never printed.package-lifecycle-enable-disable: the step should say an object needssharingModeland a viewviewKindbefore publish accepts them.builtin-apps-nav-rendersteps[5]: 45 Setup destinations → this boot serves 42 (matches source minus gates); Configuration / Advanced (Setup) and Developer (Account) groups render collapsed — a sidebar walk must expand them.home-admin-cluster-linksacceptance[3]: the Help menu also lists Keyboard shortcuts and About, not "documentation only".settings-hub-roundtrip: invalid-timezone code isvalue_domain, notinvalid_option; no stock namespace is user-scoped, so the multi-scope secret walk cannot run as written.notification-center: console pollssys_inbox_message/sys_notification_receiptthrough the data API, notGET /notifications?view=mine; the Approvals tab is a count + "View approvals" link, not a list (scored pass on the count match — reword).docs-audience-gate: knownGaps[0] half stale (showcase ships a public book); acceptance[3] "lesser persona's tree ⊂ admin's" is wrong for permission-set books (a non-holder admin sees less than a holder) — reword to holder vs non-holder. Missing test: unknown audience shape denies.runtime-config-boot-readsteps[1]: logoUrl / faviconUrl / brandColor appear only when configured.manifest-install-contract: knownGaps[3] and negative[2] sayNamespaceConflictErrorhas no code — stale; it carriesNAMESPACE_CONFLICT/ 422.marketplace-install-local-lifecycle: step 3 typoinstall./examples; "fresh sign-up" must be admin create-user (invite-only posture); on showcase the cloud arm comes from host config, not the CLI.activation-ledger-row-contract: the catalog door now also acceptssharing_rule— a fifth ledger writer; the four-value variants are stale.serve-composition-v18:examples/app-crmno longer declares adatasourceMapping.settings-organization-isolation-and-platform-rung/ walled legs generally: the isolated-posture fixture cannot boot on 18.0 — knownGap should say live legs wait for the walled release.url-overlay-contract:useUrlOverlay.test.tsxasserts no history behaviour (replace semantics evidenced only by hand); step 5 "breadcrumb" is now an inline back link.metadata-authoring-roundtrip: objectuistudio-record-page.spec.tsred (stale, as noted);studio-object-designer.spec.tsF1 (targets the not-loaded studio app) and F4 (new select-options rule) are red too, contrary to the item's note; acceptance[2] hand-driven instead.- RUNNER env fact: showcase resolves @playwright/test 1.63.0, not 1.62.1.
- fixture: marketplace browse needs egress to the cloud catalog (502
MARKETPLACE_PROXY_FAILEDhere) — knownGap, not scored.
Observations (not clause verdicts)
- Seed replay rewrites 5 unchanged showcase rows on every restart (
updated_atbumped; counts and ids unchanged) — extracted as a product defect at close-out. - A list view bound to an object outside the writing package's dependency closure is accepted with unknown
columnsfields on active PUT and draft→publish (observed while driving metadata authoring; no clause asserts it) — extracted at close-out. - console-login: after a server-side sign-out, in-app navigation without reload does not auto-redirect (data pages show "Sign in required"); not scored by the item.
- builtin-apps: a member's user record shows both "Unban User" and "Ban User".
- seed-mode-matrix: with two rows sharing the match key, upsert matched the most recently created one;
replacemode inserts without clearing (posture recorded, not scored as scoped deletion). - docs-portal: in-body cross-links inside the app-scoped docs viewer leave the app for global
/docs.
Timing
Wave-level (one shared build for all three area runs): environment preparation 20 min wall (12:26 → 12:46 UTC: pnpm install 25 s, pnpm build 7m45s, console build 19 min incl. one re-run after a concurrent-build race in @objectstack/lint DTS), of which the API/unit lane was ready after 9 min; testing ~70 min wall (12:35 → 13:45) across parallel runners. Environment : testing ≈ 1 : 3.5 wall. Inside the runners, per-item boot/wait time summed ~55 min of agent time vs ~300 min of driving. This area: Tier 1 batch ~14 min (shared), Tier 2 drivers 4 parallel groups.
Derived item verdicts
- PASS (21):
platform-core.boot-health,platform-core.seed-integrity,platform-core.console-login,platform-core.nav-surfaces-render,platform-core.builtin-apps-nav-render,platform-core.metadata-registry-serving,platform-core.metadata-authoring-roundtrip,platform-core.interrupted-migration-boot-report,platform-core.activation-ledger-registration-home,platform-core.activation-ledger-row-contract,platform-core.url-overlay-contract,platform-core.lifecycle-retention-sweep,platform-core.settings-audit-secret-fingerprint,platform-core.settings-hub-roundtrip,platform-core.notification-center,platform-core.manifest-install-contract,platform-core.marketplace-install-local-lifecycle,platform-core.runtime-config-boot-read,platform-core.docs-portal-render,platform-core.home-admin-cluster-links,platform-core.seed-mode-matrix - PARTIAL (4):
platform-core.docs-audience-gate,platform-core.protocol-18-handshake-and-stored-replay,platform-core.settings-organization-isolation-and-platform-rung,platform-core.serve-composition-v18 - FAIL (2):
platform-core.v18-boot-refusals,platform-core.package-lifecycle-enable-disable - NOT-RUN (7): Tier 3 deferred, listed above
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Close-out (checklist-test SKILL §5).
Extracted: objectstack-ai/objectui#13052 · platform-core.package-lifecycle-enable-disable · acceptance[5]
Extracted: #24562 · platform-core.seed-integrity · acceptance[3]
Extracted: #24563 · platform-core.metadata-authoring-roundtrip · observation outside the clauses
FAIL dispositions:
Partial gaps and checklist-accuracy findings / fixture gaps: listed in the record body, routed to the wave anchor card by the dispatch session.
NOT-RUN: 7 Tier 3 items deferred by the 18.0 release-sweep plan (handoff list in the body).
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Parked-on: #24579 · this record's checklist-accuracy findings, fixture gaps, partial coverage gaps and not-run handover (wave anchor §2–§4)
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Superseded-by: #24603
Round 2 at ba4afcc re-judged this record's A-class unjudged clauses (
platform-core.serve-composition-v18acceptance[5] — pass) and carried every other verdict forward to #24603 (Carried-forward:lines there, valid for 5b14b7f). B-class gaps stay parked on #24579.Generated by Claude Code
docs(qa): correct twelve platform-core checklist items the 18.0 sweep…