howone 0.1.31 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. package/package.json +1 -1
  2. package/templates/vite/.howone/skills/howone/01-architect/01-app-generation.md +33 -27
  3. package/templates/vite/.howone/skills/howone/01-architect/02-manifest-codegen.md +2 -2
  4. package/templates/vite/.howone/skills/howone/02-entity-schema/01-schema-design.md +9 -29
  5. package/templates/vite/.howone/skills/howone/02-entity-schema/02-schema-operations.md +166 -235
  6. package/templates/vite/.howone/skills/howone/02-entity-schema/03-access-models.md +151 -0
  7. package/templates/vite/.howone/skills/howone/02-entity-schema/04-query-contracts.md +123 -0
  8. package/templates/vite/.howone/skills/howone/02-entity-schema/05-ai-persistence-patterns.md +84 -201
  9. package/templates/vite/.howone/skills/howone/03-ai-capabilities/01-ai-capability-architecture.md +37 -32
  10. package/templates/vite/.howone/skills/howone/03-ai-capabilities/02-workflow-contract-rules.md +5 -4
  11. package/templates/vite/.howone/skills/howone/03-ai-capabilities/04-workflow-operations.md +89 -204
  12. package/templates/vite/.howone/skills/howone/03-ai-capabilities/05-ai-feature-playbooks.md +8 -29
  13. package/templates/vite/.howone/skills/howone/04-app-sdk/01-client-setup.md +1 -2
  14. package/templates/vite/.howone/skills/howone/04-app-sdk/07-ai-action-calls.md +2 -2
  15. package/templates/vite/.howone/skills/howone/04-app-sdk/08-ai-manifest-handoff.md +6 -5
  16. package/templates/vite/.howone/skills/howone/04-app-sdk/09-extension-boundaries.md +1 -1
  17. package/templates/vite/.howone/skills/howone/{02-entity-schema/03-data-access-patterns.md → 04-app-sdk/11-entity-data-access-patterns.md} +4 -4
  18. package/templates/vite/.howone/skills/howone/{02-entity-schema/04-query-dsl-and-responses.md → 04-app-sdk/12-query-dsl-and-responses.md} +1 -1
  19. package/templates/vite/.howone/skills/howone/SKILL.md +112 -75
@@ -1,108 +1,108 @@
1
1
  # Schema Operations
2
2
 
3
- Use this reference when applying backend entity schema changes through HowOne runtime tools or
4
- `client.schema.*`. It answers: **how do I safely change the schema and keep app code in sync?**
5
-
6
- For schema design decisions, read `01-schema-design.md` first.
3
+ Use this reference when calling `backend-api-design` to change HowOne backend entity contracts.
4
+ This is a backend design reference, not an app SDK guide.
7
5
 
8
6
  ## Source Of Truth
9
7
 
10
8
  ```text
11
- agent proposal = draft
12
- backend preview/apply result = validated contract
13
- synced .howone/database files = local copy of backend version
14
- src/lib/sdk.ts = generated app binding from synced manifest
15
- frontend code = consumer of generated bindings
9
+ current backend schema = inspect result
10
+ backend-api-design apply result = validated contract version
11
+ sync_schema_artifacts output = local .howone/database manifest
12
+ src/lib/sdk.ts = later SDK binding, handled by SDK track
16
13
  ```
17
14
 
18
- Do not hand-write `.howone/database/*`. Sync artifacts from a concrete backend version.
19
-
20
- ## Preferred Patch Flow
21
-
22
- Use patch flow for most feature-level changes:
15
+ Do not handwrite `.howone/database/*`. Sync from a backend version after apply.
23
16
 
24
- 1. Inspect current `schema.getState()` and current entity definitions.
25
- 2. Design a full patch containing all related operations.
26
- 3. Preview the patch.
27
- 4. Review risk, diff, and current version.
28
- 5. Apply the exact same patch with `expectedVersionId`.
29
- 6. Sync schema artifacts from `next.versionId`.
30
- 7. Read `.howone/database/manifest.json`.
31
- 8. Regenerate/update `src/lib/sdk.ts` bindings.
32
- 9. Update frontend calls according to `access`.
33
- 10. Run typecheck/build/tests.
17
+ ## Normal Flow
34
18
 
35
- Do not preview one patch and apply a different operation set.
19
+ For generated app work, avoid fake dry-runs. Build one correct patch and apply it.
36
20
 
