Darkwing's round 2 candidate, approved by Filbert (#1518 comment 26730). build-r2.patch a27890d5, manifest 869168c7, 34 files, applied on HEAD and checked 34/34. Integration gate on an export of HEAD plus the patch: business 60/60 on Node 24 and 26, every package test and every scripts/test-*.sh green, test-task 98/98 with the live-provider cases. Conductor, queue, conversation and discord confirmed in git worktrees of HEAD with and without the patch, identical results. Lead decision 63 accepts the vocabulary location, the example path and the business branch. Co-Authored-By: Claude Opus 5.5 <[email protected]>
89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
// 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 <read|write>:<category>. "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}$/;
|