howone 0.1.23 → 0.1.25
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.
- package/package.json +1 -1
- package/templates/vite/.howone/skills/howone-sdk/01-architect/01-app-generation.md +180 -91
- package/templates/vite/.howone/skills/howone-sdk/01-architect/02-manifest-codegen.md +67 -4
- package/templates/vite/.howone/skills/howone-sdk/02-database/01-schema-design.md +463 -69
- package/templates/vite/.howone/skills/howone-sdk/02-database/02-schema-operations.md +366 -64
- package/templates/vite/.howone/skills/howone-sdk/02-database/03-data-access-patterns.md +204 -67
- package/templates/vite/.howone/skills/howone-sdk/02-database/04-query-dsl-and-responses.md +237 -0
- package/templates/vite/.howone/skills/howone-sdk/02-database/05-ai-persistence-patterns.md +372 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/01-client-setup.md +58 -36
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/02-entity-operations.md +67 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/03-auth.md +267 -469
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/04-react-integration.md +113 -320
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/07-ai-action-calls.md +66 -16
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/08-extension-boundaries.md +226 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/01-ai-capability-architecture.md +159 -96
- package/templates/vite/.howone/skills/howone-sdk/04-ai/02-workflow-contract-rules.md +353 -96
- package/templates/vite/.howone/skills/howone-sdk/04-ai/03-ai-sdk-handoff.md +181 -42
- package/templates/vite/.howone/skills/howone-sdk/04-ai/04-service-capability-catalog.md +281 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/05-workflow-operations.md +256 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/06-ai-feature-playbooks.md +296 -0
- package/templates/vite/.howone/skills/howone-sdk/SKILL.md +29 -12
- package/templates/vite/.howone/skills/howone-sdk/agents/openai.yaml +2 -2
- package/templates/vite/package.json +1 -1
- package/templates/vite/.howone/skills/howone-sdk/04-ai/.gitkeep +0 -1
|
@@ -1,76 +1,332 @@
|
|
|
1
1
|
# Schema Operations
|
|
2
2
|
|
|
3
|
-
Use this reference when applying backend entity schema changes through HowOne runtime tools
|
|
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.
|
|
4
7
|
|
|
5
8
|
## Source Of Truth
|
|
6
9
|
|
|
7
10
|
```text
|
|
8
|
-
agent proposal
|
|
9
|
-
|
|
10
|
-
synced .howone/database files
|
|
11
|
-
src/lib/sdk.ts
|
|
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
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
Do not hand-write `.howone/database
|
|
18
|
+
Do not hand-write `.howone/database/*`. Sync artifacts from a concrete backend version.
|
|
15
19
|
|
|
16
20
|
## Preferred Patch Flow
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Use patch flow for most feature-level changes:
|
|
19
23
|
|
|
20
|
-
1. Inspect current schema
|
|
21
|
-
2. Design
|
|
24
|
+
1. Inspect current `schema.getState()` and current entity definitions.
|
|
25
|
+
2. Design a full patch containing all related operations.
|
|
22
26
|
3. Preview the patch.
|
|
23
|
-
4.
|
|
24
|
-
5.
|
|
25
|
-
6.
|
|
26
|
-
7.
|
|
27
|
-
8.
|
|
28
|
-
9.
|
|
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.
|
|
29
34
|
|
|
30
|
-
Do not preview
|
|
35
|
+
Do not preview one patch and apply a different operation set.
|
|
31
36
|
|
|
32
|
-
##
|
|
37
|
+
## SDK Schema Client
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const state = await client.schema.getState()
|
|
41
|
+
const definitions = await client.schema.listDefinitions()
|
|
42
|
+
const todo = await client.schema.getDefinition('Todo')
|
|
43
|
+
|
|
44
|
+
const preview = await client.schema.previewPatch(patch, {
|
|
45
|
+
expectedVersionId: state.currentVersionId,
|
|
46
|
+
reason: 'Add priority field to Todo',
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if (preview.risk?.level === 'dangerous') {
|
|
50
|
+
// stop and ask for confirmation
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const applied = await client.schema.applyPatch(patch, {
|
|
54
|
+
expectedVersionId: state.currentVersionId,
|
|
55
|
+
reason: 'Add priority field to Todo',
|
|
56
|
+
})
|
|
57
|
+
```
|
|
33
58
|
|
|
34
|
-
|
|
59
|
+
Available schema methods:
|
|
35
60
|
|
|
36
|
-
|
|
|
61
|
+
| Method | Use |
|
|
37
62
|
|---|---|
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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. |
|
|
73
|
+
|
|
74
|
+
## Operation Types
|
|
75
|
+
|
|
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:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
type EntitySchemaOperation = {
|
|
94
|
+
type: EntitySchemaOperationType
|
|
95
|
+
entityName?: string
|
|
96
|
+
payload?: Record<string, unknown>
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Patch shape:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
type EntitySchemaPatch = {
|
|
104
|
+
operations: EntitySchemaOperation[]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Operation Payloads
|
|
109
|
+
|
|
110
|
+
### create_entity
|
|
111
|
+
|
|
112
|
+
Use when the entity does not exist.
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"type": "create_entity",
|
|
117
|
+
"entityName": "Todo",
|
|
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" }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Rules:
|
|
138
|
+
|
|
139
|
+
- `payload.definition.properties` is required.
|
|
140
|
+
- `required` must reference existing fields.
|
|
141
|
+
- Include explicit `access`, even if `visibility` seems obvious.
|
|
142
|
+
|
|
143
|
+
### update_entity
|
|
144
|
+
|
|
145
|
+
Use for metadata and contract sections, not individual field changes.
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"type": "update_entity",
|
|
150
|
+
"entityName": "Article",
|
|
151
|
+
"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"]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Use this for:
|
|
177
|
+
|
|
178
|
+
- `description`
|
|
179
|
+
- `visibility`
|
|
180
|
+
- `access`
|
|
181
|
+
- `indexes`
|
|
182
|
+
- `relations`
|
|
183
|
+
- `presentation`
|
|
184
|
+
- `lifecycle`
|
|
185
|
+
- `performance`
|
|
186
|
+
|
|
187
|
+
### add_field
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"type": "add_field",
|
|
192
|
+
"entityName": "Todo",
|
|
193
|
+
"payload": {
|
|
194
|
+
"fieldName": "priority",
|
|
195
|
+
"field": {
|
|
196
|
+
"type": "string",
|
|
197
|
+
"enum": ["low", "medium", "high"],
|
|
198
|
+
"default": "medium"
|
|
199
|
+
},
|
|
200
|
+
"required": false
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Rules:
|
|
206
|
+
|
|
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.
|
|
210
|
+
|
|
211
|
+
### update_field
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"type": "update_field",
|
|
216
|
+
"entityName": "Todo",
|
|
217
|
+
"payload": {
|
|
218
|
+
"fieldName": "priority",
|
|
219
|
+
"patch": {
|
|
220
|
+
"enum": ["low", "medium", "high", "urgent"]
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Risk levels:
|
|
227
|
+
|
|
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.
|
|
233
|
+
|
|
234
|
+
### delete_field
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"type": "delete_field",
|
|
239
|
+
"entityName": "Todo",
|
|
240
|
+
"payload": {
|
|
241
|
+
"fieldName": "legacyTag",
|
|
242
|
+
"removeFromData": false
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
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.
|
|
252
|
+
|
|
253
|
+
### set_field_required / unset_field_required
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"type": "set_field_required",
|
|
258
|
+
"entityName": "Todo",
|
|
259
|
+
"payload": { "fieldName": "text" }
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
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.
|
|
268
|
+
|
|
269
|
+
### delete_entity
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"type": "delete_entity",
|
|
274
|
+
"entityName": "Todo",
|
|
275
|
+
"payload": {
|
|
276
|
+
"hard": false,
|
|
277
|
+
"deleteData": false
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Rules:
|
|
283
|
+
|
|
284
|
+
- Default to soft delete.
|
|
285
|
+
- Hard delete requires explicit user request.
|
|
286
|
+
- `deleteData: true` requires explicit confirmation because it destroys records.
|
|
287
|
+
|
|
288
|
+
## Risk Checklist
|
|
289
|
+
|
|
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.
|
|
310
|
+
|
|
311
|
+
## Version Semantics
|
|
312
|
+
|
|
313
|
+
Schema versions manage definitions, not business records.
|
|
314
|
+
|
|
315
|
+
Restore behavior:
|
|
316
|
+
|
|
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.
|
|
326
|
+
|
|
327
|
+
## Manifest Sync Handoff
|
|
328
|
+
|
|
329
|
+
After apply, the result should include a version hint:
|
|
74
330
|
|
|
75
331
|
```json
|
|
76
332
|
{
|
|
@@ -81,16 +337,62 @@ After apply, use the returned version context:
|
|
|
81
337
|
}
|
|
82
338
|
```
|
|
83
339
|
|
|
84
|
-
|
|
340
|
+
Agent handoff sequence:
|
|
341
|
+
|
|
342
|
+
1. Sync artifacts for `versionId`.
|
|
343
|
+
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.
|
|
85
347
|
|
|
86
|
-
|
|
348
|
+
Never update frontend entity types from the draft patch alone when a synced manifest exists.
|
|
349
|
+
|
|
350
|
+
## Common Agent Mistakes
|
|
351
|
+
|
|
352
|
+
| Mistake | Correct behavior |
|
|
353
|
+
|---|---|
|
|
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`. |
|
|
359
|
+
| 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. |
|
|
87
361
|
|
|
88
|
-
|
|
362
|
+
## Minimal Safe Patch Template
|
|
89
363
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
3. Preview/apply one `add_field` patch or use the schema operation fallback.
|
|
93
|
-
4. Sync artifacts.
|
|
94
|
-
5. Update SDK/UI.
|
|
364
|
+
```ts
|
|
365
|
+
const state = await client.schema.getState()
|
|
95
366
|
|
|
96
|
-
|
|
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
|
+
```
|