37
- ## SDK Schema Client
21
+ ```text
22
+ 1. backend-api-design { type: "get_current_schema" }
23
+ 2. Design one complete patch.operations[] for the feature.
24
+ 3. backend-api-design { type: "apply_schema_patch", expectedVersionId, reason, patch }
25
+ 4. sync_schema_artifacts with the returned versionId/currentVersionId.
26
+ 5. Read .howone/database/manifest.json.
27
+ 6. Stop backend design; SDK/UI work is a separate track.
28
+ ```
38
29
 
39
- ```ts
40
- const state = await client.schema.getState()
41
- const definitions = await client.schema.listDefinitions()
42
- const todo = await client.schema.getDefinition('Todo')
30
+ There is no schema dry-run step. If a change is destructive, narrowing, or broadens public access,
31
+ stop and align with the user before applying the final patch.
43
32
 
44
- const preview = await client.schema.previewPatch(patch, {
45
- expectedVersionId: state.currentVersionId,
46
- reason: 'Add priority field to Todo',
47
- })
33
+ ## Patch Shape
48
34
 
49
- if (preview.risk?.level === 'dangerous') {
50
- // stop and ask for confirmation
35
+ ```json
36
+ {
37
+ "type": "apply_schema_patch",
38
+ "expectedVersionId": "current-version-id-from-inspect",
39
+ "reason": "Add personal todo storage",
40
+ "patch": {
41
+ "operations": [
42
+ {
43
+ "type": "create_entity",
44
+ "entityName": "Todo",
45
+ "payload": {
46
+ "description": "Personal task item.",
47
+ "visibility": "private",
48
+ "type": "object",
49
+ "properties": {
50
+ "text": { "type": "string", "description": "Task text." },
51
+ "completed": { "type": "boolean", "default": false }
52
+ },
53
+ "required": ["text"],
54
+ "access": {
55
+ "authenticated": { "read": "own", "create": "own", "update": "own", "delete": "own" },
56
+ "public": { "read": "none", "create": "none", "update": "none" }
57
+ },
58
+ "indexes": [
59
+ {
60
+ "name": "completed_updated",
61
+ "scope": "owner",
62
+ "fields": ["completed", "updatedDate"],
63
+ "order": { "updatedDate": "desc" }
64
+ }
65
+ ]
66
+ }
67
+ }
68
+ ]
69
+ }
51
70
  }
52
-
53
- const applied = await client.schema.applyPatch(patch, {
54
- expectedVersionId: state.currentVersionId,
55
- reason: 'Add priority field to Todo',
56
- })
57
71
  ```
58
72
 
59
- Available schema methods:
73
+ Critical shape rules:
60
74
 
61
- | Method | Use |
62
- |---|---|
63
- | `listDefinitions()` | Get current entity definitions. |
64
- | `getDefinition(entityName)` | Inspect one entity. |
65
- | `upsertDefinitions(definition | definition[])` | Direct definition upsert; prefer patch for agent changes. |
66
- | `operate(operation)` | Single operation endpoint. |
67
- | `previewPatch(patch, options)` | Risk/diff preview without applying. |
68
- | `applyPatch(patch, options)` | Apply versioned patch. |
69
- | `getState()` | Current schema version pointer. |
70
- | `listVersions()` | Version history. |
71
- | `getVersion(versionId)` | Inspect version manifest. |
72
- | `restore(versionId, reason?)` | Restore definitions by creating a new restore version. |
75
+ - `create_entity.payload.properties` is the field map. Do not use `payload.definition`.
76
+ - `create_entity.payload.required` is the required field array. Do not use `requiredFields`.
77
+ - `update_entity.payload` is the patch itself. Do not wrap it in `payload.patch`.
78
+ - Entity and field names must match `^[a-zA-Z_][a-zA-Z0-9_]*$`.
79
+ - Backend-generated version IDs never go inside operation payloads.
73
80
 
74
81
  ## Operation Types
75
82
 
76
- ```ts
77
- type EntitySchemaOperationType =
78
- | 'list_entities'
79
- | 'get_entity'
80
- | 'create_entity'
81
- | 'update_entity'
82
- | 'delete_entity'
83
- | 'add_field'
84
- | 'update_field'
85
- | 'delete_field'
86
- | 'set_field_required'
87
- | 'unset_field_required'
88
- ```
89
-
90
- Operation shape:
83
+ Patch operations accepted by `backend-api-design`:
91
84
 
92
- ```ts
93
- type EntitySchemaOperation = {
94
- type: EntitySchemaOperationType
95
- entityName?: string
96
- payload?: Record<string, unknown>
97
- }
85
+ ```text
86
+ create_entity
87
+ update_entity
88
+ delete_entity
89
+ add_field
90
+ update_field
91
+ delete_field
92
+ set_field_required
93
+ unset_field_required
98
94
  ```
