Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/qa/platform-checklist/areas/access-security.json
Original file line number Diff line number Diff line change
Expand Up @@ -3799,10 +3799,10 @@
},
{
"id": "access-security.share-link-combined-gates",
"title": "Share links with two gates combined: signed_in + password answers an anonymous caller 401 SIGN_IN_REQUIRED whatever password is sent, email + password answers a uniform 404 INVALID_OR_EXPIRED, and /messages answers exactly like /resolve",
"title": "Share links with two gates combined: signed_in + password answers an anonymous caller 401 SIGN_IN_REQUIRED whatever password is sent, email + password answers a uniform 404 INVALID_OR_EXPIRED, and /messages opens exactly where /resolve opens",
"since": "v18",
"status": "active",
"revision": 1,
"revision": 2,
"priority": "P2",
"surface": "api",
"personas": [
Expand Down Expand Up @@ -3841,9 +3841,9 @@
"evidence": "the bodies"
},
{
"clause": "/messages answers identically to /resolve on every call",
"clause": "/messages is open iff /resolve is open on the same call — open meaning the call passes the link's token, audience and password gates: where /resolve refuses at a gate (401 / 404 / 410), /messages answers its one refusal, 404 NOT_FOUND; where /resolve passes, /messages answers 200 for an `ai_conversations` link and 400 UNSUPPORTED for any other object (the stock showcase has no ai_conversations object, so 400 UNSUPPORTED is the open answer there). The refusal codes differ by pinned design, so only the open/refused verdict is compared (a malformed password header is refused 400 VALIDATION_FAILED by both, before either reads the link)",
"oracle": "api",
"verify": "status+code pairs",
"verify": "pair each /resolve answer with the /messages answer to the same call: refused → 404 NOT_FOUND, passed → 200 or 400 UNSUPPORTED; a /messages 200 or 400 UNSUPPORTED where /resolve refused is a fail",
"evidence": "the paired lines"
}
],
Expand All @@ -3865,6 +3865,12 @@
"date": "2026-10-16",
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
"ref": "#23879"
},
{
"revision": 2,
"date": "2026-10-19",
"change": "acceptance[3] re-pointed to open/refused parity (QA run #24559 acceptance[3] fail, disposition assertion defect; parked on #24579 §1; QA run #24558 scored the same clause on that corrected intent). #24559 records the divergence as \"the pinned design (share-link-envelope.conformance.test.ts: messages for an unresolvable token → 404 NOT_FOUND; for a non-ai_conversations link → 400 UNSUPPORTED)\" and re-points the clause to \"/messages opens iff /resolve opens\". packages/plugins/plugin-sharing/src/share-link-routes.ts#registerShareLinkRoutes hands /messages the same audience inputs and answers every refusal 404 NOT_FOUND, and packages/runtime/src/domains/share-links-messages-audience.test.ts holds both mounts to one open/refused verdict per audience. The clause asked for identical status+code pairs, which the design contradicts on every refused call. The title follows; re-derived from main 5a871e4e41, where every source cited here is byte-identical to the run subject 5b14b7f3",
"ref": "#24622"
}
]
}
Expand Down
12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/areas/api-backend.json
Original file line number Diff line number Diff line change
Expand Up @@ -2823,7 +2823,7 @@
"title": "Error answers made honest on v18: a hook refusal on a hand-built write route answers 400 with the hook's own sentence, a crash answers a generic 500 that withholds the caught message, and a unique-constraint refusal is 409 UNIQUE_VIOLATION with details at the action door, MCP and sandboxed bodies too",
"since": "v18",
"status": "active",
"revision": 1,
"revision": 2,
"priority": "P2",
"surface": "api",
"personas": [
Expand Down Expand Up @@ -2856,9 +2856,9 @@
"evidence": "the body"
},
{
"clause": "the duplicate insert through the action door → 409 UNIQUE_VIOLATION with details naming the field",
"clause": "the duplicate insert through the action door → 409 UNIQUE_VIOLATION with `details` in the composite shape: exactly `object` (the object's name) and `developerMessage`, no `field` — a field's `unique: true` is a tenancy-scoped composite index, so the driver names no single column (`field` is added only when the refusal states one)",
"oracle": "api",
"verify": "status + code + details",
"verify": "status + error.code + the key set of error.details (exactly developerMessage and object) + details.object",
"evidence": "the body"
},
{
Expand All @@ -2885,6 +2885,12 @@
"date": "2026-10-16",
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
"ref": "#23879"
},
{
"revision": 2,
"date": "2026-10-19",
"change": "acceptance[2] asserts the composite details shape (QA run #24541 acceptance[2] fail, disposition assertion-defect; parked on #24579 §1). #24541: \"details carry field only when the driver names a single column; a tenancy-scoped composite unique names none\" — the pin packages/runtime/src/dispatcher-unique-violation-wire-code.test.ts asserts the details keys are exactly developerMessage and object for an engine write, adding field only when DuplicateRecordError states a column, and driver-sql builds a field's unique: true as the (organization, field) composite. The clause asked for details naming the field, which no stock unique field produces. acceptance[1] is unchanged: QA run #24558's fail there is the product defect #24561; re-derived from main 5a871e4e41, where every source cited here is byte-identical to the run subject 5b14b7f3",
"ref": "#24622"
}
]
}
Expand Down
24 changes: 15 additions & 9 deletions docs/qa/platform-checklist/areas/attachments-storage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1077,10 +1077,10 @@
},
{
"id": "attachments-storage.chunked-upload-integrity",
"title": "Chunked upload integrity on v18: complete with a missing chunk is 409 with details.missingChunks and the session stays in_progress, a re-sent chunk counts once, parallel chunks all record, and an org switch mid-upload answers 409 on complete / chunk / progress",
"title": "Chunked upload integrity on v18: complete with a missing chunk is 409 with details.missingChunks and the session stays in_progress, a re-sent chunk counts once, parallel chunks all record, and an org switch mid-upload answers 409 on chunk and complete, and on progress only once the session's expiry stamp is due",
"since": "v18",
"status": "active",
"revision": 1,
"revision": 2,
"priority": "P2",
"surface": "api",
"personas": [
Expand All @@ -1093,10 +1093,10 @@
]
},
"steps": [
"start a chunked upload of 3 chunks; PUT chunks 1 and 3; POST complete",
"PUT chunk 1 again; GET progress",
"PUT chunk 2; POST complete",
"start another, switch active organization (POST /api/v1/auth/organization/set-active) and PUT a chunk"
"start a chunked upload of 3 chunks, numbered from 0 as the API numbers them (0, 1, 2); PUT chunks 0 and 2; POST complete",
"PUT chunk 0 again; GET progress",
"PUT chunk 1; POST complete",
"start another, switch active organization (POST /api/v1/auth/organization/set-active), then PUT a chunk, POST complete and GET progress"
],
"acceptance": [
{
Expand All @@ -1106,7 +1106,7 @@
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
},
{
"clause": "complete with a missing chunk → 409 RESOURCE_CONFLICT with details.missingChunks [2]; progress still in_progress",
"clause": "complete with a missing chunk → 409 RESOURCE_CONFLICT with details.missingChunks [1] (the chunk not sent, numbered from 0); progress still in_progress",
"oracle": "api",
"verify": "status + details + progress",
"evidence": "answers"
Expand All @@ -1118,9 +1118,9 @@
"evidence": "the progress body"
},
{
"clause": "after the org switch chunk PUT / complete / progress → 409",
"clause": "after the org switch: chunk PUT → 409 and complete → 409 (RESOURCE_CONFLICT); progress → 200 unless the session's expiry stamp is due (its own expires_at has passed), and 409 then — a progress read writes nothing else, so it has no reach to miss",
"oracle": "api",
"verify": "status per call",
"verify": "status per call; for progress, also the session's expiresAt against the clock of the call",
"evidence": "three lines"
}
],
Expand All @@ -1138,6 +1138,12 @@
"date": "2026-10-16",
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
"ref": "#23879"
},
{
"revision": 2,
"date": "2026-10-19",
"change": "acceptance[3] re-pointed and the chunk numbering made the API's (QA run #24560 acceptance[3] fail, disposition stale spec; parked on #24579 §1). #24560: \"the progress door (#22218, storage-routes.ts) deliberately keeps answering the uploader from any organization when there is nothing to stamp\" — packages/services/service-storage/src/storage-routes.ts#registerStorageRoutes asks the starting-organization gate on progress only when the expiry stamp is due, so after an org switch progress answers 200 unless the session's expires_at has passed, and 409 then; chunk PUT and complete answer 409 as before. The steps numbered chunks from 1 while the API numbers them from 0 (#24560 and #24558: the server answered missingChunks [1] for the step's \"chunk 2\"), so the steps now number from 0, acceptance[1] expects [1], and the org-switch step names all three calls. The title follows; re-derived from main 5a871e4e41, where every source cited here is byte-identical to the run subject 5b14b7f3",
"ref": "#24622"
}
]
}
Expand Down
16 changes: 11 additions & 5 deletions docs/qa/platform-checklist/areas/automation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1987,10 +1987,10 @@
},
{
"id": "automation.flow-authoring-refusals-v18",
"title": "Flow authoring contract on v18, refused at `os validate` AND at PUT /meta/flow: single-brace {…} value tokens (CEL only; text slots use {{ }}), $-prefixed bound variable names, builtin node config keys/values the executor does not declare, dangling or repeated edges, `record`/`user.id` roots a flow cannot bind, retryPolicy.maxRetry, and an anonymous endpoint targeting a runAs:\"system\" flow",
"title": "Flow authoring contract on v18, refused at `os validate` — and at PUT /meta/flow for every flow-body arm but the CEL root scope, which only `os validate` judges: single-brace {…} value tokens (CEL only; text slots use {{ }}), $-prefixed bound variable names, builtin node config keys/values the executor does not declare, dangling or repeated edges, `record`/`user.id` roots a flow cannot bind, retryPolicy.maxRetry, and an anonymous endpoint targeting a runAs:\"system\" flow",
"since": "v18",
"status": "active",
"revision": 1,
"revision": 2,
"priority": "P1",
"surface": "cli",
"personas": [
Expand All @@ -2006,7 +2006,7 @@
},
"steps": [
"for each arm run `os validate` in the scratch app and record exit code + rule id + located path: fields:{owner:\"{record.owner}\"}; notify title \"{record.name}\"; screen field named \"$x\"; create_record outputVariable:42; notify bogusKey; an edge to a removed node; a REST-triggered flow reading record.x; `user.id` in CEL; job retryPolicy:{maxRetry:3}; an authRequired:false flow endpoint targeting a runAs:\"system\" flow",
"PUT the same flow bodies to a fresh showcase boot at /api/v1/meta/flow/qa_ARM (scratch names)",
"PUT the flow-body arms acceptance[2] lists to a boot of the scratch app at /api/v1/meta/flow/qa_ARM (scratch names); the endpoint arm PUTs the `runAs: \"system\"` body of the flow its code-declared endpoint names",
"controls: the CEL envelope {dialect:\"cel\",source:\"record.owner\"}, a notify title \"Case {{ record.name }}\", and a query placeholder {current_user_id} in a CRUD filter operand all PASS"
],
"acceptance": [
Expand All @@ -2023,9 +2023,9 @@
"evidence": "one line per arm"
},
{
"clause": "the same bodies at PUT /meta/flow → 422 INVALID_METADATA and nothing stored",
"clause": "at PUT /api/v1/meta/flow the door refuses what it judges — 422 INVALID_METADATA with a located issue, and nothing stored — for the single-brace value and text tokens, the $-prefixed bound name, the undeclared builtin node config key and value, the dangling or repeated edge, an undeclared `retry` key under a `try_catch` node (`retry: {maxRetry: 3}`, the flow-shaped twin of the job arm) and a `runAs: \"system\"` flow that a code-declared `authRequired: false` endpoint names (issue at `runAs`). The two CEL root-scope arms — `record.x` in a flow no entrance hands a record, and `user.id` — answer 200 and are stored: this door does not judge root scope, by design, and `os validate` (acceptance[1]) refuses them. The job and the endpoint themselves are code-only types (PUT /meta/job and /meta/api answer 403 NOT_CREATABLE), so they are not PUT arms",
"oracle": "api",
"verify": "status + read-back 404",
"verify": "status + code per arm, then GET the same name: a 422 arm reads back 404, a root-scope arm 200",
"evidence": "per-arm lines"
},
{
Expand Down Expand Up @@ -2055,6 +2055,12 @@
"date": "2026-10-16",
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
"ref": "#23879"
},
{
"revision": 2,
"date": "2026-10-19",
"change": "acceptance[2] narrowed to what the PUT door refuses (QA run #24545 acceptance[2] fail, disposition stale-spec; parked on #24579 §1; QA run #24558 flagged the same clause's \"PUT 422\" as too broad). #24545: the runtime PUT gate \"deliberately stands down on the CEL root-scope judgment (whole-stack, owned by os validate — which still refuses both arms, so negative clauses hold)\", by rulings 5791822697 (item 3) and 6104584601 (letter B) as packages/lint/src/flow-cel-root-scope.ts#perWriteSnapshotEntrance records, and the item's own pin validate-expressions.flow-cel-root.test.ts asserts the stand-down. So the record.x and user.id arms now expect 200 and a stored row at PUT. The job arm and the endpoint arm are not PUT /meta/flow bodies — job and api are code-only types, 403 NOT_CREATABLE (#24558) — so the clause names the flow-side twins the door does refuse: a try_catch node's retry.maxRetry, and the runAs: \"system\" flow a code-declared anonymous endpoint names (422 at runAs, #24545). steps[1] and the title follow; re-derived from main 5a871e4e41, where every source cited here is byte-identical to the run subject 5b14b7f3",
"ref": "#24622"
}
]
},
Expand Down
Loading
Loading