// The closed action vocabulary and the other frozen lists a role file is // checked against. Design: agents/darkwing/work/slice1-data-model-2026-10-04.md // section 1.4, addendum A sections 2 and 8, addendum B section 2. // // A change to any list here is a reviewed commit to this file. Role files // that name something outside these lists are refused. // Every action with an outside effect that slice 1 touches. Routine work // (editing files, running tests, writing docs, retrying) has no outside // effect and isn't listed. export const ACTIONS = Object.freeze([ "task.create", "task.assign", "task.schedule", "task.update.assigned", "task.close", "task.reassign", "task.scope.change", "task.priority.change", "git.push.working", "git.push.protected", "git.merge.protected", "review.request", "review.verdict", "message.send", "message.external", "role.launch", "role.revoke", "credential.mint", "spend", "deploy", "policy.change", "prd.approve", "decision.resolve.technical", ]); // Always gated: a role file may not list these under withinRole or // crossRole. role.revoke is here because lead decision 46 (6.7) keeps every // revoke gated in slice 1. export const GATED_ONLY = Object.freeze([ "credential.mint", "git.merge.protected", "git.push.protected", "deploy", "spend", "message.external", "policy.change", "prd.approve", "role.revoke", ]); // pi's documented built-in tools; the same list scripts/mosaic-task.mjs // accepts for tasks. export const TOOLS = Object.freeze(["read", "write", "edit", "bash", "grep", "find", "ls"]); // Ordered from narrowest to widest. Intersecting two values keeps the // narrower one. export const NETWORKS = Object.freeze(["none", "api-only", "open"]); export const SERVICES = Object.freeze(["gitea", "vikunja"]); // Gitea token scopes are :. "all" and the admin // category are never granted to a role. export const GITEA_SCOPE_CATEGORIES = Object.freeze([ "activitypub", "issue", "misc", "notification", "organization", "package", "repository", "user", ]); // Vikunja 2.7.0 route groups and verbs a role token may hold. Each pair was // minted or used on a scratch 2.7.0 (Researcher's probes, addendum B // section 2). Anything not listed, including every verb addendum B lists // as never granted, is refused. A pair missing here that a role really // needs gets added after a probe shows Vikunja accepts it. export const VIKUNJA_GRANTABLE = Object.freeze({ projects: Object.freeze(["read_one", "views_buckets", "views_buckets_tasks", "views_buckets_tasks_get"]), projects_views: Object.freeze(["read_all"]), tasks: Object.freeze(["read_all", "read_one", "create", "update"]), tasks_comments: Object.freeze(["read_all", "create"]), tasks_assignees: Object.freeze(["create", "delete"]), tasks_relations: Object.freeze(["create", "delete"]), tasks_labels: Object.freeze(["create", "delete"]), labels: Object.freeze(["read_all"]), }); // Model families the launch block may cap, and the PRD's ceiling for each // (REQ-LAUNCH-1: at most 4 Opus and 4 Sonnet sessions at once). export const LAUNCH_CEILING = Object.freeze({ opus: 4, sonnet: 4 }); export const ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;