99
95
 
100
- Patch shape:
96
+ Inspect/version operations are top-level tool calls, not patch operations:
101
97
 
102
- ```ts
103
- type EntitySchemaPatch = {
104
- operations: EntitySchemaOperation[]
105
- }
98
+ ```text
99
+ get_current_schema
100
+ list_entities
101
+ get_entity
102
+ list_schema_versions
103
+ get_schema_version
104
+ restore_schema_version
105
+ diff_schema_versions
106
106
  ```
107
107
 
108
108
  ## Operation Payloads
@@ -114,20 +114,28 @@ Use when the entity does not exist.
114
114
  ```json
115
115
  {
116
116
  "type": "create_entity",
117
- "entityName": "Todo",
117
+ "entityName": "Article",
118
118
  "payload": {
119
- "definition": {
120
- "name": "Todo",
121
- "type": "object",
122
- "visibility": "private",
123
- "properties": {
124
- "text": { "type": "string" },
125
- "completed": { "type": "boolean", "default": false }
126
- },
127
- "required": ["text"],
128
- "access": {
129
- "authenticated": { "read": "own", "create": "own", "update": "own", "delete": "own" },
130
- "public": { "read": "none", "create": "none", "update": "none", "delete": "none" }
119
+ "description": "Published article.",
120
+ "visibility": "public",
121
+ "type": "object",
122
+ "properties": {
123
+ "title": { "type": "string" },
124
+ "slug": { "type": "string" },
125
+ "status": { "type": "string", "enum": ["draft", "published"], "default": "draft" },
126
+ "publishedAt": { "type": ["date", "null"], "default": null }
127
+ },
128
+ "required": ["title", "slug"],
129
+ "access": {
130
+ "authenticated": { "read": "all", "create": "all", "update": "all", "delete": "all" },
131
+ "public": {
132
+ "read": "list",
133
+ "create": "none",
134
+ "update": "none",
135
+ "allowedFilters": ["slug", "status"],
136
+ "allowedSorts": ["publishedAt", "updatedDate"],
137
+ "defaultLimit": 20,
138
+ "maxLimit": 100
131
139
  }
132
140
  }
133
141
  }
@@ -136,53 +144,44 @@ Use when the entity does not exist.
136
144
 
137
145
  Rules:
138
146
 
139
- - `payload.definition.properties` is required.
140
- - `required` must reference existing fields.
141
- - Include explicit `access`, even if `visibility` seems obvious.
147
+ - Include explicit `access.authenticated` and `access.public`.
148
+ - Required fields must exist in `properties`.
149
+ - Required fields with `default` or `autoGenerate` may be omitted by create callers.
150
+ - Do not include system fields such as `id`, `created_date`, `updated_date`, or owner IDs.
142
151
 
143
152
  ### update_entity
144
153
 
145
- Use for metadata and contract sections, not individual field changes.
154
+ Use for entity metadata and contract sections.
146
155
 
147
156
  ```json
148
157
  {
149
158
  "type": "update_entity",
150
159
  "entityName": "Article",
151
160
  "payload": {
152
- "patch": {
153
- "access": {
154
- "authenticated": { "read": "all", "create": "all", "update": "all", "delete": "all" },
155
- "public": {
156
- "read": "list",
157
- "create": "none",
158
- "update": "none",
159
- "delete": "none",
160
- "allowedFilters": ["status", "slug"],
161
- "allowedSorts": ["publishedAt"],
162
- "defaultLimit": 20,
163
- "maxLimit": 100
164
- }
165
- },
166
- "performance": {
167
- "defaultLimit": 20,
168
- "maxLimit": 100,
169
- "allowedSorts": ["publishedAt", "updatedDate"]
161
+ "description": "Published article with scoped public lookup.",
162
+ "access": {
163
+ "authenticated": { "read": "all", "create": "all", "update": "all", "delete": "all" },
164
+ "public": {
165
+ "read": "scoped",
166
+ "create": "none",
167
+ "update": "none",
168
+ "requiredScopes": ["slug"],
169
+ "allowedFilters": ["slug", "status"],
170
+ "allowedSorts": ["publishedAt"],
171
+ "defaultLimit": 1,
172
+ "maxLimit": 10
170
173
  }
174
+ },
175
+ "presentation": {
176
+ "titleField": "title",
177
+ "listFields": ["title", "status", "publishedAt"]
171
178
  }
172
179
  }
173
180
  }
174
181
  ```
175
182
 
176
- Use this for:
177
-
178
- - `description`
179
- - `visibility`
180
- - `access`
181
- - `indexes`
182
- - `relations`
183
- - `presentation`
184
- - `lifecycle`
185
- - `performance`
183
+ Allowed sections include `description`, `visibility`, `isActive`, `access`, `indexes`,
184
+ `relations`, `presentation`, `lifecycle`, and `performance`.
186
185
 
187
186
  ### add_field
188
187
 
@@ -204,9 +203,9 @@ Use this for:
204
203
 
205
204
  Rules:
206
205
 
207
- - If adding a required field to an existing entity, prefer a default.
208
- - If no default exists and records already exist, treat as risky and ask for migration policy.
209
- - Add indexes only when new query paths need them.
206
+ - Add optional fields or fields with defaults for existing entities.
207
+ - If an added field must be required and has no default, align with the user first.
208
+ - Add indexes only for actual query paths.
210
209
 
211
210
  ### update_field
212
211
 
@@ -223,13 +222,10 @@ Rules:
223
222
  }
224
223
  ```
225
224
 
226
- Risk levels:
225
+ Usually safe: adding enum values, adding descriptions, adding validation hints.
227
226
 
228
- - Adding enum values: usually safe.
229
- - Removing enum values: risky if existing records use them.
230
- - Changing type: high risk.
231
- - Making nullable field required: high risk.
232
- - Removing default: risky for create flows.
227
+ Risky: removing enum values, changing type, removing defaults, narrowing nullability, or making
228
+ existing data invalid.
233
229
 
234
230
  ### delete_field
235
231
 
@@ -244,11 +240,7 @@ Risk levels:
244
240
  }
245
241
  ```
246
242
 
247
- Rules:
248
-
249
- - Default `removeFromData` to `false`.
250
- - Only use `removeFromData: true` after explicit confirmation.
251
- - Removing from schema does not necessarily remove historical data.
243
+ Default `removeFromData` to `false`. `true` requires explicit user confirmation.
252
244
 
253
245
  ### set_field_required / unset_field_required
254
246
 
@@ -260,11 +252,8 @@ Rules:
260
252
  }
261
253
  ```
262
254
 
263
- Rules:
264
-
265
- - Required fields must exist in `properties`.
266
- - Setting required on an existing field is risky unless a default exists or old records are acceptable.
267
- - Unsetting required is generally safe but may change frontend validation expectations.
255
+ Setting required on existing data is risky unless a default exists or old records are known valid.
256
+ Unsetting required is usually safe.
268
257
 
269
258
  ### delete_entity
270
259
 
@@ -273,7 +262,7 @@ Rules:
273
262
  "type": "delete_entity",
274
263
  "entityName": "Todo",
275
264
  "payload": {
276
- "hard": false,
265
+ "mode": "soft",
277
266
  "deleteData": false
278
267
  }
279
268
  }
@@ -281,52 +270,35 @@ Rules:
281
270
 
282
271
  Rules:
283
272
 
284
- - Default to soft delete.
285
- - Hard delete requires explicit user request.
286
- - `deleteData: true` requires explicit confirmation because it destroys records.
273
+ - Default to `mode: "soft"`.
274
+ - `mode: "hard"` requires explicit user request.
275
+ - `deleteData: true` requires explicit confirmation.
287
276
 
288
277
  ## Risk Checklist
289
278
 
290
- Stop and ask before applying when:
291
-
292
- - deleting an entity;
293
- - deleting a field used by UI or workflow output;
294
- - removing historical data;
295
- - changing field type;
296
- - making a field required without default;
297
- - broadening public access;
298
- - enabling public write;
299
- - reducing public guardrails such as removing required scopes or raising max limits;
300
- - changing owner/public scope semantics.
301
-
302
- Usually safe:
303
-
304
- - adding optional field;
305
- - adding field with default;
306
- - adding enum value;
307
- - adding index for existing query path;
308
- - adding presentation metadata;
309
- - adding stricter public filter/sort limits.
279
+ Stop before apply and align with the user when the patch:
310
280
 
311
- ## Version Semantics
281
+ - deletes entity definitions;
282
+ - deletes fields used by app UI or AI outputs;
283
+ - removes data from historical records;
284
+ - changes field types;
285
+ - makes fields required without defaults;
286
+ - broadens public read;
287
+ - enables public create/update;
288
+ - removes public scopes/filter/sort limits.
312
289
 
313
- Schema versions manage definitions, not business records.
290
+ Usually safe to apply directly:
314
291
 
315
- Restore behavior:
292
+ - create a new entity with explicit private/public access;
293
+ - add optional fields;
294
+ - add fields with defaults;
295
+ - add enum values;
296
+ - add indexes for known query paths;
297
+ - add presentation/performance metadata.
316
298
 
317
- ```text
318
- restore(versionId)
319
- -> creates a new current schema version from old manifest
320
- -> future create/update uses restored definitions
321
- -> old entitydatashares records are not rolled back
322
- ```
323
-
324
- Data records may still carry old fields after schema changes. Do not assume restore deletes or
325
- rewrites data.
299
+ ## Sync Handoff
326
300
 
327
- ## Manifest Sync Handoff
328
-
329
- After apply, the result should include a version hint:
301
+ After apply, use the returned version hint:
330
302
 
331
303
  ```json
332
304
  {
@@ -337,62 +309,21 @@ After apply, the result should include a version hint:
337
309
  }
338
310
  ```
339
311
 
340
- Agent handoff sequence:
312
+ Then:
341
313
 
342
- 1. Sync artifacts for `versionId`.
314
+ 1. Call `sync_schema_artifacts` with the version ID and app root.
343
315
  2. Read `.howone/database/manifest.json`.
344
- 3. Update `src/lib/sdk.ts` generated entity bindings.
345
- 4. Update frontend CRUD/query code.
346
- 5. Validate build.
347
-
348
- Never update frontend entity types from the draft patch alone when a synced manifest exists.
316
+ 3. Leave backend track.
317
+ 4. If app code must call the entity, read SDK track files and update `src/lib/sdk.ts`.
349
318
 
350
- ## Common Agent Mistakes
319
+ ## Common Mistakes
351
320
 
352
321
  | Mistake | Correct behavior |
353
322
  |---|---|
354
- | Hand-writing `.howone/database/manifest.json` | Sync from backend version. |
355
- | Applying one operation after previewing a different patch | Apply the exact previewed patch. |
356
- | Adding required field without default | Treat as risk; ask migration/default policy. |
357
- | Public read list without `allowedFilters` / `allowedSorts` | Add public guardrails. |
358
- | Using `visibility: "public"` as permission model | Write explicit `access.public`. |
323
+ | `payload.definition` for create | Put `properties`, `required`, `access`, etc. directly under `payload`. |
324
+ | `payload.patch` for update | Put changed contract sections directly under `payload`. |
325
+ | `fields[]` or `requiredFields` | Use `properties` object map and `required` array. |
326
+ | Handwriting `.howone/database/manifest.json` | Use `sync_schema_artifacts`. |
327
+ | Updating SDK bindings from draft patch | Read synced manifest first. |
328
+ | Using public visibility as permission model | Write explicit `access.public`. |
359
329
  | Deleting data because schema changed | Schema changes do not imply data deletion. |
360
- | Updating `src/lib/sdk.ts` before manifest sync | Wait for synced manifest. |
361
-
362
- ## Minimal Safe Patch Template
363
-
364
- ```ts
365
- const state = await client.schema.getState()
366
-
367
- const patch = {
368
- operations: [
369
- {
370
- type: 'add_field',
371
- entityName: 'Todo',
372
- payload: {
373
- fieldName: 'priority',
374
- field: {
375
- type: 'string',
376
- enum: ['low', 'medium', 'high'],
377
- default: 'medium',
378
- },
379
- required: false,
380
- },
381
- },
382
- ],
383
- }
384
-
385
- const preview = await client.schema.previewPatch(patch, {
386
- expectedVersionId: state.currentVersionId,
387
- reason: 'Add todo priority',
388
- })
389
-
390
- if (preview.risk?.level === 'dangerous') {
391
- throw new Error('User confirmation required before applying schema patch')
392
- }
393
-
394
- const applied = await client.schema.applyPatch(patch, {
395
- expectedVersionId: state.currentVersionId,
396
- reason: 'Add todo priority',
397
- })
398
- ```
@@ -0,0 +1,151 @@
1
+ # Backend Access Models
2
+
3
+ Use this reference while designing entity `access` contracts with `backend-api-design`.
4
+ It is backend-only: do not write SDK calls from this file.
5
+
6
+ Access answers four questions:
7
+
8
+ 1. Who can read records?
9
+ 2. Who can create records?
10
+ 3. Who can update/delete records?
11
+ 4. Which anonymous public operations are safe?
12
+
13
+ ## Authenticated Access
14
+
15
+ Authenticated access is for logged-in users. Values are `own`, `all`, or `none`.
16
+
17
+ Private per-user records:
18
+
19
+ ```json
20
+ {
21
+ "access": {
22
+ "authenticated": {
23
+ "read": "own",
24
+ "create": "own",
25
+ "update": "own",
26
+ "delete": "own"
27
+ },
28
+ "public": {
29
+ "read": "none",
30
+ "create": "none",
31
+ "update": "none",
32
+ "delete": "none"
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ Shared authenticated records:
39
+
40
+ ```json
41
+ {
42
+ "access": {
43
+ "authenticated": {
44
+ "read": "all",
45
+ "create": "all",
46
+ "update": "all",
47
+ "delete": "all"
48
+ },
49
+ "public": {
50
+ "read": "none",
51
+ "create": "none",
52
+ "update": "none",
53
+ "delete": "none"
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ Use `all` conservatively. If the product implies team roles, moderation, or ownership transitions
60
+ that the dynamic schema cannot express yet, keep the schema narrow and leave role logic to a future
61
+ platform capability or app-owned guard.
62
+
63
+ ## Public Read
64
+
65
+ Public read is for anonymous access. Values are `none`, `list`, or `scoped`.
66
+
67
+ Use `list` only when broad anonymous browsing is intended:
68
+
69
+ ```json
70
+ {
71
+ "access": {
72
+ "authenticated": {
73
+ "read": "all",
74
+ "create": "all",
75
+ "update": "all",
76
+ "delete": "all"
77
+ },
78
+ "public": {
79
+ "read": "list",
80
+ "create": "none",
81
+ "update": "none",
82
+ "delete": "none",
83
+ "allowedFilters": ["status", "category", "slug"],
84
+ "allowedSorts": ["publishedAt", "updatedDate"],
85
+ "defaultLimit": 20,
86
+ "maxLimit": 100
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ Use `scoped` when a public page should expose only records that match route-bound scope fields:
93
+
94
+ ```json
95
+ {
96
+ "access": {
97
+ "public": {
98
+ "read": "scoped",
99
+ "requiredScopes": ["shareId"],
100
+ "allowedFilters": ["shareId", "active"],
101
+ "allowedSorts": ["updatedDate"],
102
+ "defaultLimit": 1,
103
+ "maxLimit": 1
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ Public rules:
110
+
111
+ - Public filters must be listed in `allowedFilters`.
112
+ - Public sorts must be listed in `allowedSorts`.
113
+ - Scoped reads must include every `requiredScopes` field.
114
+ - Public records must not contain private prompts, tokens, internal review states, or owner-only data.
115
+ - Public list entities need indexes covering common filters and sorts.
116
+
117
+ ## Public Create
118
+
119
+ Public create is only for anonymous submissions such as waitlists, contact forms, feedback, and
120
+ public RSVP flows.
121
+
122
+ ```json
123
+ {
124
+ "access": {
125
+ "public": {
126
+ "read": "none",
127
+ "create": "scoped",
128
+ "update": "none",
129
+ "delete": "none",
130
+ "requiredScopes": ["created_by_user_id"],
131
+ "allowedFilters": [],
132
+ "allowedSorts": [],
133
+ "defaultLimit": 1,
134
+ "maxLimit": 1
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ Do not enable public create for user libraries, generated histories, admin content, or entities that
141
+ store sensitive workflow output. Add app-owned anti-abuse controls when anonymous submission is open.
142
+
143
+ ## Access Design Checklist
144
+
145
+ - The entity has the narrowest access model that supports the product.
146
+ - Private history uses authenticated `own`.
147
+ - Shared admin/internal data uses authenticated `all` only when acceptable.
148
+ - Public list data exposes only safe fields.
149
+ - Public scoped pages use stable scope fields and low `maxLimit`.
150
+ - Public create has a clear ownership/scope story and no anonymous read by default.
151
+ - Access decisions are synced before SDK/UI implementation starts.