Skip to content

Commit f79460c

Browse files
committed
feat(schema): v1.3 — schedule shape simplified, members get URI-style principal
Schedules - Drop `assetIds: string[]`. Replace with `anchors: AnchorRef[]` (scenecast canonical `{ asset_id, anchor? }`). Optional — empty/omitted means the run inherits scene context. Present means a curated input attachment, same wire format the iOS composer uses for selection. - Drop sibling `timezone` field. `cron` now accepts an optional `TZ=<IANA>` prefix (`TZ=Europe/Berlin 0 6 * * *`) — one string carries the recurrence rule, DST-aware. - `title` becomes optional (defaults to `id`). - Add `tools` override (allow/deny) for per-schedule tool scoping. Members - Add canonical `principal: "<provider>:<identity>"` form (e.g. `github:mirkokiefer`, `email:foo@bar.com`, `google:<sub>`, `apple:<sub>`). The provider prefix is the auth-provider slug; identity is unique within that provider. Scales to any new auth provider (including asset-provider-auth like `slack:U07ABC`) with zero schema change. `email` and `device` remain as legacy aliases.
1 parent c8e3ff5 commit f79460c

1 file changed

Lines changed: 29 additions & 10 deletions

File tree

schemas/scene-blueprint-v1.json

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,30 @@
179179
"type": "array",
180180
"items": {
181181
"type": "object",
182-
"required": ["id", "title", "cron", "prompt"],
182+
"required": ["id", "cron", "prompt"],
183183
"additionalProperties": false,
184184
"properties": {
185-
"id": { "type": "string", "pattern": "^[a-z0-9_-]+$" },
186-
"title": { "type": "string" },
187-
"cron": { "type": "string" },
188-
"prompt": { "type": "string" },
189-
"assetIds": { "type": "array", "items": { "type": "string" } },
190-
"runAs": { "type": "string", "pattern": "^[a-z0-9_-]+$", "description": "Role slug to scope the run's layer permissions. References a `roles[]` slug or built-in (owner|editor|viewer). Defaults to owner-attribution." },
191-
"enabled": { "type": "boolean", "default": true },
192-
"checks": { "type": "array", "items": { "$ref": "#/$defs/check" } }
185+
"id": { "type": "string", "pattern": "^[a-z0-9_-]+$" },
186+
"title": { "type": "string", "description": "Optional display name. Defaults to `id` titlecased." },
187+
"cron": { "type": "string", "description": "Standard 5-field cron expression with optional `TZ=<IANA>` prefix. Examples: `0 6 * * *` (UTC daily 06:00), `TZ=Europe/Berlin 0 6 * * *` (Berlin local time, DST-aware). The cron string is the recurrence rule — `next_run_at` is derived on the DB row, never duplicated here." },
188+
"prompt": { "type": "string", "description": "The user-input prompt that fires on each tick. Lives directly on the schedule template — no separate phantom prompt_user call. The scheduler reads this at fire time and seeds a real prompt_user call on the freshly-minted child run." },
189+
"anchors": {
190+
"type": "array",
191+
"description": "Optional explicit input attachments. Each entry is an `AnchorRef` (scenecast canonical shape: `{ asset_id, anchor? }`). When omitted/empty, the scheduled run inherits the scene's default context (all visible assets). When present, behaves like a user pre-selecting these elements in the iOS composer before tapping Run — identical wire format. `asset_id` may be a blueprint-local handle (resolved by the applier) or a real db id.",
192+
"items": {
193+
"type": "object",
194+
"required": ["asset_id"],
195+
"additionalProperties": false,
196+
"properties": {
197+
"asset_id": { "type": "string" },
198+
"anchor": { "type": "string", "description": "Optional sub-element selector in scenecast anchor grammar: `item[<id>]`, `row[<idx>|<id>]`, `field[<key>]`, `step[<id>]`, `metric[<id>]`, `zone[<name>]`, `object[<id>]`, `surface[<id>]`, `point[<x>,<y>(,<z>)]`. Omit to address the whole asset." }
199+
}
200+
}
201+
},
202+
"runAs": { "type": "string", "pattern": "^[a-z0-9_-]+$", "description": "Role slug to scope the run's layer permissions. References a `roles[]` slug or built-in (owner|editor|viewer). Defaults to the scene's owner-attribution." },
203+
"tools": { "type": "object", "additionalProperties": false, "properties": { "allow": { "type": "array", "items": { "type": "string" } }, "deny": { "type": "array", "items": { "type": "string" } } }, "description": "Optional override of the scene's tool allow/deny. Omit to inherit scene defaults." },
204+
"enabled": { "type": "boolean", "default": true, "description": "Cron only fires when true. Use false to pause without deleting the template." },
205+
"checks": { "type": "array", "items": { "$ref": "#/$defs/check" }, "description": "Per-run success criteria evaluated against the completed run." }
193206
}
194207
}
195208
},
@@ -250,11 +263,17 @@
250263
"required": ["role"],
251264
"additionalProperties": false,
252265
"oneOf": [
266+
{ "required": ["principal"] },
253267
{ "required": ["email"] },
254268
{ "required": ["device"] }
255269
],
256270
"properties": {
257-
"email": { "type": "string", "format": "email" },
271+
"principal": {
272+
"type": "string",
273+
"pattern": "^[a-z0-9_-]+:.+$",
274+
"description": "Canonical principal in '<provider>:<identity>' form. Examples: 'github:mirkokiefer', 'github:315252' (numeric id, rename-proof), 'email:foo@bar.com', 'google:<sub>', 'apple:<sub>'. The provider prefix is the auth-provider slug; identity is unique within that provider. Use this form for new blueprints — it scales to any auth provider including asset-provider-auth (e.g. 'slack:U07ABC'). `email` and `device` below remain as legacy aliases."
275+
},
276+
"email": { "type": "string", "format": "email", "description": "Legacy alias for principal 'email:<addr>'. Resolves only against email-auth users." },
258277
"device": { "type": "string", "pattern": "^[a-z0-9_-]+$", "description": "Device slot slug. Provisioning binds physical devices to slots out-of-band (e.g. eFuse pubkey registration)." },
259278
"role": { "type": "string", "pattern": "^[a-z0-9_-]+$", "examples": ["owner", "editor", "viewer"], "description": "Built-in (owner|editor|viewer) or a custom role slug declared in `roles[]`. Anonymous public access is declared on the role itself via `roles[i].public: true`, not on a member entry." }
260279
}

0 commit comments

Comments
 (0)