arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
254 lines • 9.27 kB
JavaScript
/**
* JSON Schema for YAML ticket validation
*/
export const ticketYamlSchema = {
$schema: "http://json-schema.org/draft-07/schema#",
title: "ARELA Ticket Schema",
description: "Schema for ARELA YAML ticket format with comprehensive metadata support",
type: "object",
required: ["id", "title"],
properties: {
id: {
type: "string",
description: "Unique ticket identifier (e.g., CODEX-001, ARELA-004)",
pattern: "^[A-Z0-9]+-[0-9]+$",
},
title: {
type: "string",
description: "Short, descriptive title for the ticket",
minLength: 3,
maxLength: 200,
},
description: {
type: "string",
description: "Optional longer description",
},
agent: {
type: "string",
description: "Suggested agent for this ticket (codex, claude, deepseek, ollama, cascade)",
enum: ["codex", "claude", "deepseek", "ollama", "cascade"],
},
priority: {
type: "string",
description: "Priority level for the ticket",
enum: ["low", "medium", "high", "critical"],
default: "medium",
},
complexity: {
type: "string",
description: "Complexity estimation for the ticket",
enum: ["simple", "medium", "complex"],
default: "medium",
},
status: {
type: "string",
description: "Current status of the ticket",
enum: ["pending", "in_progress", "completed", "failed", "blocked"],
default: "pending",
},
estimatedTime: {
type: "string",
description: "Estimated time to complete (e.g., 30m, 2h, 1d)",
pattern: "^\\d+[mhd]$",
},
estimatedCost: {
type: "number",
description: "Estimated API cost",
},
context: {
type: "string",
description: "Context and background information for this ticket (supports multiline)",
},
requirements: {
type: "array",
description: "List of requirements for this ticket",
items: {
type: "string",
},
},
acceptance: {
type: "array",
description: "Acceptance criteria with optional test commands",
items: {
type: "object",
properties: {
id: {
type: "string",
description: "Unique identifier for this acceptance criterion",
},
description: {
type: "string",
description: "Description of the acceptance criterion",
},
status: {
type: "string",
description: "Status of this criterion",
enum: ["pending", "passed", "failed"],
default: "pending",
},
test: {
type: "string",
description: "Optional test command to validate this criterion (bash, npm, etc)",
},
},
required: ["description"],
},
},
files: {
type: "array",
description: "Files affected or created by this ticket",
items: {
type: "object",
properties: {
path: {
type: "string",
description: "File path",
},
action: {
type: "string",
description: "Action to perform on this file",
enum: ["create", "modify", "delete"],
},
description: {
type: "string",
description: "What changes will be made to this file",
},
},
required: ["path", "action"],
},
},
dependencies: {
oneOf: [
{
type: "array",
description: "Ticket IDs that must be completed before this one",
items: {
type: "string",
pattern: "^[A-Z0-9]+-[0-9]+$",
},
},
{
type: "string",
description: "Comma-separated list of ticket dependencies",
},
],
},
tags: {
type: "array",
description: "Tags for categorizing this ticket",
items: {
type: "string",
},
},
assignee: {
type: "string",
description: "Person or agent assigned to this ticket",
},
created_at: {
type: "string",
format: "date-time",
description: "ISO 8601 timestamp when ticket was created",
},
updated_at: {
type: "string",
format: "date-time",
description: "ISO 8601 timestamp when ticket was last updated",
},
completed_at: {
type: "string",
format: "date-time",
description: "ISO 8601 timestamp when ticket was completed",
},
},
additionalProperties: true,
};
/**
* Validate a ticket object against the schema
*/
export function validateTicket(ticket) {
const errors = [];
if (typeof ticket !== "object" || ticket === null) {
return { valid: false, errors: ["Ticket must be an object"] };
}
const t = ticket;
// Check required fields
if (!t.id || typeof t.id !== "string") {
errors.push("Missing required field: id");
}
else if (!/^[A-Z0-9]+-[0-9]+$/.test(t.id)) {
errors.push(`Invalid id format: ${t.id} (expected: PREFIX-001)`);
}
if (!t.title || typeof t.title !== "string") {
errors.push("Missing required field: title");
}
else if (t.title.length < 3) {
errors.push("Title must be at least 3 characters");
}
// Check optional enum fields
if (t.agent && !["codex", "claude", "deepseek", "ollama", "cascade"].includes(String(t.agent))) {
errors.push(`Invalid agent: ${t.agent} (must be one of: codex, claude, deepseek, ollama, cascade)`);
}
if (t.priority && !["low", "medium", "high", "critical"].includes(String(t.priority))) {
errors.push(`Invalid priority: ${t.priority}`);
}
if (t.complexity && !["simple", "medium", "complex"].includes(String(t.complexity))) {
errors.push(`Invalid complexity: ${t.complexity}`);
}
if (t.status && !["pending", "in_progress", "completed", "failed", "blocked"].includes(String(t.status))) {
errors.push(`Invalid status: ${t.status}`);
}
// Check estimatedTime format
if (t.estimatedTime && !/^\d+[mhd]$/.test(String(t.estimatedTime))) {
errors.push(`Invalid estimatedTime format: ${t.estimatedTime} (expected: e.g., 30m, 2h, 1d)`);
}
// Check dependencies format
if (t.dependencies) {
if (Array.isArray(t.dependencies)) {
for (const dep of t.dependencies) {
if (typeof dep !== "string" || !/^[A-Z0-9]+-[0-9]+$/.test(dep)) {
errors.push(`Invalid dependency format: ${dep}`);
}
}
}
else if (typeof t.dependencies !== "string") {
errors.push("Dependencies must be an array or comma-separated string");
}
}
// Check acceptance criteria
if (t.acceptance && Array.isArray(t.acceptance)) {
for (let i = 0; i < t.acceptance.length; i++) {
const ac = t.acceptance[i];
if (typeof ac !== "object" || ac === null) {
errors.push(`Invalid acceptance criterion at index ${i}`);
}
else if (!ac.description) {
errors.push(`Acceptance criterion at index ${i} missing description`);
}
}
}
// Check files
if (t.files && Array.isArray(t.files)) {
for (let i = 0; i < t.files.length; i++) {
const file = t.files[i];
if (typeof file !== "object" || file === null) {
errors.push(`Invalid file entry at index ${i}`);
}
else {
if (!file.path) {
errors.push(`File entry at index ${i} missing path`);
}
if (!file.action) {
errors.push(`File entry at index ${i} missing action`);
}
else if (!["create", "modify", "delete"].includes(String(file.action))) {
errors.push(`Invalid file action at index ${i}: ${file.action} (must be: create, modify, or delete)`);
}
}
}
}
return {
valid: errors.length === 0,
errors,
};
}
//# sourceMappingURL=schema.js.map