Skip to content

Commit 9598f0e

Browse files
os-elonclaude
andauthored
fix(qa,spec,client): address the metadata write door by its canonical singular type segment (#13011)
* 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw * 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 907eb04 commit 9598f0e

4 files changed

Lines changed: 7 additions & 7 deletions

File tree

docs/qa/platform-checklist/areas/attachments-storage.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,21 @@
2424
},
2525
{
2626
"step": 2,
27-
"call": "PUT /api/v1/meta/objects/qa_vault?package=com.objectstack.qa.attachments",
27+
"call": "PUT /api/v1/meta/object/qa_vault?package=com.objectstack.qa.attachments",
2828
"body": { "name": "qa_vault", "label": "QA Vault", "sharingModel": "private", "enable": { "files": true }, "fields": { "name": { "type": "text", "label": "Name", "required": true } } },
2929
"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.",
3030
"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)"
3131
},
3232
{
3333
"step": 3,
34-
"call": "PUT /api/v1/meta/objects/qa_shared?package=com.objectstack.qa.attachments",
34+
"call": "PUT /api/v1/meta/object/qa_shared?package=com.objectstack.qa.attachments",
3535
"body": { "name": "qa_shared", "label": "QA Shared", "sharingModel": "public_read", "enable": { "files": true }, "fields": { "name": { "type": "text", "label": "Name", "required": true } } },
3636
"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.",
3737
"source": "same citations as step 2"
3838
},
3939
{
4040
"step": 4,
41-
"call": "PUT /api/v1/meta/objects/qa_nofiles?package=com.objectstack.qa.attachments",
41+
"call": "PUT /api/v1/meta/object/qa_nofiles?package=com.objectstack.qa.attachments",
4242
"body": { "name": "qa_nofiles", "label": "QA No Files", "sharingModel": "public_read", "fields": { "name": { "type": "text", "label": "Name", "required": true } } },
4343
"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.",
4444
"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 @@
8181
},
8282
{
8383
"step": 2,
84-
"call": "PUT /api/v1/meta/objects/qa_media?package=com.objectstack.qa.media",
84+
"call": "PUT /api/v1/meta/object/qa_media?package=com.objectstack.qa.media",
8585
"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"] } } },
8686
"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.",
8787
"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)"

packages/client/src/client.hono.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe('ObjectStackClient (with Hono Server)', () => {
167167
expect(endpoints?.metadata).toContain('/api/v1/meta');
168168

169169
// Enforced, not just declared — the pairing #4018 exists to hold.
170-
expect((await fetch(`${baseUrl}/api/v1/meta/objects`)).status).not.toBe(404);
170+
expect((await fetch(`${baseUrl}/api/v1/meta/object`)).status).not.toBe(404);
171171
});
172172

173173
it('should create and retrieve data via hono', async () => {

packages/spec/liveness/object.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
"status": "live",
102102
"verifiedAt": "2026-08-28",
103103
"evidence": "packages/runtime/src/app-plugin.ts#collectBundleActions (walks `bundle.objects[*].actions` and `bundle.manifest.objects[*].actions`, stamping each entry with its parent object name — the list the action body-runner binding and the /meta surface are both built from)",
104-
"note": "served on /meta/objects/:name. 2026-08-28: RE-ANCHORED (#13003) and REPOINTED — the citation was `app-plugin.ts:929`, in range of this 1840-line file but landing in the JOB scheduling block (`svc.schedule(...)`), which reads nothing of this key. The file mentions `actions` on plenty of lines, so the #11457 key-mention check anchors and passes; only a hand read separates the per-object action walk from the unrelated `bundle.actions` and job paths. Re-closed by hand against c459da6bc."
104+
"note": "served on /meta/object/:name. 2026-08-28: RE-ANCHORED (#13003) and REPOINTED — the citation was `app-plugin.ts:929`, in range of this 1840-line file but landing in the JOB scheduling block (`svc.schedule(...)`), which reads nothing of this key. The file mentions `actions` on plenty of lines, so the #11457 key-mention check anchors and passes; only a hand read separates the per-object action walk from the unrelated `bundle.actions` and job paths. Re-closed by hand against c459da6bc."
105105
},
106106
"managedBy": {
107107
"status": "live",

packages/spec/src/api/http-cache.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export type ETagParsed = z.infer<typeof ETagSchema>;
105105
* Request with cache validation headers
106106
*
107107
* @example
108-
* // GET /api/v1/metadata/objects/account
108+
* // GET /api/v1/metadata/object/account
109109
* // Headers:
110110
* // If-None-Match: "686897696a7c876b7e"
111111
* // If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

0 commit comments

Comments
 (0)