@assembly-js/app-foundation 0.3.1 → 0.3.2

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/CHANGELOG.md CHANGED
@@ -19,6 +19,9 @@ A patch release never changes what an app's build requires; a minor release may
19
19
  not by hand; see `changes/README.md`. CI fails a PR that changes a check script without a
20
20
  `bump: minor` change file.
21
21
 
22
+ ## 0.3.2
23
+ - [new] The `assembly-api-contract` skill teaches the build agent to write and maintain `.assembly/openapi.json` alongside `app/api` routes.
24
+
22
25
  ## 0.3.1
23
26
  - [guidance] The build agent now upgrades an app's bridge to 2.1.0 and its foundation package instead of skipping a deep link, mounts the location reporter, and deep-links notifications to the specific record.
24
27
  - [new] App destinations in `NavigateDestination` now accept an optional `path` for links to an app's inner page.
package/CLAUDE.md CHANGED
@@ -68,6 +68,7 @@ Assembly-specific gotchas live in `.claude/skills/<name>/SKILL.md`. Invoke them
68
68
  - `assembly-route-auth` — REQUIRED on every `app/api/.../[id]/route.ts` handler: `requireAuth` + `requireCompanyScope` before returning or mutating data.
69
69
  - `assembly-scheduled-work` — REQUIRED when the prompt implies anything on a schedule or while nobody has the app open (remind, daily digest, every week, follow up in N days, at a specific time): one `*/30 * * * *` Vercel cron in `vercel.json` hitting one dispatcher route, every scheduled action a row in a schedule table, `CRON_SECRET` declared in `requiredSecrets` and verified on every tick, `getOfflineAssembly()` for platform calls. Checking for due work on page load ships a schedule that never fires.
70
70
  - `assembly-route-errors` — REQUIRED in every route handler: use `handleRouteError` from `@assembly-js/app-foundation/server` in catch blocks — never return `err.message`. Validate values before they hit typed DB columns (e.g. `parseUuid` before a uuid-column lookup).
71
+ - `assembly-api-contract` — REQUIRED whenever an `app/api` route is added, removed, or changes its request or response shape: keep `.assembly/openapi.json` (OpenAPI 3.1, `servers: [{"url": "/api"}]`, paths without the `/api` prefix) in step; cron, webhook, `assembly-*` and `privateRoutes` routes stay out.
71
72
  - `assembly-action-results` — REQUIRED when a server action refuses work the UI should explain (validation, permission, not ready to send): return `{ ok: false, message }` instead of throwing. A thrown message is redacted in production.
72
73
  - `assembly-dev-server-logs` — the dev server's stdout/stderr (server-side errors, stack traces, console output) is readable at `/tmp/dev-server.log`; check it before guessing at a 500 or broken preview.
73
74
  - Your own preview is `localhost:3000`; never fetch the public `sb-*.vercel.run` host from inside the sandbox (egress to it is blocked).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assembly-js/app-foundation",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Versioned foundation helpers and build context for Assembly generated apps",
5
5
  "type": "module",
