From 36b5c9e68c275b05fa69df72bc67c459743ebcc3 Mon Sep 17 00:00:00 2001 From: "objectstack-fleet[bot]" <332303061+objectstack-fleet[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 05:43:36 +0000 Subject: [PATCH 1/3] docs(skills): objectstack-api teaches the /meta/:type list's ?names= filter and its closed query set The Metadata API section states what the list door serves today: `?names=a,b,c` (one comma-separated value) narrows the list to the named items the caller may see, an unknown name is absent, an empty value is refused 400, the `package` list refuses the key, and any key outside the closed set (`package`, `preview`, `object`, `include`, `id`, `names`, `locale`) is refused 400 VALIDATION_ERROR instead of ignored. Growth is paid in the same file: the `/data` prefix sentence and the aggregate spelling restated the route block and the derived-operations table, the Datasources section folded into the Skill Boundaries table, and the RBAC aside dropped its restated symbol. Eval case 7 asks for one by-names read. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qiby5ktwhsqg839UYDQs --- .changeset/30710-meta-list-names-filter.md | 8 +++++ skills/objectstack-api/SKILL.md | 31 ++++++++----------- .../evals/endpoints-auth-routes.json | 10 ++++++ 3 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 .changeset/30710-meta-list-names-filter.md diff --git a/.changeset/30710-meta-list-names-filter.md b/.changeset/30710-meta-list-names-filter.md new file mode 100644 index 00000000000..11e3708926e --- /dev/null +++ b/.changeset/30710-meta-list-names-filter.md @@ -0,0 +1,8 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): objectstack-api's Metadata API section teaches the `/meta/:type` list door's `?names=a,b,c` filter (one comma-separated value; an unknown name absent, an empty value refused `400`, the `package` list refuses the key) and its closed query set (`package`, `preview`, `object`, `include`, `id`, `names`, `locale`; any other key `400 VALIDATION_ERROR`) + +Clause-②: no +Metadata-protocol: none diff --git a/skills/objectstack-api/SKILL.md b/skills/objectstack-api/SKILL.md index ee551eb0d46..52f7761422f 100644 --- a/skills/objectstack-api/SKILL.md +++ b/skills/objectstack-api/SKILL.md @@ -25,7 +25,8 @@ metadata: | Need | Use instead | |:-----|:------------| -| Model objects, fields, permissions, or datasources | **objectstack-data** | +| Model objects, fields, permissions, or datasources (`rules/datasources.md`) | **objectstack-data** | +| Pick a driver package | **objectstack-platform** → Driver Selection Guide | | Filter, sort, paginate, or aggregate a request | **objectstack-query** | | Register a kernel service, or read one from a plugin | **objectstack-platform** | | CEL in a route guard or auth predicate | **objectstack-formula** | @@ -50,10 +51,8 @@ POST /api/v1/data/{object}/batch # Per-object batch operations POST /api/v1/batch # Cross-object atomic batch ``` -Data CRUD lives under the `/data` prefix. There is no `/bulk` route and no -`GET .../aggregate` route — batch writes go through the `batch` endpoints, and -aggregation goes through `POST /api/v1/data/{object}/query` with -`groupBy`/`aggregations` in the body. +There is no `/bulk` route and no `GET .../aggregate` route — batch writes go +through the `batch` endpoints, aggregation through the `query` one. > **Key rule:** If your object defines `apiMethods`, only those operations (and > what derives from them) are exposed; `[]` means deny-all. The authorable @@ -69,8 +68,13 @@ GET /api/v1/meta/:type # List metadata items of a type (object, view, GET /api/v1/meta/:type/:name # Read a single metadata item ``` -The read-side query params (`?preview=draft`, `?package=`, `?include=content`) -are **client** contracts — nothing in a stack declares them. +The list reads a closed query set — `package`, `preview` (`draft`), `object`, +`include` (`content`), `id`, `names`, `locale` — and refuses any other key with +`400 VALIDATION_ERROR` instead of ignoring it. `?names=a,b,c` (one +comma-separated value) narrows the list to the named items the caller may see: +an unknown name is simply absent, an empty value is refused `400`, and the +`package` list (keyed by id) refuses the key. These are **client** contracts — +nothing in a stack declares them. ### Public (anonymous) Form Endpoints @@ -315,8 +319,8 @@ Realtime contracts are pointer-style — read the spec source for exact shapes: ## Authentication & Authorization -RBAC (`definePermissionSet`) and RLS are objectstack-data's surface, not this -one; this section is the endpoint's own auth keys. +RBAC and RLS are objectstack-data's surface; this section is the endpoint's +own auth keys. ### Auth Configuration @@ -350,15 +354,6 @@ session and token response shapes before wiring an auth flow. --- -## Datasources - -`defineDatasource`, `schemaMode`, the `external` write gate and -`credentialsRef` are objectstack-data → `rules/datasources.md`; driver packages -and the Turso Cloud/EE caveat are objectstack-platform → **Driver Selection -Guide**; the `driver` id vocabulary is `data/driver/config-registry.zod.ts`. - ---- - ## Error Envelopes & the Code Ledger (ADR-0112) `error.code` is **two-tier**: the closed `StandardErrorCode` catalog plus the diff --git a/skills/objectstack-api/evals/endpoints-auth-routes.json b/skills/objectstack-api/evals/endpoints-auth-routes.json index 9871640ba78..6f7bd8803dc 100644 --- a/skills/objectstack-api/evals/endpoints-auth-routes.json +++ b/skills/objectstack-api/evals/endpoints-auth-routes.json @@ -60,6 +60,16 @@ "must_contain": ["applyOn", "'create'", "sourceField", "targetField"], "must_not_contain": ["writeMode: 'insert'"] } + }, + { + "id": 7, + "prompt": "Our integration boots by reading the `account`, `contact` and `lead` object definitions from the runtime's metadata API. Today it sends three requests; make it one.", + "expected_output": "One `GET /api/v1/meta/object?names=account,contact,lead` — the list door's `?names=` filter takes one comma-separated value and answers only the named items the caller may see; a name that matches no item is simply absent (no 404), and an empty value is refused `400`. Does not spell the filter `?name=`, `?ids=` or `?filter=`: the list reads a closed query set (`package`, `preview`, `object`, `include`, `id`, `names`, `locale`) and refuses any other key with `400 VALIDATION_ERROR` instead of ignoring it, so a misspelt filter never silently answers the whole list.", + "files": [], + "assertions": { + "must_contain": ["?names=", "/api/v1/meta/object", "VALIDATION_ERROR"], + "must_not_contain": ["?name=", "?ids=", "?filter="] + } } ] } From 1403468afe798e3c63744bc743e7f4d2f32b287c Mon Sep 17 00:00:00 2001 From: "objectstack-fleet[bot]" <332303061+objectstack-fleet[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 06:32:29 +0000 Subject: [PATCH 2/3] docs(skills): name both include values in the closed set; give eval case 7 its refused-filter hook Contract review should-fixes: the closed-set sentence now lists `include` (`content`, `grants`), since the position list reads `?include=grants`; the growth is paid by dropping the dead `enable.trash` 16.x parenthetical. Eval case 7's prompt now carries the `?name=` attempt that came back 400, so its `VALIDATION_ERROR` assertion is reachable from the prompt. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qiby5ktwhsqg839UYDQs --- skills/objectstack-api/SKILL.md | 6 +++--- skills/objectstack-api/evals/endpoints-auth-routes.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/skills/objectstack-api/SKILL.md b/skills/objectstack-api/SKILL.md index 52f7761422f..2012bb558ce 100644 --- a/skills/objectstack-api/SKILL.md +++ b/skills/objectstack-api/SKILL.md @@ -69,7 +69,7 @@ GET /api/v1/meta/:type/:name # Read a single metadata item ``` The list reads a closed query set — `package`, `preview` (`draft`), `object`, -`include` (`content`), `id`, `names`, `locale` — and refuses any other key with +`include` (`content`, `grants`), `id`, `names`, `locale` — and refuses any other key with `400 VALIDATION_ERROR` instead of ignoring it. `?names=a,b,c` (one comma-separated value) narrows the list to the named items the caller may see: an unknown name is simply absent, an empty value is refused `400`, and the @@ -409,8 +409,8 @@ keys mean the run produced none — never invent an empty. `CONCURRENT_UPDATE`**; the quoted-empty entity-tag (`""`) is refused **400 `VALIDATION_FAILED`**, not treated as omitted. 2. **Assuming `DELETE` is recoverable.** ObjectStack `DELETE` is a hard - delete — there is no recycle bin (the dead `enable.trash` flag was removed - in 16.x). For recoverability, use per-field `trackHistory` (audit + delete — there is no recycle bin. + For recoverability, use per-field `trackHistory` (audit trail) or a `lifecycle` archive policy instead of custom soft-delete logic. --- diff --git a/skills/objectstack-api/evals/endpoints-auth-routes.json b/skills/objectstack-api/evals/endpoints-auth-routes.json index 6f7bd8803dc..d25f5ced5c4 100644 --- a/skills/objectstack-api/evals/endpoints-auth-routes.json +++ b/skills/objectstack-api/evals/endpoints-auth-routes.json @@ -63,7 +63,7 @@ }, { "id": 7, - "prompt": "Our integration boots by reading the `account`, `contact` and `lead` object definitions from the runtime's metadata API. Today it sends three requests; make it one.", + "prompt": "Our integration boots by reading the `account`, `contact` and `lead` object definitions from the runtime's metadata API. Today it sends three requests; our `?name=account,contact,lead` attempt came back 400. Make it one request.", "expected_output": "One `GET /api/v1/meta/object?names=account,contact,lead` — the list door's `?names=` filter takes one comma-separated value and answers only the named items the caller may see; a name that matches no item is simply absent (no 404), and an empty value is refused `400`. Does not spell the filter `?name=`, `?ids=` or `?filter=`: the list reads a closed query set (`package`, `preview`, `object`, `include`, `id`, `names`, `locale`) and refuses any other key with `400 VALIDATION_ERROR` instead of ignoring it, so a misspelt filter never silently answers the whole list.", "files": [], "assertions": { From fa02fd0ff4c5da2adb6c41794f8b634aac0edc72 Mon Sep 17 00:00:00 2001 From: "objectstack-fleet[bot]" <332303061+objectstack-fleet[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 06:48:56 +0000 Subject: [PATCH 3/3] docs(skills): eval case 7 no longer forbids the ?name= spelling its own prompt quotes A correct answer may quote the refused `?name=` attempt the prompt names, so `must_not_contain` keeps only the spellings a wrong answer writes (`?ids=`, `?filter=`). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qiby5ktwhsqg839UYDQs --- skills/objectstack-api/evals/endpoints-auth-routes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/objectstack-api/evals/endpoints-auth-routes.json b/skills/objectstack-api/evals/endpoints-auth-routes.json index d25f5ced5c4..b49417304ed 100644 --- a/skills/objectstack-api/evals/endpoints-auth-routes.json +++ b/skills/objectstack-api/evals/endpoints-auth-routes.json @@ -68,7 +68,7 @@ "files": [], "assertions": { "must_contain": ["?names=", "/api/v1/meta/object", "VALIDATION_ERROR"], - "must_not_contain": ["?name=", "?ids=", "?filter="] + "must_not_contain": ["?ids=", "?filter="] } } ]