Repository navigation
Closed as not planned
Closed as not planned
Copy link
Labels
Description
Run record for the 18.0 release QA wave (dispatch session session_01CfRDy7SuTfUqaTu2AEH2Hi; maintainer-approved plan B: Tier 1 + Tier 2, Tier 3 deferred). Runner session session_016CErRJhdMYZdRg4pe8iQfD. Protocol: .claude/skills/checklist-test/SKILL.md + docs/qa/platform-checklist/RUNNER.md.
Environment fingerprint
- framework:
5b14b7f3ad8e2e0786d228772f473d07b36b2477(pinned;pnpm install --frozen-lockfile+pnpm build, 76/76 tasks green) .objectui-sha:4016370af3c9c9dafec2bc5299afd8e8079701b8; console built withpnpm objectui:build(shallow clone into.cache/objectui-4016370af3c9),packages/console/dist/.objectui-shamatches,pnpm check:console-shagreen- resolver:
node scripts/checklist-select.mjs area:api-backend --json→ 27 runnable · 0 blocked hidden · 0 planned - app: showcase · boot
OS_PORT=P objectstack dev --ui --seed-admin -p P -d file:…/ITEM.db· per-runner port range and per-item file DB · seeded admin + demo personas; staged configs only in scratch copies of the app - browser: Playwright on
/opt/pw-browsers/chromium, headless, one context per persona - timestamp: 2026-10-19 (UTC) · execution: parallel fan-out, one sub-agent runner per item group:
- A1: ports [41300,41301,41302] · env 3 min / test 12 min
- A2: ports [41310] · env 3 min / test 9 min
- A3: ports [41360,41361] · env 3 min / test 13 min
- A4: ports [41370,41371,41372] · env 9 min / test 15 min
Scope
Selector area:api-backend · 27 runnable item(s) (revision = the one each verdict is valid for):
api-backend.batch-transactional-discoveryr5 · P1 · api · Tier 2 → pass (runner A1)api-backend.formula-gatesr2 · P1 · build · Tier 2 → pass (runner A2)api-backend.enforce-or-remove-authoring-gatesr4 · P1 · build · Tier 2 → pass (runner A2)api-backend.server-timing-admin-gatedr4 · P2 · api · Tier 3 → not-runapi-backend.query-contract-matrixr3 · P0 · api · Tier 1 → pass (runner A3)api-backend.error-envelope-ledgerr4 · P1 · api · Tier 2 → pass (runner A1)api-backend.bulk-write-contractr2 · P1 · api · Tier 1 → pass (runner A3)api-backend.route-ledger-live-parityr4 · P2 · api · Tier 3 → not-runapi-backend.declarative-endpoint-executionr3 · P1 · api · Tier 1 → pass (runner A3)api-backend.package-rest-lifecycler3 · P2 · api · Tier 3 → not-runapi-backend.api-console-discovery-executer2 · P2 · browser · Tier 3 → not-runapi-backend.filter-comparand-conformancer3 · P1 · api · Tier 2 → pass (runner A1)api-backend.date-range-preset-matrixr2 · P2 · api · Tier 3 → not-runapi-backend.retired-def-refusalr2 · P1 · build · Tier 2 → pass (runner A2)api-backend.packaged-action-disabled-dispatchr3 · P0 · api · Tier 1 → pass (runner A3)api-backend.action-activation-door-contractr2 · P2 · api · Tier 1 → pass (runner A4)api-backend.aggregate-contract-matrixr2 · P1 · api · Tier 2 → partial (runner A1)api-backend.formula-stdlib-matrixr2 · P1 · mixed · Tier 2 → pass (runner A2)api-backend.api-methods-verb-gater1 · P1 · api · Tier 2 → pass (runner A1)api-backend.rest-crud-config-contractr2 · P2 · api · Tier 1 → pass (runner A4)api-backend.rest-batch-config-contractr2 · P2 · api · Tier 1 → pass (runner A4)api-backend.rest-metadata-config-contractr4 · P1 · api · Tier 1 → pass (runner A4)api-backend.rest-route-generation-tombstonesr1 · P2 · build · Tier 1 → pass (runner A4)api-backend.second-object-exposure-and-internal-fieldsr1 · P1 · api · Tier 1 → pass (runner A3)api-backend.data-request-shape-and-query-arityr1 · P1 · api · Tier 1 → pass (runner A3)api-backend.meta-read-doors-v18r1 · P2 · api · Tier 1 → pass (runner A4)api-backend.error-sentences-and-unique-violation-wire-coder1 · P2 · api · Tier 1 → fail (runner A4)
Per-clause verdicts
| item | clause | short | verdict | evidence |
|---|---|---|---|---|
| api-backend.batch-transactional-discovery | acc[0] | discovery capability agrees with live atomic batch | pass | GET /discovery capabilities.transactionalBatch = {enabled:true, description:'Atomic cross-object batch endpoint ...'}; atomic batch [create note qa-b… |
| api-backend.batch-transactional-discovery | acc[1] | top-level atomic:false -› 400 BATCH_NOT_ATOMIC | pass | 400 {code:BATCH_NOT_ATOMIC, error:'Cross-object batch is always atomic; use POST /data/:object/batch for non-atomic per-object batches'}; neither qa-… |
| api-backend.batch-transactional-discovery | acc[2] | failing member rolls whole batch back | pass | [note, invoice missing name, task] -› 400 VALIDATION_FAILED, re-reads qa-b4-note/qa-b4-task count=0; runtime-failure variant [create note, update tas… |
| api-backend.batch-transactional-discovery | acc[3] | oversize batch 400 BATCH_TOO_LARGE, nothing created | pass | 201 ops -› 400 {code:BATCH_TOO_LARGE, count:201, max:200}; qa-b5-over count=0 |
| api-backend.batch-transactional-discovery | acc[4] | per-object door non-atomic, continueOnError semantics | pass | continueOnError:true -› total 3 succeeded 2 failed 1, row1 VALIDATION_FAILED 'Title is required', rows 0 and 2 persisted; default options -› succeede… |
| api-backend.batch-transactional-discovery | neg[0] | doors not conflated | pass | atomic:false refused on /batch (not accepted-and-ignored); per-object door kept valid row 0 (no silent rollback) |
| api-backend.formula-gates | acc[0] | runtime shapes correct against BUILT package | pass | harness resolved @objectstack/formula -› packages/formula/dist/index.js (dist mtime 12:30 › src 12:24); record.due_date('2026-10-19')==today()@2026-1… |
| api-backend.formula-gates | acc[1] | date arithmetic is build-time error | pass | scratch showcase copy + showcase_project.qa_duration_days = celrecord.end_date - record.start_date + 1: os build EXIT 1, '✗ Author-time rules faile… |
| api-backend.formula-gates | acc[2] | build error located at object/field | pass | "object 'showcase_project' · field 'qa_duration_days' expression: date arithmetic ... Use daysBetween(a, b) ... — source: `record.end_date - record… |
| api-backend.formula-gates | acc[3] | untouched showcase builds exit 0 | pass | byte-identical scratch copy of examples/app-showcase (uses record.f_date ‹ today() etc.): os build EXIT 0, 'Build complete', 24 Objects 210 Fields,… |
| api-backend.formula-gates | neg[0] | harness must not load src | pass | RESOLVED /home/user/objectstack/packages/formula/dist/index.js (realpath identical) — no src/ path |
| api-backend.enforce-or-remove-authoring-gates | acc[0] | tombstoned keys rejected; filter op runtime-refused | pass | built @objectstack/spec dist: QuerySchema cursor/joins/distinct/windowFunctions all success=false (baseline {object} true); EnhancedApiError fieldErr… |
| api-backend.enforce-or-remove-authoring-gates | acc[1] | each rejection names replacement | pass | cursor: 'Express the keyset as an ordinary where predicate on your sort key'; joins: 'Related records are read through expand'; fieldErrors: 'ren… |
| api-backend.enforce-or-remove-authoring-gates | acc[2] | documented-strip control strips; includeObjects refused | pass | RestServerConfigSchema.safeParse({routes:{someUnknownKey:1}}) success data {"routes":{}}; RouteGenerationConfigSchema docblock rest-server.zod.ts:648… |
| api-backend.enforce-or-remove-authoring-gates | acc[3] | ≥3 registries sampled | pass | query schema (data/query.zod.ts retiredKey x4), error envelope (api/errors.zod.ts fieldErrors), RETIRED_FILTER_OPERATORS (data/filter.zod.ts |
| api-backend.enforce-or-remove-authoring-gates | neg[0] | strip deliberate; no tombstone parses clean | pass | every sampled tombstone failed parse; only the documented non-strict routes unknown key stripped |
| api-backend.query-contract-matrix | acc[0] | every operator: exact id set, both spellings | pass | live, baseline GET showcase_account 14 rows; 21 variants × 3 doors (POST $-object, POST AST array, GET $filter AST) all set-equal on ids: eq 9, ne 5,… |
| api-backend.query-contract-matrix | acc[1] | pagination windowed slices | pass | GET $orderby=name&$top=3&$skip=2 → [Fabrikam,Globex,Hooli] = baseline[2:5]; $top=4&$skip=9 → [Umbrella Health,Vandelay Industries,Wayne Enterprises,W… |
| api-backend.query-contract-matrix | acc[2] | $select narrows; dotted field refused INVALID_FIELD | pass | GET $select=name,status and POST fields:['name','status'] → all 14 records key set exactly {name,status}; POST showcase_invoice fields:['account.name… |
| api-backend.query-contract-matrix | acc[3] | full sort both ways; INVALID_SORT | pass | annual_revenue asc/desc full 14-id sequence equals locally sorted baseline on GET $orderby and POST orderBy; $orderby=no_such_field and POST orderBy … |
| api-backend.query-contract-matrix | acc[4] | expand honors target RLS/FLS (both personas) | pass | contributor-bound member (qa-contributor-bound-member recipe; sees 0 of 12 seeded invoices) created own INV-A3-001 with contact=Carl Fox; contributor… |
| api-backend.query-contract-matrix | acc[5] | scalar comparand refused on both doors | pass | POST {where:{status:{$nin:'done'}}} → 400 VALIDATION_FAILED fields[0].field 'query.where.status.$nin' message names $nin, status, ARRAY; POST AST ['s… |
| api-backend.query-contract-matrix | acc[6] | unknown inputs get ledgered codes | pass | ?$pageSize=5 → 400 UNSUPPORTED_QUERY_PARAM listing $top,$skip,$orderby,$select,$count,$search,$searchFields,$filter,$expand; ?not_a_field=x → 400 INV… |
| api-backend.query-contract-matrix | acc[7] | $in [] zero rows, $nin [] full set | pass | showcase_task (10 rows): $in [] → 200, 0 rows (POST obj, POST AST, GET); $nin [] → 200, 10 rows = full id set (POST obj + AST) |
| api-backend.query-contract-matrix | acc[8] | every variant has a recorded verdict | pass | all 16 op variants (+like verbatim, date, unicode) and params $top,$skip,$select,$orderby,$filter,$expand (list GET $expand=account → embedded accoun… |
| api-backend.query-contract-matrix | neg[0] | no silent widening on malformed body | pass | POST {limit:'ten'} → 400 VALIDATION_FAILED query.limit; POST {not_a_query_key:{x:1}} → 400 INVALID_FIELD; no response carried rows |
| api-backend.error-envelope-ledger | acc[0] | every sampled code is in ErrorCode | pass | VALIDATION_FAILED, UNAUTHENTICATED, PERMISSION_DENIED, OBJECT_NOT_FOUND, RECORD_NOT_FOUND, BATCH_NOT_ATOMIC, UNSUPPORTED_QUERY_PARAM all ErrorCode.sa… |
| api-backend.error-envelope-ledger | acc[1] | validation sample has fields[] name/required | pass | POST showcase_invoice {} -› 400 {code:VALIDATION_FAILED, fields:[{field:'name',code:'required',message:'Invoice Number is required',label:'Invoice Nu… |
| api-backend.error-envelope-ledger | acc[2] | no fieldErrors key in any body | pass | grep of all 7 captured bodies: no 'fieldErrors' |
| api-backend.error-envelope-ledger | acc[3] | status matches code family | pass | 400 VALIDATION_FAILED/BATCH_NOT_ATOMIC/UNSUPPORTED_QUERY_PARAM, 401 UNAUTHENTICATED, 403 PERMISSION_DENIED, 404 OBJECT_NOT_FOUND/RECORD_NOT_FOUND; no… |
| api-backend.error-envelope-ledger | acc[4] | two 404 flavors distinguishable | pass | not_a_real_object -› 404 OBJECT_NOT_FOUND "Object 'not_a_real_object' is not registered"; showcase_task/nonexistent-id-0000 -› 404 RECORD_NOT_FOUND "… |
| api-backend.error-envelope-ledger | acc[5] | one sample per family captured | pass | 7/7 families: validation, 401 (anon GET showcase_private_note), 403 (Mei POST showcase_announcement -› PERMISSION_DENIED; identity proven via get-ses… |
| api-backend.error-envelope-ledger | neg[0] | no 500 for caller-fixable input, no 2xx | pass | all 7 probes 4xx |
| api-backend.bulk-write-contract | acc[0] | createMany parity with single door | pass | baseline member (admin create-user, no position): createMany bare array of 3 → 201; {records} wrapper → 400 VALIDATION_FAILED; createMany row vs sing… |
| api-backend.bulk-write-contract | acc[1] | updateMany touches only named rows | pass | updateMany {records:[{id,data:{body:'A3-UPDATED'}}×3]} → total 3 succeeded 3; re-read: 3 markers updated, 4th own note body null, admin-owned foreign… |
| api-backend.bulk-write-contract | acc[2] | nonexistent/foreign id calm per-row RECORD_NOT_FOUND | pass | member updateMany foreign admin note alone → 200 success false, RECORD_NOT_FOUND; nonexistent alone → RECORD_NOT_FOUND (identical code); both with op… |
| api-backend.bulk-write-contract | acc[3] | 201 records → 400 BATCH_TOO_LARGE, zero rows | pass | admin createMany 201 → 400 BATCH_TOO_LARGE count 201 max 200; query A3-CAP-* → 0 rows; /batch create 201 → 400 BATCH_TOO_LARGE |
| api-backend.bulk-write-contract | acc[4] | deleteMany id-exact, per-row delete gate | pass | member deleteMany 2 own ids → row0 PERMISSION_DENIED, row1 NOT_ATTEMPTED, nothing deleted (single DELETE 403); {where} → 400 naming ids; admin delete… |
| api-backend.bulk-write-contract | acc[5] | no ERR_BULK_RESULT_MISMATCH | pass | grep of all captured responses and both boot logs → 0 hits |
| api-backend.bulk-write-contract | neg[0] | invisible row neither counted nor mutated | pass | foreign note → RECORD_NOT_FOUND same as nonexistent, succeeded 0, row unchanged |
| api-backend.declarative-endpoint-execution | acc[0] | authed 200 + Cache-Control private, max-age=30 | pass | live GET /api/v1/apps/showcase/tasks (admin bearer) → 200, cache-control: private, max-age=30, body {success,data:{object:'showcase_task',records:[…]… |
| api-backend.declarative-endpoint-execution | acc[1] | anonymous → 401 UNAUTHENTICATED | pass | live unauth GET → 401 code UNAUTHENTICATED, no cache-control header; declarative-endpoint-policy.dogfood 8/8 passed ('an OMITTED authRequired denies … |
| api-backend.declarative-endpoint-execution | acc[2] | script/proxy refused at authoring; runtime 501 pinned | pass | scratch config (baseline validate exit 0) + type 'script' and 'proxy' endpoints → os validate exit 1: '✗ apis.3.type: Endpoint 'qa_a3_script_api' (ap… |
| api-backend.declarative-endpoint-execution | acc[3] | unmatched /apps path → transport 404 | pass | GET /api/v1/apps/showcase/nope → 404 ENDPOINT_NOT_FOUND (authed and anon) |
| api-backend.declarative-endpoint-execution | neg[0] | no anonymous serving with authRequired default | pass | anon → 401 |
| api-backend.filter-comparand-conformance | acc[0] | every accepted comparand type accepted with known answers | pass | REST POST /query on showcase_field_zoo (2 rows): string name='Specimen — Full' -› 1 [Full]; number f_number $gt 100 -› 1 [Full]; boolean true -› [Ful… |
| api-backend.filter-comparand-conformance | acc[1] | non-accepted type refused, located, prescriptive | pass | POST {f_number:{$eq:{x:1}}} -› 400 VALIDATION_FAILED fields[query.where.f_number.$eq] 'Filter comparand is a plain object ... must be a string, numbe… |
| api-backend.filter-comparand-conformance | acc[2] | bigint exact-limit enforced at in-process door | pass | engine-comparand-type-door.test.ts 12/12 incl. 'refuses an oversized bigint with the envelope, and NO driver read runs'; 'a bigint beyond ±2^53 is re… |
| api-backend.filter-comparand-conformance | acc[3] | dotted heads classified and handled | pass | relation f_lookup.name -› 400 'follows the relationship ... nest the condition beneath the relation field'; virtual f_formula.x -› 400 INVALID_FIELD … |
| api-backend.filter-comparand-conformance | acc[4] | refusal is contract-level across doors | pass | same plain-object comparand: POST /query 400 VALIDATION_FAILED fields[query.where.f_number.$eq]; GET ?$filter= 400 INVALID_FILTER 'Filter comparand a… |
| api-backend.filter-comparand-conformance | neg[0] | rejected predicate never dropped to full table | pass | every bad comparand answered 400 with no records |
| api-backend.filter-comparand-conformance | neg[1] | no cross-type coercion where contract refuses | pass | f_number $gt 'abc' -› 400 INVALID_FILTER on POST and GET; f_number '420' / $eq '420' -› 1 row is the declared numeric-string grammar (filter-number-c… |
| api-backend.filter-comparand-conformance | neg[2] | past-limit bigint not answered rounded | pass | in-process door refuses beyond ±2^53 in all 5 driver suites + engine |
| api-backend.filter-comparand-conformance | neg[3] | scalar dotted head not traversed | pass | name.x -› 400 INVALID_FIELD |
| api-backend.retired-def-refusal | acc[0] | every sampled retired def refused at os build | pass | minimal scratch stack baseline EXIT 0 (agent-without-lifecycle control also EXIT 0); 17 data/ExternalDataSource import EXIT 2; 18 automation/Schedule… |
| api-backend.retired-def-refusal | acc[1] | prescription refusals distinguishable from generic control | pass | carriers: 'manifest.loading: manifest.loading was removed in @objectstack/spec 17.0.0 (ADR-0049 ...) ... Delete the key. Plugins are composed at bo… |
| api-backend.retired-def-refusal | acc[2] | runtime door refuses same types as build | pass | live boot :41310 as admin: PUT /meta/agent/qa_agent (with lifecycle, and without) 403 'Metadata type agent is code-only'; PUT /meta/external_data_sou… |
| api-backend.retired-def-refusal | acc[3] | sample spans both version prefixes | pass | registry 224 entries: 53 at prefix 17, 171 at prefix 18; sample = 17 data/ExternalDataSource(R3, none), 17 kernel/PluginLoadingConfig(carrier manifes… |
| api-backend.retired-def-refusal | neg[0] | no retired def parses clean and dropped | pass | all 5 sampled defs non-zero exit; no artifact emitted for them |
| api-backend.retired-def-refusal | neg[1] | no bare unknown-type where prescription authored | pass | both carrier defs answered with their tombstone prescription, not the root Unrecognized-key text |
| api-backend.retired-def-refusal | neg[2] | no build/runtime door disagreement | pass | see acceptance[2] table |
| api-backend.retired-def-refusal | neg[3] | no wrong successor named | pass | manifest.loading -› defineStack plugin composition; agent.lifecycle -› skill/Flow/state_machine rule; both match their entry files and ADR text |
| api-backend.packaged-action-disabled-dispatch | acc[0] | REST door 409 ACTION_DISABLED, no side effect | pass | live :41360 — POST /actions/showcase_task/showcase_mark_done {recordId:'no-such-record-0000'} and {recordId:‹real seeded task BDTORUgoFI53x3WE›} both… |
| api-backend.packaged-action-disabled-dispatch | acc[1] | activation consult precedes param contract (contrast) | pass | live: identical request {recordId:'no-such-zoo-0000',params:{bogus_key:1}} to showcase_action_param_gallery — enabled(before) 400 VALIDATION_ERROR 'I… |
| api-backend.packaged-action-disabled-dispatch | acc[2] | consult precedes record load (absence-proven) | pass | live: both disabled traces are 409 with a nonexistent recordId present; real-id disabled call left the record untouched (snapshot equal). Run-record … |
| api-backend.packaged-action-disabled-dispatch | acc[3] | 403 never an oracle for the switch | pass | live: showcase_zoo_perm_missing as admin enabled vs disabled → both 403 PERMISSION_DENIED 'requires capability [showcase.restricted_ops] — caller is … |
| api-backend.packaged-action-disabled-dispatch | acc[4] | MCP run_action refuses with same envelope, re-arms | pass | live POST /api/v1/mcp tools/call run_action {actionName:'showcase_portfolio_snapshot'}: enabled → ok:true result{accounts:14,projects:5,invoices:13};… |
| api-backend.packaged-action-disabled-dispatch | acc[5] | switch durable across restart; row updated in place | pass | live: server killed and rebooted on same file DB (:41360); post-restart showcase_mark_done 409 ACTION_DISABLED (and showcase_bulk_reassign 409); re-e… |
| api-backend.packaged-action-disabled-dispatch | acc[6] | flow-type action refused by its own switch | pass | live: disable showcase_bulk_reassign → POST /actions/showcase_task/showcase_bulk_reassign 409 ACTION_DISABLED naming showcase_bulk_reassign; GET /aut… |
| api-backend.packaged-action-disabled-dispatch | neg[0] | no 400/handler answer for a disabled action | pass | every disabled invocation (mark_done, param_gallery w/ garbage params, bulk_reassign, portfolio_snapshot) answered 409, never 400/200 |
| api-backend.packaged-action-disabled-dispatch | neg[1] | 403 wins over 409; no ACTION_DISABLED without a row | pass | 403 bodies identical enabled/disabled; with an empty ledger (GET sys_metadata_activation → total 0) enabled calls answered their normal shapes (param… |
| api-backend.packaged-action-disabled-dispatch | neg[2] | anonymous floor 401 | pass | anonymous POST /actions/showcase_task/showcase_mark_done → 401 UNAUTHENTICATED |
| api-backend.action-activation-door-contract | acc[0] | authority first: non-admin 403, anonymous 401 | pass | phone.demo@example.com (session verified) POST /actions/_activation/showcase_task/no_such_action {"enable":false} → 403 {code:PERMISSION_DENIED,messa… |
| api-backend.action-activation-door-contract | acc[1] | 2-segment door 400 shape sentence; 4-segment router 404 | pass | POST /actions/_activation/showcase_mark_done {} → 400 {code:VALIDATION_ERROR,message:'Path must be /actions/_activation/:object/:action (use global… |
| api-backend.action-activation-door-contract | acc[2] | body contract refuses, never inverts | pass | {"enable":false} → 400 VALIDATION_FAILED details.fields[0]={field:enable,code:unknown_field,message:'… did you mean enabled?'}; {"enabled":"false"}… |
| api-backend.action-activation-door-contract | acc[3] | empty body enables | pass | POST …/showcase_task/showcase_mark_done {} → 200 {success:true,data:{name:showcase_mark_done,objectName:showcase_task,enabled:true}} |
| api-backend.action-activation-door-contract | acc[4] | undeclared 404; refused probes write no row | pass | admin …/no_such_action {"enabled":false} → 404 {code:RESOURCE_NOT_FOUND,message:"Action 'no_such_action' on 'showcase_task' has no declaration — ther… |
| api-backend.action-activation-door-contract | acc[5] | happy path writes one row, no organization_id key, re-enable updates | pass | {"enabled":false} 200 → ledger total 1, row KjAB3v4BBIBA0QTo action showcase_mark_done active=false, keys=[active,created_at,created_by,id,metadata_t… |
| api-backend.action-activation-door-contract | acc[6] | ambiguity arm 409 RESOURCE_CONFLICT, no write (pin) | pass | oracle=test (no stock fixture): runtime action-activation-posture-gate.test.ts 'REFUSES an ambiguous name instead of switching off artifacts the call… |
| api-backend.action-activation-door-contract | acc[7] | 501 no projection, 503 non-durable (pins) | pass | posture-gate pin 'says so plainly when the engine has no activation ledger at all' ✓ and 'reports a write that could not be made durable, instead of … |
| api-backend.action-activation-door-contract | neg[0] | {enable:false} never 200 | pass | answered 400 unknown_field; ledger unchanged (0 rows) |
| api-backend.action-activation-door-contract | neg[1] | non-manage_metadata caller never sees body 400/decl 404 | pass | non-admin mis-bodied undeclared probe → 403 only; message names no positions/sets |
| api-backend.action-activation-door-contract | neg[2] | no refused arm leaves a ledger row | pass | 0 rows after 2 authority, 2 shape, 3 body, 1 declaration refusals |
| api-backend.action-activation-door-contract | neg[3] | _activation never read as invocation | pass | 2-segment _activation path answered the door's shape 400, not an action-invocation body |
| api-backend.aggregate-contract-matrix | acc[0] | 6 functions known answers grouped+ungrouped, count-all vs count-field | pass | 17 baseline accounts; per-function grouped-by-status and ungrouped values all MATCH local computation (e.g. sum active 609100000, avg active 67677777… |
| api-backend.aggregate-contract-matrix | acc[1] | 5 granularities ISO shapes, Monday weeks, real-null bucket | pass | clock 2026-10-19T12:46Z, TZ UTC; day/week/month/quarter/year bucket keys+counts all MATCH local ISO computation (14/13/12/8/3 distinct non-null bucke… |
| api-backend.aggregate-contract-matrix | acc[2] | empty-set identities on empty-where and measure filter | pass | empty where ungrouped -› {count:0,sum:0,avg:null,min:null,max:null,count_distinct:0}, grouped -› []; aggregations[].filter matching nothing, grouped … |
| api-backend.aggregate-contract-matrix | acc[3] | retired fns prescriptions vs unknown enum listing | pass | array_agg and string_agg -› 400 VALIDATION_FAILED fields[query.aggregations.0.function] 'array_agg was removed ... Run os migrate meta --from 16 … |
| api-backend.aggregate-contract-matrix | acc[4] | aggregate secured for restricted persona | pass | contributor-bound member (invited sign-up + sys_user_position contributor, one own invoice): own baseline 1 row {draft:1}, grouped count -› [{draft:1… |
| api-backend.aggregate-contract-matrix | acc[5] | all 11 variants carry recorded verdict | pass | 6 function rows + 5 granularity rows recorded; all 5 granularities answer-discriminating after seeding 3 accounts (2026-01-04 null revenue, 2024-12-3… |
| api-backend.aggregate-contract-matrix | neg[0] | refused aggregation never degrades to raw rows | pass | all refusals 400 with error bodies, no records array |
| api-backend.aggregate-contract-matrix | neg[1] | nonexistent aggregation field refused | pass | sum over no_such_field -› 400 INVALID_FIELD 'Unknown field ... sum answered 0 ...'; groupBy no_such_field -› 400 INVALID_FIELD |
| api-backend.aggregate-contract-matrix | neg[2] | pushdown vs in-memory labels agree | not-run | timezone lever not exercised; only the UTC path observed (path not observable on this boot) |
| api-backend.formula-stdlib-matrix | acc[0] | 29 functions known answers; docs match | pass | source re-count registerStdLib = 29 registerFunction calls (now..isEmpty, can outside); formulas.mdx table 19 rows / 29 names; registered-not-docum… |
| api-backend.formula-stdlib-matrix | acc[1] | calendar-day trio uses reference tz | pass | now=2026-08-30T22:00:00Z tz Asia/Shanghai: today()=2026-08-31T00:00:00.000Z, daysFromNow(1)=09-01, daysAgo(1)=08-30, isoDate(today())='2026-08-31'; n… |
| api-backend.formula-stdlib-matrix | acc[2] | documented sharp edges hold | pass | addMonths(date('2026-01-31'),1)=2026-02-28T00:00Z; floor(-1.2)=-2 ceil(-1.2)=-1; joinNonEmpty([record.a..d],'-') with {a,null,' ',b}='a-b' (literal m… |
| api-backend.formula-stdlib-matrix | acc[3] | mixed arithmetic promotes; int/int integer | pass | record.amount(250.5)/100=2.505; record.amount(1.25)*2=2.5; 7/2=3 |
| api-backend.formula-stdlib-matrix | acc[4] | same evaluator serves live read path | pass | live :41310: GET showcase_project budget_remaining 2000/180000/90000 (=budget-spent); showcase_field_zoo f_formula 315, 3.5; scratch object qa_fx_pro… |
| api-backend.formula-stdlib-matrix | acc[5] | unregistered function refused 422 at authoring door | pass | PUT /meta/object/qa_fx_sqrt{1,2} (active) bad_f=sqrt(4) -› 422 code INVALID_METADATA issue rule expression-invalid path "object 'qa_fx_sqrt1' · field… |
| api-backend.formula-stdlib-matrix | neg[0] | harness not loading src | pass | RESOLVED packages/formula/dist/index.js |
| api-backend.formula-stdlib-matrix | neg[1] | today() no wall-clock / UTC-day drift | pass | today() values all exact UTC midnight; Shanghai run returned zone day, not UTC day |
| api-backend.formula-stdlib-matrix | neg[2] | unregistered fn never saves 2xx | pass | active save 422 x2, promotion 422, nothing stored |
| api-backend.api-methods-verb-gate | acc[0] | 404 apiEnabled:false vs 405 whitelist-miss fork | pass | [system objects] admin GET /data/sys_oauth_consent (and /abc) -› 404 OBJECT_API_DISABLED "Object 'sys_oauth_consent' is not exposed via the API"; PAT… |
| api-backend.api-methods-verb-gate | acc[1] | 405 allowed[] is effective closure in API_OPERATION_ORDER | pass | [scratch qa_verb_gate_probe ['get','list','create']] PATCH/DELETE -› 405 allowed ["get","list","create","aggregate","search","import","export"] == lo… |
| api-backend.api-methods-verb-gate | acc[2] | three-state: undefined open, []/stripped/non-array deny-all, subset | pass | [scratch] no apiMethods -› PATCH 200 + DELETE 200; authored [] -› GET one 405 allowed:[]; ['upsert'] stripped -› list/get/create/update all 405 allow… |
| api-backend.api-methods-verb-gate | acc[3] | bulk∧child and writeMode-precise import | pass | [scratch] without bulk: createMany -› 405 'bulk'; with ['get','list','create','bulk']: createMany 201, updateMany 405 'update', per-object batch upda… |
| api-backend.api-methods-verb-gate | acc[4] | legacy values stripped-and-warned, not rejected | pass | PUT ['upsert'] -› 200; log: "[spec] enable.apiMethods declares retired legacy value(s) [upsert] ... upsert: declare ['create','update'] — upsert … |
| api-backend.api-methods-verb-gate | acc[5] | object gate answers before caller authz (405 before 403) | pass | [sys_session] PATCH as admin, as Mei (own session and another's) -› identical 405 allowed [get,list,aggregate,export]; anonymous -› 401 UNAUTHENTICAT… |
| api-backend.api-methods-verb-gate | acc[6] | cross-object batch gated per-op before txn | pass | POST /batch [create showcase_task 'qa-vg-batch-task', update sys_session] atomic -› 405 "API operation 'bulk' is not allowed on object 'sys_session'"… |
| api-backend.api-methods-verb-gate | neg[0] | no write-then-refuse on refused ops | pass | every 405 was followed by a re-read: sys_session revoke_reason null, probe row name 'r1' unchanged after refused PATCH/import-update/updateMany, batc… |
| api-backend.api-methods-verb-gate | neg[1] | allowed[] never raw whitelist, never restore/purge | pass | allowed[] carries derived aggregate/search/import/export beyond raw ['get','list','create']; restore/purge absent on every captured body incl. unrest… |
| api-backend.api-methods-verb-gate | neg[2] | internal callers not filed as bypass | pass | no internal-path write was scored; only external REST doors probed |
| api-backend.api-methods-verb-gate | neg[3] | legacy value not hard-rejected at parse | pass | PUT ['upsert'] and ['get','history'] both answered 200 (strip-and-warn) |
| api-backend.rest-crud-config-contract | acc[0] | stock: six default data mounts answer | pass | admin :41370 showcase_task: GET list 200, POST create 201 (id u_dy0FFxGmPlXJA2), GET /:id 200, POST /query 200, PATCH /:id 200, DELETE /:id 200 {succ… |
| api-backend.rest-crud-config-contract | acc[1] | create/read/update/delete remove one; list removes two | pass | harness diffs, added=[]: create → POST /data/:object; read → GET /data/:object/:id; update → PATCH …/:id; delete → DELETE …/:id; list → GET /data/:ob… |
| api-backend.rest-crud-config-contract | acc[2] | unmounted route distinguishable from OBJECT_API_DISABLED | pass | switched-off op: no handler in the RestServer table (harness); routing 404 control body {success:false,error:{code:ENDPOINT_NOT_FOUND,message:'Not fo… |
| api-backend.rest-crud-config-contract | acc[3] | dataPrefix re-bases data surface and discovery | pass | harness {crud:{dataPrefix:'/records'}}: 0 routes under /api/v1/data; 22 under /api/v1/records = the full baseline data family (CRUD, query, clone, ex… |
| api-backend.rest-crud-config-contract | acc[4] | crud.patterns and objectParamStyle refused at construction | pass | 'crud does not satisfy CrudEndpointsConfigSchema … crud.patterns was removed … crud.dataPrefix is the one live knob … an endpoint on a custom… |
| api-backend.rest-crud-config-contract | acc[5] | schema defaults are the only defaults; partial ops kept | pass | pin §C/§D ✓ ('KEEPS a partial crud.operations'); harness empty → crud {operations: all five true, dataPrefix:'/data'}; {operations:{create:false}} … |
| api-backend.rest-crud-config-contract | neg[0] | no route stays mounted with switch false | pass | every crud switch-off table lacks exactly its route(s) |
| api-backend.rest-crud-config-contract | neg[1] | absence never scored from bare 404 | pass | absences scored from the route table + the control body, all live probes authenticated |
| api-backend.rest-batch-config-contract | acc[0] | stock: four per-object bulk doors mounted | pass | admin :41370 showcase_private_note: POST …/batch 200 {success:true,operation:update,total:0}, /createMany 201 {records:[],count:0}, /updateMany 200, … |
| api-backend.rest-batch-config-contract | acc[1] | enableBatchEndpoint gates only per-object /batch | pass | harness diff enableBatchEndpoint:false → removed ['POST /api/v1/data/:object/batch'], added []; 'POST /api/v1/batch' still mounted (true). Pin §1 'ba… |
| api-backend.rest-batch-config-contract | acc[2] | each *Many removes own mount; gates AND protocol member | pass | harness: createMany/updateMany/deleteMany false each remove exactly POST …/createMany | /updateMany | /deleteMany; protocol without batchData/creat… |
| api-backend.rest-batch-config-contract | acc[3] | maxBatchSize is the one cap for all five doors | pass | pin rest-batch-size-cap.test.ts 8/8 ✓ (incl. 'the cap follows the deployment config, not a hardcoded 200', 'cross-object batch … BATCH_TOO_LARGE'); h… |
| api-backend.rest-batch-config-contract | acc[4] | live: 201 records → 400 BATCH_TOO_LARGE, nothing created | pass | POST /data/showcase_private_note/createMany 201 rows → 400 {error:'Batch too large: 201 records (max 200)',code:BATCH_TOO_LARGE,count:201,max:200}; q… |
| api-backend.rest-batch-config-contract | acc[5] | upsertMany and defaultAtomic (true/false) refused | pass | constructor throws 'batch does not satisfy BatchEndpointsConfigSchema … batch.operations.upsertMany was removed … Upsert is an operation type o… |
| api-backend.rest-batch-config-contract | acc[6] | maxBatchSize bounds 0/2000/2.5 refused; empty → 200 | pass | pin §A 3 refusals ✓ + §C 'an empty config still constructs, with the declared defaults' ✓; harness texts 'Too small: expected number to be ›=1' / 'To… |
| api-backend.rest-batch-config-contract | neg[0] | no bulk door mounted with switch false; cap honours config | pass | every switch-off table lacks its route; cap 5 reported max:5 |
| api-backend.rest-batch-config-contract | neg[1] | cross-object /batch not used as enableBatchEndpoint evidence | pass | per-object switch scored from the per-object mount only |
| api-backend.rest-metadata-config-contract | acc[0] | stock default-prefix metadata mounts answer, not routing-404 | pass | live :41370 admin: GET /meta 200, /meta/types 200, /meta/diagnostics 200 {entries:[],scannedTypes:27}, /meta/_drafts 200 {drafts:[]}, /meta/object 20… |
| api-backend.rest-metadata-config-contract | acc[1] | prefix moves all metadata mounts and discovery advertisement | pass | harness {metadata:{prefix:'/metadata'}}: 0 routes left under /api/v1/meta, 19 under /api/v1/metadata (baseline 19 under /meta); GET /api/v1/discovery… |
| api-backend.rest-metadata-config-contract | acc[2] | four endpoint switches each remove exactly their named mounts | pass | harness diffs vs all-true baseline (75 routes), added=[] for all: types → GET /meta, GET /meta/types; items → GET /meta/:type only; maintenance → GET… |
| api-backend.rest-metadata-config-contract | acc[3] | maskObjectFields default ON hides unreadable fields; false passes through | pass | live: member a4.liaison (granted showcase_client_liaison, session verified via /auth/get-session) GET /meta/object/showcase_project → 19 fields; admi… |
| api-backend.rest-metadata-config-contract | acc[4] | enableCache selects cached read; six carve-outs bypass it | pass | harness GET /meta/:type/:name with getMetaItemCached+getMetaItem spies: enableCache=true plain object → cached 1/uncached 0; enableCache=false → cach… |
| api-backend.rest-metadata-config-contract | acc[5] | cacheTtl (3600/0/-1) and endpoints.schema refused at construction | pass | all four throw 'REST API configuration is invalid: metadata does not satisfy MetadataEndpointsConfigSchema … metadata.cacheTtl was removed in @… |
| api-backend.rest-metadata-config-contract | neg[0] | served schema never carries an unreadable field | pass | liaison showcase_project document: no budget/spent/budget_remaining key or string in the whole body |
| api-backend.rest-metadata-config-contract | neg[1] | masking never scored from admin's document | pass | masking scored from the liaison member (no admin capability); admin copy used only as the diff baseline |
| api-backend.rest-metadata-config-contract | neg[2] | no metadata route mounted with its switch false | pass | every switch-off table lacks exactly its routes; no route survives its own switch |
| api-backend.rest-route-generation-tombstones | acc[0] | four routes.* keys refused at construction | pass | includeObjects, excludeObjects, nameTransform 'none' AND 'plural', overrides each throw 'routes does not satisfy RouteGenerationConfigSchema (@ob… |
| api-backend.rest-route-generation-tombstones | acc[1] | refusal survives createRestApiPlugin().start() | pass | pin §B 'rejects createRestApiPlugin({ api: { routes: { nameTransform: "none" } } }).start()' ✓ and §E 'the plugin path refuses the same keys' ✓; harn… |
| api-backend.rest-route-generation-tombstones | acc[2] | each prescription names a replacement that exists | pass | enable.apiEnabled (spec data/object.zod.ts:271) live — showcase_a4_hidden answers 404 OBJECT_API_DISABLED; enable.apiMethods (object.zod.ts:283); cru… |
| api-backend.rest-route-generation-tombstones | acc[3] | authoring any of the four is a tsc error | pass | RUNDIR/tsc tsc -p (repo TypeScript, spec + rest dist types): cfg.ts(6,41) TS2741 includeObjects, (7,41) TS2741 excludeObjects, (8,41) TS2322 nameTran… |
| api-backend.rest-route-generation-tombstones | acc[4] | routes:{} constructs; unknown key stripped | pass | harness: routes:{} → no refusal, normalized routes {}; routes:{someUnknownKey:1} → no refusal, normalized {}; pin §E 'an empty routes sub-object st… |
| api-backend.rest-route-generation-tombstones | neg[0] | no routes.* key parses clean anywhere | pass | refused at constructor, plugin path and tsc |
| api-backend.rest-route-generation-tombstones | neg[1] | not scored from spec schema tests alone | pass | scored from the real RestServer/createRestApiPlugin (dist) and rest pins |
| api-backend.second-object-exposure-and-internal-fields | acc[0] | permanent pins pass at 5b14b7f | pass | t1-api-backend.log: @objectstack/metadata-protocol protocol.relation-condition-target-exposure.test.ts 13, protocol.expand-target-exposure.test.ts 15… |
| api-backend.second-object-exposure-and-internal-fields | acc[1] | nested-relation filter → 404/405, both personas | pass | scratch app (:41361) showcase_qa_source→showcase_qa_hidden(apiEnabled:false)/showcase_qa_getonly(apiMethods:['get']). Admin AND member identical: GET… |
| api-backend.second-object-exposure-and-internal-fields | acc[2] | $expand into unexposed target → bare id | pass | admin+member: list GET $expand=t_ref,g_ref, by-id ?expand=, POST expand map → t_ref = 'gW6cvWJAmP0VYO7Q' (bare id); control g_ref (get-whitelisted, a… |
| api-backend.second-object-exposure-and-internal-fields | acc[3] | internal field 400 INVALID_FIELD in every position | pass | admin+member: filter (GET filter, implicit ?internal_f=, $filter, POST where) / sort (?sort, $orderby, POST orderBy) / groupBy / aggregations[0].fiel… |
| api-backend.second-object-exposure-and-internal-fields | neg[0] | no 200 row-count oracle on internal filter | pass | no internal-field evaluate position answered 200 |
| api-backend.data-request-shape-and-query-arity | acc[0] | permanent pins pass at 5b14b7f | pass | t1-api-backend.log @5b14b7f3: @objectstack/rest request-schema-gate.conformance.test.ts 16 passed; @objectstack/runtime data-write-request-gate.test.… |
| api-backend.data-request-shape-and-query-arity | acc[1] | null/array/scalar PATCH + empty token → 400, updated_at unchanged | pass | PATCH /data/showcase_task/BDTORUgoFI53x3WE body null/[]/5/"str" → each 400 VALIDATION_FAILED fields[data] 'expected record, received …'; {expectedVer… |
| api-backend.data-request-shape-and-query-arity | acc[2] | null /query body → 400 | pass | POST /data/showcase_task/query body null → 400 VALIDATION_FAILED fields[query] 'expected object, received null', no records |
| api-backend.data-request-shape-and-query-arity | acc[3] | repeated query name 400; doubled keepData refused | pass | GET /packages?status=a&status=b → 400 VALIDATION_ERROR 'supplied 2 times'; DELETE /packages/com.example.showcase?keepData=true&keepData=true → 400 VA… |
| api-backend.data-request-shape-and-query-arity | acc[4] | stale If-Match on unreadable row → 404 | pass | contributor-bound member (task RLS hides BDTORUgoFI53x3WE) PATCH with If-Match "2020-01-01T00:00:00.000Z" → 404 RECORD_NOT_FOUND (no currentRecord); … |
| api-backend.data-request-shape-and-query-arity | neg[0] | doubled keepData never honoured | pass | 400 before any work; package + tables intact (reads above) |
| api-backend.meta-read-doors-v18 | acc[0] | seven permanent pins pass at subject sha | pass | t1-api-backend.log @5b14b7f3: metadata-protocol protocol.app-package-id-key 8 ✓ (Test Files 4 passed, EXIT 0); runtime meta-app-package-id-key 12 ✓, … |
| api-backend.meta-read-doors-v18 | acc[1] | app by package id; unknown package = unknown-name 404 | pass | GET /meta/app/com.example.showcase 200 {type:app,name:showcase_app,…}; /meta/app/com.unknown.pkg 404 {error:{code:RESOURCE_NOT_FOUND,message:'Metadat… |
| api-backend.meta-read-doors-v18 | acc[2] | ?member= reads one member; non-bundled 400; unknown param 400 | pass | GET /meta/email_template/auth.password_reset?member=zh-CN 200 item.locale=zh-CN label 密码重置 (ja-JP → ja-JP; none → en-US); /meta/object/showcase_task?… |
| api-backend.meta-read-doors-v18 | acc[3] | ?package=all byte-identical to no parameter | pass | GET /meta/view?package=all vs /meta/view: cmp BYTE-IDENTICAL, 36 ids equal in order; discriminating control on object: ?package=all 107 items byte-id… |
| api-backend.meta-read-doors-v18 | acc[4] | FLS-hidden field unsortable in meta and 403 on data sort | pass | liaison member on showcase_project (stock FLS fixture; showcase_task has none): sortability.fields has no budget/spent entry (name:{sortable:true}); … |
| api-backend.error-sentences-and-unique-violation-wire-code | acc[0] | four permanent pins pass at subject sha | pass | t1-api-backend.log @5b14b7f3: packages/rest rest-write-route-hook-refusal-sentence.ledger.test.ts 577 ✓ 0 ✗, rest-hook-script-fault-envelope.test.ts … |
| api-backend.error-sentences-and-unique-violation-wire-code | acc[1] | hook refusal → 400 VALIDATION_ERROR with hook sentence | blocked(fixture) | the step's sys_sharing_rule hook cannot be authored by a package, so the nested-envelope VALIDATION_ERROR rows (sharing/approvals/suggested-bindings … |
| api-backend.error-sentences-and-unique-violation-wire-code | acc[2] | duplicate via action door → 409 UNIQUE_VIOLATION, details name field | fail | scratch :41371 POST /api/v1/actions/showcase_a4_unique/showcase_a4_dup (twice, also with /:recordId) → 409 {success:false,error:{code:UNIQUE_VIOLATIO… |
| api-backend.error-sentences-and-unique-violation-wire-code | acc[3] | forced storage 500 carries generic sentence only | pass | scratch :41372 with OS_STORAGE_LOCAL_ROOT under a regular file: PUT /api/v1/storage/_local/raw/‹token› (twice) → 500 {success:false,error:{code:INTER… |
Reproduction rules (fails)
api-backend.error-sentences-and-unique-violation-wire-codeacceptance[2] — disposition: assertion-defect · the product matches its pinned contract (dispatcher-unique-violation-wire-code.test.ts: details carry field only when the driver names a single column; a tenancy-scoped composite unique names none), the clause over-claims. Per this wave's plan (no checklist edits, no anchor cards from runners) the test-fix is parked on the wave anchor via the Checklist-accuracy section below for the dispatch session to collect; no product card.- repro: scratch app: object showcase_a4_unique {code: text unique:true}; script action showcase_a4_dup (perRecord) whose body does ctx.api.object('showcase_a4_unique').insert({code:'DUP-A4'}); POST /api/v1/data/showcase_a4_unique {code:'DUP-A4'} → 201; POST /api/v1/actions/showcase_a4_unique/showcase_a4_dup {recordId:‹id›} → 409
- expected vs actual: clause expects details naming the field; actual details = {developerMessage, object}, no field. The pin dispatcher-unique-violation-wire-code.test.ts asserts exactly this key set for an engine write ('The driver names no single column: the index is the tenancy-scoped composite' → ['developerMessage','object']) and adds
fieldonly when DuplicateRecordError states a column. Product matches its pinned contract; the clause over-claims. - mechanism: packages/runtime/src/dispatcher-unique-violation-wire-code.test.ts:195-201 (key set without field) vs :207-211 (field only for a named-column throw)
Derived item verdicts
20 pass · 1 partial · 1 fail · 0 blocked · 5 not-run (of 27).
Tier 1 pin runs (RUNNER rule 6, one vitest batch per package, all at the subject sha):
- packages/metadata-protocol: 4 files, 77 passed (relation-condition-target-exposure, expand-target-exposure, data-door-internal-field-positions, app-package-id-key)
- packages/runtime: 12 files, 296 passed (action-activation-dispatch, action-activation-posture-gate, data-write-request-gate, data-delete-expected-version, dispatcher-query-arity, packages-write-flag-arity, data-conflict-caller-visibility, dispatcher-unique-violation-wire-code, endpoint-executor, meta-object-sortability-caller-fls, meta-app-package-id-key, meta-package-all-fold)
- packages/objectql: 3 files, 202 passed (engine incl. [Security: $expand bypasses RLS/FLS on the referenced object (data leak on lookups) #2850], engine-filter-array-lowering, internal-field-positions)
- packages/rest: 9 files, 742 passed (request-schema-gate.conformance, rest-sub-config-parse-not-cast, rest-config-mount-table.pin, rest-batch-size-cap, rest-write-route-hook-refusal-sentence.ledger, rest-hook-script-fault-envelope, meta-object-sortability-caller-fls, rest-server-meta-member-selector, rest-server-meta-door-closed-sets)
- packages/spec: 1 file, 48 passed (api/apis-publish-gates)
- packages/services/service-storage: 1 file, 18 passed (storage-routes.server-fault-withheld)
- packages/qa/dogfood: 5 files, 57 passed (declarative-endpoint-policy, action-params-contract, owner-anchor-and-bulk-writes, packaged-activation-ledger-reach, showcase-declarative-endpoints) — first attempt was OOM-killed (exit 137, container memory cap) and re-run with --maxWorkers=1; the re-run is the evidence
Partial / gap notes:
- api-backend.aggregate-contract-matrix: negative[2] (driver pushdown vs in-memory bucket labels agree under a non-UTC zone) not-run — the timezone lever was not exercised; everything else passed
- api-backend.error-sentences-and-unique-violation-wire-code: acceptance[1] blocked(fixture) — a package cannot author a hook on sys_sharing_rule (compile refuses), so the VALIDATION_ERROR rows rest on the ledger pin only; the /data-family probe on a scratch object returned the hook sentence verbatim with the pinned code-less 400
Handover — items with no verdict this run
api-backend.server-timing-admin-gatedr4 · P2 · api — Tier 3 deferred by the 18.0 release-sweep planapi-backend.route-ledger-live-parityr4 · P2 · api — Tier 3 deferred by the 18.0 release-sweep planapi-backend.package-rest-lifecycler3 · P2 · api — Tier 3 deferred by the 18.0 release-sweep planapi-backend.api-console-discovery-executer2 · P2 · browser — Tier 3 deferred by the 18.0 release-sweep planapi-backend.date-range-preset-matrixr2 · P2 · api — Tier 3 deferred by the 18.0 release-sweep plan
Checklist-accuracy findings / fixture gaps
Recorded for the wave anchor (collected by the dispatch session); not extracted, no checklist edit in this run.
Checklist accuracy:
- api-backend.error-envelope-ledger · acceptance[3] · HttpStatusErrorCodeMap is a status→floor-code map (400→VALIDATION_ERROR, 404→RESOURCE_NOT_FOUND …), not a code→status map; OBJECT_NOT_FOUND/RECORD_NOT_FOUND/VALIDATION_FAILED etc. have no entry in it, so the clause can only be judged by code family (ErrorHttpStatusMap category). Name the oracle the clause actually means
- api-backend.aggregate-contract-matrix · step 6 · 'aggregations as a non-array → the shape refusal with the worked example': on POST /data/:object/query the request schema answers first (400 VALIDATION_FAILED fields[query.aggregations] 'Invalid input: expected array, received object'), so protocol.ts's worked-example message (~line 13960) is not reached on this door — the same spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116 door posture filter-comparand-conformance rev 3 records. Step text should name the door or expect the schema refusal
- api-backend.api-methods-verb-gate · fixtures · scratch-object legs need OS_METADATA_WRITABLE=object on the boot and the object body needs sharingModel (author-time lint security-owd-unset 422 otherwise); the item says only 'runtime metadata authoring'. Non-array apiMethods is refused 422 by Zod at PUT, so the fail-closed arm of acceptance[2] is reachable only via the local resolver, not live
- api-backend.api-methods-verb-gate · step 7 · the cross-object /batch 405 names 'bulk' (the gate the route checks) not 'update'; the step's expectation is unspecific, recorded for the next runner
- api-backend.batch-transactional-discovery · step 4 · an invoice missing required fields fails pre-write validation, so it may not exercise a real transaction rollback; a mid-batch runtime failure (update on a nonexistent id after a create) proves the rollback more directly — consider naming it
- api-backend.formula-stdlib-matrix · acceptance[2]/step 3 · 'coalesce falls back on null/undefined ONLY' — undefined is not observable through CEL: a record key whose value is undefined faults 'No such key' before coalesce runs; the clause is effectively null-only from the authoring surface
- api-backend.enforce-or-remove-authoring-gates · step 2 · a 'view' filter is the array shape [{field,operator,value}], not a FilterCondition; the stored-$regex leg is only meaningful on a widget/report filter (used ops-dashboard widget filter)
- api-backend.retired-def-refusal · step 5 · manifest.loading's carrier has no runtime metadata-write door at all (package/manifest/plugin are 'not a metadata type'); step could say so like it does for code-only agent
- api-backend.second-object-exposure-and-internal-fields · step 1 · the literal probe filter=[["t_ref.name","=","x"]] is a DOTTED path and answers 400 INVALID_FIELD (prescribing the nested form), not the exposure refusal; the nested-relation spelling that reaches the exposure gate is filter={"t_ref":{"name":"x"}} / where {t_ref:{name:x}}
- api-backend.second-object-exposure-and-internal-fields · acceptance[1] · clause says the refusal names 'where.t_ref'; both GET and POST doors locate it as 'filter.t_ref'
- api-backend.bulk-write-contract · step 4 / acceptance[2] · updateMany stops at the first failed row by default, so a single batch [nonexistent, foreign] reports the foreign id NOT_ATTEMPTED, not RECORD_NOT_FOUND; the step must send the ids in separate calls or with options.continueOnError:true to observe the no-existence-oracle parity
- api-backend.query-contract-matrix · observation · list GET accepts $expand while single-record GET /data/:object/:id refuses $expand (400 VALIDATION_ERROR, 'Supported parameters: expand, select') with a different envelope ({error:{code,message}} vs flat {error,code}) — not an item claim, A repeated
?filter=answers two different error codes depending on which data route received it #8001-family residual - api-backend.query-contract-matrix · observation · POST /query body sort:'name' (bare string) → 400 VALIDATION_FAILED query.sort; only the array spelling is served — not an item claim
- api-backend.declarative-endpoint-execution · observation · the stack-schema refusal text reads 'Only object_operation and flow endpoints execute in 17.x' on the 18.0 subject (packages/spec/src/api/endpoint-publish-gate.ts:371) — stale version wording; dogfood pin title '[E8(#5040 执行器):验收 —— showcase 端点回迁
/apps/showcase/…+ RED-first e2e + 真实 boot 探针 + 升级文档安全复核 #5112] GET /meta/api returns both restored declarations' while the showcase declares three - api-backend.packaged-action-disabled-dispatch · observation · POST /auth/admin/create-user answered membershipCreated:false, yet the user operated in the Default Organization (stamped organization_id on writes) — no clause affected
- api-backend.rest-crud-config-contract · knownGaps[1] + automated.ref · 'the MOUNT half has no permanent pin' is stale: packages/rest/src/rest-config-mount-table.pin.test.ts ([finding] The MOUNT half of every
RestServerConfigswitch is unpinned — the tests assert what a switch normalizes to, never that the route leaves the table #15544) pins every crud.operations.* radius as an exact route-set diff (24/24 ✓ at 5b14b7f) — add it to automated.ref - api-backend.rest-batch-config-contract · knownGaps[1] + automated.ref · same: the mount pin covers batch.enableBatchEndpoint, operations.*Many and the protocol-member conjunct (§2) — add it to automated.ref
- api-backend.rest-metadata-config-contract · acceptance[4]/step 9 · enableCache carve-outs are now SEVEN: a ?member= (memberScoped, [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877) read also bypasses getMetaItemCached (packages/rest/src/meta-item-read-routes.ts:672-673)
- api-backend.rest-metadata-config-contract · personas[1] · still says 'cannot read every field of showcase_account'; rev 4 moved the fixture to showcase_project/client_liaison
- api-backend.meta-read-doors-v18 · step 5 / acceptance[4] · step names showcase_task, but stock FLS only exists on showcase_project (showcase_client_liaison); and the contract is that the hidden field is ABSENT from sortability (pin: 'sortability never offers the unreadable field'), not 'marked unsortable'
- api-backend.error-sentences-and-unique-violation-wire-code · fixtures/step 1/acceptance[1] · a package hook on sys_sharing_rule is refused at compile ("Hook … references object 'sys_sharing_rule' which is not defined in objects"); the /data door answers a hook refusal 400 with NO code by pinned design ('the refusal envelope carries no
codewhen the hook declared none'); VALIDATION_ERROR applies only to the re-dressed hand-built rows (REFUSED_400 in the ledger pin) - api-backend.error-sentences-and-unique-violation-wire-code · acceptance[2] · 'details naming the field' over-claims: details carry
fieldonly when the driver names a single column; an engine write against a tenancy-scoped composite unique index answers details {developerMessage, object} (dispatcher-unique-violation-wire-code.test.ts:195-211) - api-backend.error-sentences-and-unique-violation-wire-code · fixtures · 'pnpm dev -- --fresh -p PORT' boot spelling vs RUNNER canonical boot; also a scratch object needs sharingModel or the author-time rule security-owd-unset refuses compile
- api-backend.action-activation-door-contract · acceptance[4] · the metadata-plane-degraded 503 arm (actions.ts:212-221) is named in the clause but is neither stock-reachable nor covered by any pin in automated.ref
- api-backend.rest-crud-config-contract · acceptance[2] · the harness can only show 'no handler'; the switched-off op's live wire answer is unobservable ([finding] No shipped boot path authors
RestServerConfigat all —os servefixes it and the dev plugin passes none, so every livecrud/metadata/batchkey is embedder-only #15543) and the composed server's hono dispatcher catch-all also serves /data routes — the clause's dispatcher-vs-hono-route trap is not discharged by a RestServer-only table
Fixture gaps:
- api-backend.aggregate-contract-matrix · stock showcase_account has no row with null annual_revenue (needed for count-'*' vs count-field) and no Sat/Sun or year-boundary ISO-week pair — 3 rows seeded at run time
- api-backend.filter-comparand-conformance · showcase_field_zoo has only 2 rows — answer sets are discriminating but thin
- api-backend.query-contract-matrix · acceptance[4] needs a contributor-bound member owning an invoice WITH a contact; no seeded user holds the contributor position and no seeded invoice carries a contact (all 12 contact:null). Provisioned at runtime: admin create-user + sys_user_position{position:'contributor'} (search:qa-contributor-bound-member) + member-created invoice with owner=self, account=Contoso, contact=Carl Fox
- api-backend.packaged-action-disabled-dispatch · acceptance[3] both-persona variant used the same runtime-provisioned contributor member
- api-backend.error-sentences-and-unique-violation-wire-code · no package-authorable hook can reach a hand-built nested-envelope write row (sharing rules / approvals / suggested bindings) — the VALIDATION_ERROR half is pin-only
- api-backend.action-activation-door-contract · ambiguity / 501 / 503 arms still pin-only (no colliding scratch package authored this run)
Incidental product finding (outside any clause)
- Seen while driving
api-backend.error-sentences-and-unique-violation-wire-codeacceptance[3] (forced storage fault), independently re-verified on a healthy stock boot:POST /api/v1/storage/upload/completecommits a file record (status committed) for bytes that were never stored; the signed download then answers 500. Extracted as its own defect card at close-out (see the close-out comment).
Activity
objectstack-fleet commentedon Oct 19, 2026
Close-out (extraction) for this run record.
Still open on this record, handed to the dispatch session for the wave anchor (runners file no anchor card this wave):
api-backend.error-sentences-and-unique-violation-wire-codeacceptance[2] fail — disposition assertion-defect (product matches its pinned contract; the clause over-claimsdetails.field), plus acceptance[1] blocked(fixture). Test-fix belongs on the anchor.api-backend.aggregate-contract-matrixpartial — negative[2] (non-UTC pushdown vs in-memory labels) not-run.No P0 item failed in this area; no authentication/authorization finding.
Generated by Claude Code
[-]QA run · area:api-backend (22/27) · 5b14b7f3 · 2026-10-19 · 20 PASS / 1 PARTIAL / 1 FAIL / 0 BLOCKED / 5 NOT-RUN[/-][+]QA run · api-backend (22/27) · 5b14b7f3 · 2026-10-19 · 20 PASS / 1 PARTIAL / 1 FAIL / 0 BLOCKED / 5 NOT-RUN[/+]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)
Parked-on: #24579 · api-backend.error-sentences-and-unique-violation-wire-code · acceptance[2] (re-point, wave anchor §1)
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Superseded-by: #24595
Round 2 at ba4afcc re-judged this record's A-class unjudged clauses (
api-backend.aggregate-contract-matrixnegative[2] — FAIL (in-memory date bucketing shifts aField.datea day earlier under a negative-offset zone; rule-7 verified)) and carried every other verdict forward to #24595 (Carried-forward:lines there, valid for 5b14b7f). B-class gaps stay parked on #24579.Generated by Claude Code