6
6
  "files": [
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: assembly-api-contract
3
+ description: REQUIRED whenever you add, remove, or change the request or response shape of a route under app/api. Write and maintain .assembly/openapi.json, the app's OpenAPI 3.1 API contract — one operation per callable (method, path) with JSON Schema bodies; servers /api and paths without the /api prefix. Skip cron, webhook, assembly-* and privateRoutes routes.
4
+ ---
5
+
6
+ Keep `.assembly/openapi.json` in step with `app/api` in the same turn as the route change. It is the document other apps and tools read to learn what this app's API accepts and returns.
7
+
8
+ ## When to touch it
9
+
10
+ - Route added: add an operation for each exported GET, POST, PUT, PATCH or DELETE handler; never document head or options, which are not proxied.
11
+ - Route removed, or a method removed from it: delete that operation (and the path entry once it has no operations).
12
+ - Request body, query parameter, or response field changed: update the schemas to match.
13
+
14
+ ## Format
15
+
16
+ - OpenAPI `3.1.0`, JSON, at `.assembly/openapi.json`. Bodies are JSON Schema.
17
+ - Identity of an operation is `(method, path template)`. `operationId` is a label only; renaming it changes nothing.
18
+ - `servers` is `[{ "url": "/api" }]` and `paths` are written **without** the `/api` prefix, exactly as the route scanner templates them (`app/api/projects/[projectId]/tasks/route.ts` becomes `/projects/{projectId}/tasks`). Path parameters are `{name}` using the folder's bracket name.
19
+ - The stored document is capped at 1 MB and publishing refuses a larger one. Keep it small: describe fields, do not paste sample data.
20
+
21
+ ## What stays out
22
+
23
+ A route is not callable by other apps, so it must not appear, when it is:
24
+
25
+ - under a `cron`, `webhook` or `webhooks` segment, or a segment starting `assembly-`
26
+ - wrapped in `cronRoute` or `automationActionRoute`
27
+ - matched by a glob in `privateRoutes` in `.assembly/app.json` (matched against the route template, not the file path). The scanner records these as excluded, so documenting one is reported as a drift.
28
+
29
+ ## Worked example
30
+
31
+ `app/api/projects/[projectId]/tasks/route.ts` exports `GET` (list) and `POST` (create):
32
+
33
+ ```json
34
+ {
35
+ "openapi": "3.1.0",
36
+ "info": { "title": "Project tasks", "version": "1.0.0" },
37
+ "servers": [{ "url": "/api" }],
38
+ "paths": {
39
+ "/projects/{projectId}/tasks": {
40
+ "get": {
41
+ "operationId": "listProjectTasks",
42
+ "summary": "List the tasks of a project",
43
+ "parameters": [
44
+ { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
45
+ ],
46
+ "responses": {
47
+ "200": {
48
+ "description": "The project's tasks",
49
+ "content": {
50
+ "application/json": {
51
+ "schema": {
52
+ "type": "object",
53
+ "required": ["tasks"],
54
+ "properties": { "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/Task" } } }
55
+ }
56
+ }
57
+ }
58
+ },
59
+ "404": { "$ref": "#/components/responses/Error" }
60
+ }
61
+ },
62
+ "post": {
63
+ "operationId": "createProjectTask",
64
+ "summary": "Create a task in a project",
65
+ "parameters": [
66
+ { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
67
+ ],
68
+ "requestBody": {
69
+ "required": true,
70
+ "content": {
71
+ "application/json": {
72
+ "schema": {
73
+ "type": "object",
74
+ "required": ["title"],
75
+ "properties": { "title": { "type": "string", "minLength": 1 }, "dueDate": { "type": ["string", "null"], "format": "date" } }
76
+ }
77
+ }
78
+ }
79
+ },
80
+ "responses": {
81
+ "201": { "description": "The created task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Task" } } } },
82
+ "400": { "$ref": "#/components/responses/Error" }
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "components": {
88
+ "schemas": {
89
+ "Task": {
90
+ "type": "object",
91
+ "required": ["id", "title", "done"],
92
+ "properties": {
93
+ "id": { "type": "string", "format": "uuid" },
94
+ "title": { "type": "string" },
95
+ "done": { "type": "boolean" },
96
+ "dueDate": { "type": ["string", "null"], "format": "date" }
97
+ }
98
+ }
99
+ },
100
+ "responses": {
101
+ "Error": {
102
+ "description": "Error",
103
+ "content": {
104
+ "application/json": {
105
+ "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" } } }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ Document the error shape your handlers really return (see `assembly-route-errors`: `{ error }`).
115
+
116
+ ## Drift
117
+
118
+ The platform may compare the contract with the scanned routes after a turn and report disagreements. Fix each kind at its source:
119
+
120
+ - Documented operation with no route in code: remove the operation, or restore the route if it was deleted by mistake. A method the file does not export counts as missing.
121
+ - Route in code with no operation: add the operation, unless the route is one of the excluded kinds above.
122
+ - Same path under a different parameter name (`{id}` in the contract, `[taskId]` in the folder): Next allows one name per dynamic segment, so rename the contract's parameter to the folder's.
123
+ - Shape mismatch (a response field renamed, a body field now required): update the schemas to what the handler returns and accepts.