From 3fc0044946e49f34f592ca1521fc65a67aae2524 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 14:05:07 +0000 Subject: [PATCH 1/2] fix(qa,spec,client): address the metadata write door by its canonical singular type segment The QA platform checklist told its own operator to send a plural item write (PUT /api/v1/meta/objects/:name) on four executable steps. Those answer 200 only because the boundary fold tolerates the spelling; the /meta type segment is always singular. Rewrite the four executable call strings plus two address-stating notes to the canonical spelling. Part of #11042 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- docs/qa/platform-checklist/areas/attachments-storage.json | 8 ++++---- packages/client/src/client.hono.test.ts | 2 +- packages/spec/liveness/object.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/qa/platform-checklist/areas/attachments-storage.json b/docs/qa/platform-checklist/areas/attachments-storage.json index aa4621d589..fd0dc8812b 100644 --- a/docs/qa/platform-checklist/areas/attachments-storage.json +++ b/docs/qa/platform-checklist/areas/attachments-storage.json @@ -24,21 +24,21 @@ }, { "step": 2, - "call": "PUT /api/v1/meta/objects/qa_vault?package=com.objectstack.qa.attachments", + "call": "PUT /api/v1/meta/object/qa_vault?package=com.objectstack.qa.attachments", "body": { "name": "qa_vault", "label": "QA Vault", "sharingModel": "private", "enable": { "files": true }, "fields": { "name": { "type": "text", "label": "Name", "required": true } } }, "expect": "2xx. This is THE object the area was missing: private OWD (owner-only) AND attachments-enabled, so a member who is not the owner genuinely cannot read the parent and the 403 deny side becomes provable.", "source": "`sharingModel` is a TOP-LEVEL object key, enum ['private','public_read','public_read_write','controlled_by_parent'] (packages/spec/src/data/object.zod.ts:1827; ADR-0090 D4 — legacy aliases removed). `enable.files` is the #2727 attachments opt-in, default false (object.zod.ts:281). `?package=` is read as `query.package` and threaded to saveMetaItem as packageId (packages/runtime/src/domains/meta.ts:262,319)" }, { "step": 3, - "call": "PUT /api/v1/meta/objects/qa_shared?package=com.objectstack.qa.attachments", + "call": "PUT /api/v1/meta/object/qa_shared?package=com.objectstack.qa.attachments", "body": { "name": "qa_shared", "label": "QA Shared", "sharingModel": "public_read", "enable": { "files": true }, "fields": { "name": { "type": "text", "label": "Name", "required": true } } }, "expect": "2xx. The entitled-member contrast: every member reads the parent, only the owner writes it — so a 403 here would be a real finding rather than a fixture artifact.", "source": "same citations as step 2" }, { "step": 4, - "call": "PUT /api/v1/meta/objects/qa_nofiles?package=com.objectstack.qa.attachments", + "call": "PUT /api/v1/meta/object/qa_nofiles?package=com.objectstack.qa.attachments", "body": { "name": "qa_nofiles", "label": "QA No Files", "sharingModel": "public_read", "fields": { "name": { "type": "text", "label": "Name", "required": true } } }, "expect": "2xx. `enable.files` is OMITTED deliberately (it defaults to false) — this is the FILES_DISABLED probe target, purpose-built rather than borrowed from showcase_account, so the negative keeps holding if showcase ever enables files on its own objects.", "source": "packages/spec/src/data/object.zod.ts:281 — enable.files defaults to false; 'Opt-in: true surfaces the panel and permits attachments to target this object; otherwise any write that makes an attachment target it is rejected (403 FILES_DISABLED) — a create and an update that re-points an existing attachment alike' (re-quoted post-#10733; the prior citation ended at 'creation is rejected', which #10170 made stale — see attach-requires-parent-edit clause 5)" @@ -81,7 +81,7 @@ }, { "step": 2, - "call": "PUT /api/v1/meta/objects/qa_media?package=com.objectstack.qa.media", + "call": "PUT /api/v1/meta/object/qa_media?package=com.objectstack.qa.media", "body": { "name": "qa_media", "label": "QA Media", "sharingModel": "public_read_write", "fields": { "name": { "type": "text", "label": "Name", "required": true }, "poster": { "type": "image", "label": "Poster", "accept": ["image/png", "image/jpeg"], "maxSize": 1048576 }, "doc": { "type": "file", "label": "Doc", "accept": [".pdf"] } } }, "expect": "2xx. `poster` is the MIME-entry + maxSize probe; `doc` (accept = ['.pdf'] ONLY, no maxSize) is the extension-entry probe whose dotless-filename hole is a documented boundary of the enforcement. `enable.files` is deliberately ABSENT: field-owned files ride file-reference-lifecycle.ts (activeFileFields keys on file-class field types), not the #2727 sys_attachment opt-in gate, so the object needs no attachments enablement.", "source": "accept/maxSize are declared FieldSchema keys since ADR-0104 D3 wave 2 (packages/spec/src/data/field.zod.ts:876-883 — 'Offered to the file picker AND enforced on write'); authoring-call shape identical to qa-scratch-authz step 2 (meta.ts:262,319 for ?package=); file-class field set is FILE_REFERENCE_TYPES = image/file/avatar/video/audio (packages/spec/src/data/field-value.zod.ts:146-148)" diff --git a/packages/client/src/client.hono.test.ts b/packages/client/src/client.hono.test.ts index f85abf7f89..5731a3e54e 100644 --- a/packages/client/src/client.hono.test.ts +++ b/packages/client/src/client.hono.test.ts @@ -167,7 +167,7 @@ describe('ObjectStackClient (with Hono Server)', () => { expect(endpoints?.metadata).toContain('/api/v1/meta'); // Enforced, not just declared — the pairing #4018 exists to hold. - expect((await fetch(`${baseUrl}/api/v1/meta/objects`)).status).not.toBe(404); + expect((await fetch(`${baseUrl}/api/v1/meta/object`)).status).not.toBe(404); }); it('should create and retrieve data via hono', async () => { diff --git a/packages/spec/liveness/object.json b/packages/spec/liveness/object.json index a071cfdbcf..921d6fd7e9 100644 --- a/packages/spec/liveness/object.json +++ b/packages/spec/liveness/object.json @@ -98,7 +98,7 @@ "actions": { "status": "live", "evidence": "packages/runtime/src/app-plugin.ts:929", - "note": "served on /meta/objects/:name." + "note": "served on /meta/object/:name." }, "managedBy": { "status": "live", From 7d6723e9d248ffacb00bc9a5877285f6944770c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 01:17:55 +0000 Subject: [PATCH 2/2] docs(spec): address the metadata item door by its singular type segment in the http-cache example The seventh site of the card's slice, held back on the first pass because packages/spec/src/api was inside a declared in-flight surface (#12522). That claim has since closed (merged PR #13019), and the re-run check finds the directory's only current claim (#13040) declared file-precisely at endpoint-publish-gate.ts with 'Nothing else' -- disjoint from this file, and its PR touches exactly that one file. No open PR touches http-cache.zod.ts. Only the plural type segment moves. The /metadata prefix is a separate axis the card does not scope, and the sibling example at :188 uses it too. Part of #11042 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- packages/spec/src/api/http-cache.zod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec/src/api/http-cache.zod.ts b/packages/spec/src/api/http-cache.zod.ts index 0afef1c93a..eff738b9df 100644 --- a/packages/spec/src/api/http-cache.zod.ts +++ b/packages/spec/src/api/http-cache.zod.ts @@ -105,7 +105,7 @@ export type ETagParsed = z.infer; * Request with cache validation headers * * @example - * // GET /api/v1/metadata/objects/account + * // GET /api/v1/metadata/object/account * // Headers: * // If-None-Match: "686897696a7c876b7e" * // If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT