@jterrats/open-orchestra 0.4.2-beta.1 → 0.5.0-beta.0
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/AGENTS.md +5 -3
- package/README.md +29 -5
- package/dist/advisory-artifacts.d.ts +20 -0
- package/dist/advisory-artifacts.js +136 -0
- package/dist/advisory-artifacts.js.map +1 -0
- package/dist/assets/web-console.js +436 -4
- package/dist/cli.js +16 -117
- package/dist/cli.js.map +1 -1
- package/dist/command-manifest.d.ts +6 -0
- package/dist/command-manifest.js +141 -43
- package/dist/command-manifest.js.map +1 -1
- package/dist/command-utils.d.ts +5 -0
- package/dist/command-utils.js +23 -0
- package/dist/command-utils.js.map +1 -1
- package/dist/commands.d.ts +7 -42
- package/dist/commands.js +214 -1356
- package/dist/commands.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/context-budget.d.ts +4 -0
- package/dist/context-budget.js +119 -0
- package/dist/context-budget.js.map +1 -0
- package/dist/delivery-commands.d.ts +10 -0
- package/dist/delivery-commands.js +152 -0
- package/dist/delivery-commands.js.map +1 -0
- package/dist/github.d.ts +50 -1
- package/dist/github.js +234 -0
- package/dist/github.js.map +1 -1
- package/dist/health-checks.d.ts +1 -0
- package/dist/health-checks.js +11 -1
- package/dist/health-checks.js.map +1 -1
- package/dist/health-commands.js +2 -0
- package/dist/health-commands.js.map +1 -1
- package/dist/mcp-oauth-proxy.d.ts +32 -0
- package/dist/mcp-oauth-proxy.js +120 -0
- package/dist/mcp-oauth-proxy.js.map +1 -1
- package/dist/memory.d.ts +2 -1
- package/dist/memory.js +71 -10
- package/dist/memory.js.map +1 -1
- package/dist/package-update-check.d.ts +5 -1
- package/dist/package-update-check.js +20 -8
- package/dist/package-update-check.js.map +1 -1
- package/dist/planning-commands.d.ts +14 -0
- package/dist/planning-commands.js +372 -0
- package/dist/planning-commands.js.map +1 -0
- package/dist/release-candidate.d.ts +2 -0
- package/dist/release-candidate.js +9 -14
- package/dist/release-candidate.js.map +1 -1
- package/dist/release-commands.d.ts +2 -0
- package/dist/release-commands.js +58 -6
- package/dist/release-commands.js.map +1 -1
- package/dist/release-readiness.d.ts +49 -0
- package/dist/release-readiness.js +172 -0
- package/dist/release-readiness.js.map +1 -0
- package/dist/runtime-commands.js +11 -4
- package/dist/runtime-commands.js.map +1 -1
- package/dist/runtime-execution-renderer.js +2 -0
- package/dist/runtime-execution-renderer.js.map +1 -1
- package/dist/runtime-execution.d.ts +4 -2
- package/dist/runtime-execution.js +11 -4
- package/dist/runtime-execution.js.map +1 -1
- package/dist/setup-agents-import.d.ts +42 -0
- package/dist/setup-agents-import.js +335 -0
- package/dist/setup-agents-import.js.map +1 -0
- package/dist/skills-catalog-service.d.ts +2 -0
- package/dist/skills-catalog-service.js +8 -0
- package/dist/skills-catalog-service.js.map +1 -0
- package/dist/skills-catalog.d.ts +2 -0
- package/dist/skills-catalog.js +389 -0
- package/dist/skills-catalog.js.map +1 -0
- package/dist/skills-commands.js +1 -11
- package/dist/skills-commands.js.map +1 -1
- package/dist/skills-events.d.ts +9 -0
- package/dist/skills-events.js +50 -0
- package/dist/skills-events.js.map +1 -0
- package/dist/skills-memory.d.ts +18 -0
- package/dist/skills-memory.js +127 -0
- package/dist/skills-memory.js.map +1 -0
- package/dist/skills-planning.d.ts +2 -0
- package/dist/skills-planning.js +87 -0
- package/dist/skills-planning.js.map +1 -0
- package/dist/skills-render.d.ts +14 -0
- package/dist/skills-render.js +83 -0
- package/dist/skills-render.js.map +1 -0
- package/dist/skills-validation.d.ts +2 -0
- package/dist/skills-validation.js +49 -0
- package/dist/skills-validation.js.map +1 -0
- package/dist/skills.d.ts +6 -42
- package/dist/skills.js +6 -773
- package/dist/skills.js.map +1 -1
- package/dist/task-graph-commands.d.ts +14 -0
- package/dist/task-graph-commands.js +367 -0
- package/dist/task-graph-commands.js.map +1 -0
- package/dist/tool-commands.js +8 -0
- package/dist/tool-commands.js.map +1 -1
- package/dist/types/context.d.ts +12 -0
- package/dist/types/context.js +2 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/metrics.d.ts +114 -0
- package/dist/types/metrics.js +2 -0
- package/dist/types/metrics.js.map +1 -0
- package/dist/types/model-config.d.ts +212 -0
- package/dist/types/model-config.js +2 -0
- package/dist/types/model-config.js.map +1 -0
- package/dist/types/runtime.d.ts +93 -0
- package/dist/types/runtime.js +2 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/skills.d.ts +147 -0
- package/dist/types/skills.js +2 -0
- package/dist/types/skills.js.map +1 -0
- package/dist/types/tasks.d.ts +171 -0
- package/dist/types/tasks.js +2 -0
- package/dist/types/tasks.js.map +1 -0
- package/dist/types/workflow-run.d.ts +79 -0
- package/dist/types/workflow-run.js +2 -0
- package/dist/types/workflow-run.js.map +1 -0
- package/dist/types.d.ts +13 -798
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/upgrade-commands.d.ts +2 -0
- package/dist/upgrade-commands.js +65 -0
- package/dist/upgrade-commands.js.map +1 -0
- package/dist/web-api-read-routes.d.ts +5 -0
- package/dist/web-api-read-routes.js +37 -0
- package/dist/web-api-read-routes.js.map +1 -0
- package/dist/web-api.d.ts +1 -3
- package/dist/web-api.js +145 -44
- package/dist/web-api.js.map +1 -1
- package/dist/web-console-sections.d.ts +2 -0
- package/dist/web-console-sections.js +7 -0
- package/dist/web-console-sections.js.map +1 -0
- package/dist/web-console.js +23 -3
- package/dist/web-console.js.map +1 -1
- package/dist/workflow-approval-service.d.ts +9 -0
- package/dist/workflow-approval-service.js +126 -0
- package/dist/workflow-approval-service.js.map +1 -0
- package/dist/workflow-approval-utils.d.ts +10 -0
- package/dist/workflow-approval-utils.js +82 -0
- package/dist/workflow-approval-utils.js.map +1 -0
- package/dist/workflow-budget-utils.d.ts +7 -0
- package/dist/workflow-budget-utils.js +96 -0
- package/dist/workflow-budget-utils.js.map +1 -0
- package/dist/workflow-evidence-service.d.ts +7 -0
- package/dist/workflow-evidence-service.js +100 -0
- package/dist/workflow-evidence-service.js.map +1 -0
- package/dist/workflow-run-commands.d.ts +8 -0
- package/dist/workflow-run-commands.js +479 -0
- package/dist/workflow-run-commands.js.map +1 -0
- package/dist/workflow-services.d.ts +8 -18
- package/dist/workflow-services.js +30 -481
- package/dist/workflow-services.js.map +1 -1
- package/dist/workflow-summary-service.d.ts +4 -0
- package/dist/workflow-summary-service.js +82 -0
- package/dist/workflow-summary-service.js.map +1 -0
- package/dist/workflow-templates.d.ts +1 -0
- package/dist/workflow-templates.js +1 -0
- package/dist/workflow-templates.js.map +1 -1
- package/dist/workspace.d.ts +18 -1
- package/dist/workspace.js +72 -4
- package/dist/workspace.js.map +1 -1
- package/docs/command-contracts.md +22 -0
- package/docs/mcp-oauth-proxy-evaluation.md +14 -0
- package/docs/orchestra-mvp.md +158 -114
- package/docs/package-naming.md +20 -0
- package/docs/persona-workflows.md +209 -0
- package/docs/runtime-adapters.md +19 -14
- package/docs/runtime-llm-flow.md +29 -28
- package/docs/setup-agents-bridge.md +61 -0
- package/docs/traceability-flow.md +89 -0
- package/package.json +9 -7
|
@@ -25,6 +25,7 @@ var endpoints = [
|
|
|
25
25
|
["usage", "/api/usage"],
|
|
26
26
|
["budget", "/api/budget"],
|
|
27
27
|
["tasks", "/api/tasks"],
|
|
28
|
+
["workflowRuns", "/api/workflow/runs"],
|
|
28
29
|
["evidenceView", "/api/evidence/view"],
|
|
29
30
|
["roleActivation", "/api/roles/activation"],
|
|
30
31
|
["skills", "/api/skills"],
|
|
@@ -35,11 +36,15 @@ var endpoints = [
|
|
|
35
36
|
];
|
|
36
37
|
var statePill = document.querySelector("#load-state");
|
|
37
38
|
var lastUpdated = document.querySelector("#last-updated");
|
|
39
|
+
var adoptionGuide = document.querySelector("#adoption-guide");
|
|
40
|
+
var releaseReadiness = document.querySelector("#release-readiness");
|
|
38
41
|
var metrics = document.querySelector("#metrics");
|
|
39
42
|
var validation = document.querySelector("#validation");
|
|
40
43
|
var workspaceView = document.querySelector("#workspace-view");
|
|
41
44
|
var graph = document.querySelector("#graph");
|
|
42
45
|
var actions = document.querySelector("#actions");
|
|
46
|
+
var taskDetailSelect = document.querySelector("#task-detail-select");
|
|
47
|
+
var taskDetail = document.querySelector("#task-detail");
|
|
43
48
|
var taskOwner = document.querySelector("#task-owner");
|
|
44
49
|
var charts = document.querySelector("#charts");
|
|
45
50
|
var evidenceTask = document.querySelector("#evidence-task");
|
|
@@ -47,6 +52,8 @@ var evidenceType = document.querySelector("#evidence-type");
|
|
|
47
52
|
var evidenceList = document.querySelector("#evidence-list");
|
|
48
53
|
var rolesView = document.querySelector("#roles-view");
|
|
49
54
|
var planningView = document.querySelector("#planning-view");
|
|
55
|
+
var workflowTask = document.querySelector("#workflow-task");
|
|
56
|
+
var workflowRuns = document.querySelector("#workflow-runs");
|
|
50
57
|
var costView = document.querySelector("#cost-view");
|
|
51
58
|
var playwrightTask = document.querySelector("#playwright-task");
|
|
52
59
|
var playwrightPlan = document.querySelector("#playwright-plan");
|
|
@@ -79,6 +86,10 @@ document.querySelector("#skills-render").addEventListener("click", loadSkillRend
|
|
|
79
86
|
document.querySelector("#lesson-add").addEventListener("click", addLesson);
|
|
80
87
|
document.querySelector("#lesson-promote").addEventListener("click", promoteLessons);
|
|
81
88
|
document.querySelector("#task-add").addEventListener("click", addTaskFromWeb);
|
|
89
|
+
document.querySelector("#task-detail-refresh").addEventListener("click", loadTaskDetail);
|
|
90
|
+
taskDetailSelect.addEventListener("change", loadTaskDetail);
|
|
91
|
+
document.querySelector("#workflow-start").addEventListener("click", startWorkflowFromWeb);
|
|
92
|
+
workflowRuns.addEventListener("click", handleWorkflowAction);
|
|
82
93
|
loadDashboard();
|
|
83
94
|
function applyStoredTheme() {
|
|
84
95
|
const stored = localStorage.getItem("open-orchestra-theme");
|
|
@@ -129,15 +140,20 @@ function render(data) {
|
|
|
129
140
|
metric(data.validation.valid ? "Valid" : "Needs work", "Workspace")
|
|
130
141
|
]);
|
|
131
142
|
renderValidation(data.validation);
|
|
143
|
+
renderAdoptionGuide(data);
|
|
144
|
+
renderReleaseReadiness(data);
|
|
132
145
|
renderWorkspace(data.workspaceClassification, data.runtimeAdapters);
|
|
133
146
|
renderGraph(data.graph);
|
|
134
147
|
renderActions(data);
|
|
148
|
+
renderTaskDetailControls(data.tasks);
|
|
135
149
|
renderTaskWriteControls(data.roles);
|
|
136
150
|
renderCharts(data);
|
|
137
151
|
renderEvidenceControls(data);
|
|
138
152
|
renderEvidence(data.evidenceView);
|
|
139
153
|
renderRoles(data.roleActivation);
|
|
140
154
|
renderPlanning(data.roleActivation);
|
|
155
|
+
renderWorkflowControls(data.tasks, data.workflowRuns);
|
|
156
|
+
renderWorkflowRuns(data.workflowRuns);
|
|
141
157
|
renderCost(data.usage, data.budget);
|
|
142
158
|
renderPlaywrightTasks(data.tasks);
|
|
143
159
|
renderSkillControls(data);
|
|
@@ -146,6 +162,149 @@ function render(data) {
|
|
|
146
162
|
renderLessons(data.lessons);
|
|
147
163
|
loadPlaywrightPlan();
|
|
148
164
|
loadSkillPlan();
|
|
165
|
+
loadTaskDetail();
|
|
166
|
+
}
|
|
167
|
+
function renderTaskDetailControls(tasks) {
|
|
168
|
+
const currentValue = taskDetailSelect.value;
|
|
169
|
+
replace(
|
|
170
|
+
taskDetailSelect,
|
|
171
|
+
tasks.length > 0 ? tasks.map(function(task) {
|
|
172
|
+
return option(task.id, task.id + " - " + task.title);
|
|
173
|
+
}) : [option("", "No tasks")]
|
|
174
|
+
);
|
|
175
|
+
if (currentValue) {
|
|
176
|
+
taskDetailSelect.value = currentValue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function loadTaskDetail() {
|
|
180
|
+
if (!taskDetailSelect.value) {
|
|
181
|
+
replace(taskDetail, [
|
|
182
|
+
empty(
|
|
183
|
+
"No task selected",
|
|
184
|
+
"Create or select a task to inspect acceptance, evidence, and gates."
|
|
185
|
+
)
|
|
186
|
+
]);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(
|
|
191
|
+
"/api/task/detail?task=" + encodeURIComponent(taskDetailSelect.value)
|
|
192
|
+
);
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
throw new Error("task detail returned " + response.status);
|
|
195
|
+
}
|
|
196
|
+
renderTaskDetail(await response.json());
|
|
197
|
+
} catch (error) {
|
|
198
|
+
replace(taskDetail, [
|
|
199
|
+
empty("Task detail unavailable", String(error.message || error))
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function renderTaskDetail(detail) {
|
|
204
|
+
const context = detail.context;
|
|
205
|
+
const task = context.task;
|
|
206
|
+
const releaseReadiness2 = detail.releaseReadiness;
|
|
207
|
+
const rows = document.createElement("div");
|
|
208
|
+
rows.className = "detail-grid";
|
|
209
|
+
rows.append(
|
|
210
|
+
detailBlock("Summary", [
|
|
211
|
+
task.id + " [" + task.status + "] " + task.title,
|
|
212
|
+
"Owner: " + task.ownerRole,
|
|
213
|
+
"Backlog: " + (task.backlogItem || "missing")
|
|
214
|
+
]),
|
|
215
|
+
detailBlock("Acceptance Criteria", task.acceptanceCriteria || []),
|
|
216
|
+
detailBlock("Definition of Ready", readinessLines(context)),
|
|
217
|
+
detailBlock("Definition of Done", releaseReadinessLines(releaseReadiness2)),
|
|
218
|
+
detailBlock(
|
|
219
|
+
"AC Coverage",
|
|
220
|
+
releaseCoverageLines(detail.releaseReadinessReport)
|
|
221
|
+
),
|
|
222
|
+
detailBlock("Risks", task.risks || []),
|
|
223
|
+
detailBlock("Handoffs", eventLines(context.handoffs)),
|
|
224
|
+
detailBlock("Reviews", eventLines(context.reviews)),
|
|
225
|
+
detailBlock("Evidence", eventLines(context.evidence)),
|
|
226
|
+
detailBlock("Gates", eventLines(context.gates)),
|
|
227
|
+
detailBlock("Next Actions", taskNextActions(context, releaseReadiness2))
|
|
228
|
+
);
|
|
229
|
+
replace(taskDetail, [rows]);
|
|
230
|
+
}
|
|
231
|
+
function releaseCoverageLines(report) {
|
|
232
|
+
if (!report || !Array.isArray(report.acceptanceCoverage)) {
|
|
233
|
+
return ["Coverage unavailable"];
|
|
234
|
+
}
|
|
235
|
+
if (report.acceptanceCoverage.length === 0) {
|
|
236
|
+
return ["No acceptance criteria"];
|
|
237
|
+
}
|
|
238
|
+
return report.acceptanceCoverage.map(function(coverage) {
|
|
239
|
+
const status = coverage.covered ? "covered" : "missing";
|
|
240
|
+
const suffix = coverage.missing && coverage.missing.length > 0 ? " (" + coverage.missing.join(", ") + ")" : "";
|
|
241
|
+
return coverage.criterion + ": " + status + suffix;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function readinessLines(context) {
|
|
245
|
+
const missing = [];
|
|
246
|
+
[
|
|
247
|
+
"backlogItem",
|
|
248
|
+
"goal",
|
|
249
|
+
"scope",
|
|
250
|
+
"acceptanceCriteria",
|
|
251
|
+
"assumptions",
|
|
252
|
+
"risks",
|
|
253
|
+
"testStrategy"
|
|
254
|
+
].forEach(function(field) {
|
|
255
|
+
const value = context.task[field];
|
|
256
|
+
if (Array.isArray(value) ? value.length === 0 : !value) {
|
|
257
|
+
missing.push(field);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
return missing.length === 0 ? ["Ready for implementation"] : missing.map(function(field) {
|
|
261
|
+
return "Missing " + field;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function releaseReadinessLines(releaseReadiness2) {
|
|
265
|
+
if (releaseReadiness2.passed) {
|
|
266
|
+
return ["Release readiness passed"];
|
|
267
|
+
}
|
|
268
|
+
return releaseReadiness2.missing.map(function(field) {
|
|
269
|
+
return "Missing " + field;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function taskNextActions(context, releaseReadiness2) {
|
|
273
|
+
const actions2 = [];
|
|
274
|
+
if ((context.task.acceptanceCriteria || []).length === 0) {
|
|
275
|
+
actions2.push("Add acceptance criteria before implementation.");
|
|
276
|
+
}
|
|
277
|
+
if (context.handoffs.length === 0) {
|
|
278
|
+
actions2.push("Create the next role handoff when work changes owner.");
|
|
279
|
+
}
|
|
280
|
+
if (context.evidence.length === 0) {
|
|
281
|
+
actions2.push(
|
|
282
|
+
"Attach command, report, screenshot, trace, or video evidence."
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (!releaseReadiness2.passed) {
|
|
286
|
+
actions2.push("Resolve release-readiness missing fields.");
|
|
287
|
+
}
|
|
288
|
+
return actions2.length > 0 ? actions2 : ["No task-specific action required."];
|
|
289
|
+
}
|
|
290
|
+
function eventLines(events) {
|
|
291
|
+
return events.length > 0 ? events.map(function(event) {
|
|
292
|
+
return event.actor + ": " + event.summary;
|
|
293
|
+
}) : ["none"];
|
|
294
|
+
}
|
|
295
|
+
function detailBlock(title, lines) {
|
|
296
|
+
const block = document.createElement("section");
|
|
297
|
+
block.className = "detail-block";
|
|
298
|
+
const heading = document.createElement("h3");
|
|
299
|
+
heading.textContent = title;
|
|
300
|
+
const list = document.createElement("ul");
|
|
301
|
+
(lines.length > 0 ? lines : ["none"]).forEach(function(line) {
|
|
302
|
+
const item = document.createElement("li");
|
|
303
|
+
item.textContent = line;
|
|
304
|
+
list.append(item);
|
|
305
|
+
});
|
|
306
|
+
block.append(heading, list);
|
|
307
|
+
return block;
|
|
149
308
|
}
|
|
150
309
|
function renderTaskWriteControls(roles) {
|
|
151
310
|
replace(
|
|
@@ -187,6 +346,135 @@ async function addTaskFromWeb() {
|
|
|
187
346
|
});
|
|
188
347
|
await loadDashboard();
|
|
189
348
|
}
|
|
349
|
+
function renderAdoptionGuide(data) {
|
|
350
|
+
const next = determinePrimaryNextAction(data);
|
|
351
|
+
const container = document.createElement("div");
|
|
352
|
+
const lead = document.createElement("p");
|
|
353
|
+
lead.textContent = next.summary;
|
|
354
|
+
const commands = document.createElement("div");
|
|
355
|
+
commands.className = "command-list";
|
|
356
|
+
next.commands.forEach(function(command) {
|
|
357
|
+
const chip = document.createElement("code");
|
|
358
|
+
chip.className = "command-chip";
|
|
359
|
+
chip.textContent = command;
|
|
360
|
+
commands.append(chip);
|
|
361
|
+
});
|
|
362
|
+
const steps = document.createElement("ol");
|
|
363
|
+
steps.className = "step-list";
|
|
364
|
+
next.steps.forEach(function(step, index) {
|
|
365
|
+
const item = document.createElement("li");
|
|
366
|
+
const number = document.createElement("b");
|
|
367
|
+
number.textContent = String(index + 1);
|
|
368
|
+
const text = document.createElement("span");
|
|
369
|
+
text.textContent = step;
|
|
370
|
+
item.append(number, text);
|
|
371
|
+
steps.append(item);
|
|
372
|
+
});
|
|
373
|
+
container.append(lead, commands, steps);
|
|
374
|
+
replace(adoptionGuide, [container]);
|
|
375
|
+
}
|
|
376
|
+
function determinePrimaryNextAction(data) {
|
|
377
|
+
if (!data.validation.valid) {
|
|
378
|
+
return {
|
|
379
|
+
summary: "Resolve workspace validation before starting or releasing work.",
|
|
380
|
+
commands: ["orchestra validate", "orchestra health --json"],
|
|
381
|
+
steps: [
|
|
382
|
+
"Inspect validation errors and warnings.",
|
|
383
|
+
"Fix missing workflow files, invalid task references, or stale locks.",
|
|
384
|
+
"Refresh the console after validation passes."
|
|
385
|
+
]
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (data.status.tasks.total === 0) {
|
|
389
|
+
return {
|
|
390
|
+
summary: "Create the first governed task with an owner and acceptance criteria.",
|
|
391
|
+
commands: [
|
|
392
|
+
'orchestra task add --id STORY-001 --title "First story" --owner developer --acceptance "verified outcome"',
|
|
393
|
+
"orchestra context --task STORY-001 --json"
|
|
394
|
+
],
|
|
395
|
+
steps: [
|
|
396
|
+
"Add a task from the console or CLI.",
|
|
397
|
+
"Confirm the owner role and acceptance criteria.",
|
|
398
|
+
"Render context before implementation starts."
|
|
399
|
+
]
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
if (data.graph.ready.length > 0) {
|
|
403
|
+
const task = data.graph.ready[0];
|
|
404
|
+
return {
|
|
405
|
+
summary: task.id + " is ready. Start with context, memory, and planning.",
|
|
406
|
+
commands: [
|
|
407
|
+
"orchestra context --task " + task.id + " --json",
|
|
408
|
+
"orchestra memory hook --point before_plan --task " + task.id + " --json",
|
|
409
|
+
"orchestra workflow run --task " + task.id + " --dry-run --gates phase"
|
|
410
|
+
],
|
|
411
|
+
steps: [
|
|
412
|
+
"Review task context and relevant memory.",
|
|
413
|
+
"Confirm role plan and workflow gates.",
|
|
414
|
+
"Start the workflow when the plan matches the work."
|
|
415
|
+
]
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (data.approvals.length > 0) {
|
|
419
|
+
return {
|
|
420
|
+
summary: "There are approval artifacts waiting for a release or workflow decision.",
|
|
421
|
+
commands: ["orchestra approvals list --json", "orchestra summary --json"],
|
|
422
|
+
steps: [
|
|
423
|
+
"Review each pending approval artifact.",
|
|
424
|
+
"Approve, reject, or capture accepted risk.",
|
|
425
|
+
"Resume the paused workflow after approval."
|
|
426
|
+
]
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
summary: "The workspace has no immediate blocking action.",
|
|
431
|
+
commands: ["orchestra summary --json", "orchestra release check --json"],
|
|
432
|
+
steps: [
|
|
433
|
+
"Review summary for stale evidence or locks.",
|
|
434
|
+
"Run release check before tagging.",
|
|
435
|
+
"Record smoke and rollback evidence when preparing a release."
|
|
436
|
+
]
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function renderReleaseReadiness(data) {
|
|
440
|
+
const evidenceCount = data.evidence.length;
|
|
441
|
+
const approvalCount = data.approvals.length;
|
|
442
|
+
const rows = [
|
|
443
|
+
readinessRow(
|
|
444
|
+
data.validation.valid,
|
|
445
|
+
"Workspace validation",
|
|
446
|
+
data.validation.valid ? "Valid" : "Needs fixes"
|
|
447
|
+
),
|
|
448
|
+
readinessRow(
|
|
449
|
+
data.graph.blocked.length === 0 && data.graph.locked.length === 0,
|
|
450
|
+
"Task flow",
|
|
451
|
+
data.graph.blocked.length + " blocked, " + data.graph.locked.length + " locked"
|
|
452
|
+
),
|
|
453
|
+
readinessRow(evidenceCount > 0, "Evidence", evidenceCount + " artifact(s)"),
|
|
454
|
+
readinessRow(
|
|
455
|
+
approvalCount === 0,
|
|
456
|
+
"Approvals",
|
|
457
|
+
approvalCount + " pending artifact(s)"
|
|
458
|
+
),
|
|
459
|
+
readinessRow(
|
|
460
|
+
data.budget.passed,
|
|
461
|
+
"Budget",
|
|
462
|
+
data.budget.passed ? "Within configured limits" : data.budget.violations.length + " violation(s)"
|
|
463
|
+
)
|
|
464
|
+
];
|
|
465
|
+
replace(releaseReadiness, rows);
|
|
466
|
+
}
|
|
467
|
+
function readinessRow(ok, title, detail) {
|
|
468
|
+
const node = listItem(title, detail);
|
|
469
|
+
const tag = document.createElement("span");
|
|
470
|
+
tag.className = ok ? "tag ok" : "tag warn";
|
|
471
|
+
tag.textContent = ok ? "ready" : "attention";
|
|
472
|
+
const row = document.createElement("div");
|
|
473
|
+
row.className = "tag-row";
|
|
474
|
+
row.append(tag);
|
|
475
|
+
node.append(row);
|
|
476
|
+
return node;
|
|
477
|
+
}
|
|
190
478
|
function renderWorkspace(classification, adapters) {
|
|
191
479
|
const adapterNames = adapters.map(function(adapter) {
|
|
192
480
|
return adapter.label;
|
|
@@ -273,6 +561,138 @@ function renderChart(current, canvasId, type, slices) {
|
|
|
273
561
|
}
|
|
274
562
|
});
|
|
275
563
|
}
|
|
564
|
+
function renderWorkflowControls(tasks, runs) {
|
|
565
|
+
const currentValue = workflowTask.value;
|
|
566
|
+
replace(
|
|
567
|
+
workflowTask,
|
|
568
|
+
tasks.length > 0 ? tasks.map(function(task) {
|
|
569
|
+
return option(task.id, task.id);
|
|
570
|
+
}) : [option("", "No tasks")]
|
|
571
|
+
);
|
|
572
|
+
if (currentValue) {
|
|
573
|
+
workflowTask.value = currentValue;
|
|
574
|
+
}
|
|
575
|
+
const runningTaskIds = new Set(
|
|
576
|
+
runs.filter(function(run) {
|
|
577
|
+
return run.status === "running" || run.status === "paused";
|
|
578
|
+
}).map(function(run) {
|
|
579
|
+
return run.taskId;
|
|
580
|
+
})
|
|
581
|
+
);
|
|
582
|
+
document.querySelector("#workflow-start").disabled = !workflowTask.value || runningTaskIds.has(workflowTask.value);
|
|
583
|
+
}
|
|
584
|
+
function renderWorkflowRuns(runs) {
|
|
585
|
+
replace(
|
|
586
|
+
workflowRuns,
|
|
587
|
+
runs.length > 0 ? runs.slice(0, 8).map(workflowRunItem) : [
|
|
588
|
+
empty(
|
|
589
|
+
"No workflow runs yet",
|
|
590
|
+
"Start a task workflow to track phases, gates, and handoffs."
|
|
591
|
+
)
|
|
592
|
+
]
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
function workflowRunItem(run) {
|
|
596
|
+
const phases = (run.phases || []).map(function(phase) {
|
|
597
|
+
return phase.phase + ":" + phase.status;
|
|
598
|
+
}).join(" -> ");
|
|
599
|
+
const node = listItem(
|
|
600
|
+
run.id + " [" + run.status + "]",
|
|
601
|
+
run.taskId + " - gates " + run.gates + (phases ? " - " + phases : "")
|
|
602
|
+
);
|
|
603
|
+
const pausedGate = [...run.phases || []].reverse().find(function(phase) {
|
|
604
|
+
return phase.status === "gate_paused";
|
|
605
|
+
});
|
|
606
|
+
const row = document.createElement("div");
|
|
607
|
+
row.className = "tag-row";
|
|
608
|
+
if (pausedGate) {
|
|
609
|
+
const approve = document.createElement("button");
|
|
610
|
+
approve.className = "text-button";
|
|
611
|
+
approve.type = "button";
|
|
612
|
+
approve.dataset.workflowAction = "approve";
|
|
613
|
+
approve.dataset.run = run.id;
|
|
614
|
+
approve.dataset.task = run.taskId;
|
|
615
|
+
approve.dataset.gate = pausedGate.gateId || "";
|
|
616
|
+
approve.textContent = "Approve Gate";
|
|
617
|
+
row.append(approve);
|
|
618
|
+
}
|
|
619
|
+
if (run.status === "paused") {
|
|
620
|
+
const resume = document.createElement("button");
|
|
621
|
+
resume.className = "text-button";
|
|
622
|
+
resume.type = "button";
|
|
623
|
+
resume.dataset.workflowAction = "resume";
|
|
624
|
+
resume.dataset.run = run.id;
|
|
625
|
+
resume.dataset.task = run.taskId;
|
|
626
|
+
resume.textContent = "Resume";
|
|
627
|
+
row.append(resume);
|
|
628
|
+
}
|
|
629
|
+
node.append(row);
|
|
630
|
+
return node;
|
|
631
|
+
}
|
|
632
|
+
async function startWorkflowFromWeb() {
|
|
633
|
+
if (!workflowTask.value) {
|
|
634
|
+
setLoading("Select a task to start workflow", "warn");
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const response = await fetch("/api/workflow/start", {
|
|
638
|
+
method: "POST",
|
|
639
|
+
headers: { "content-type": "application/json" },
|
|
640
|
+
body: JSON.stringify({
|
|
641
|
+
task: workflowTask.value,
|
|
642
|
+
gates: document.querySelector("#workflow-gates").value
|
|
643
|
+
})
|
|
644
|
+
});
|
|
645
|
+
if (!response.ok) {
|
|
646
|
+
setLoading("Workflow start failed", "warn");
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
await loadDashboard();
|
|
650
|
+
}
|
|
651
|
+
async function handleWorkflowAction(event) {
|
|
652
|
+
const button = event.target.closest("[data-workflow-action]");
|
|
653
|
+
if (!button) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (button.dataset.workflowAction === "approve") {
|
|
657
|
+
await approveWorkflowGateFromWeb(button);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (button.dataset.workflowAction === "resume") {
|
|
661
|
+
await resumeWorkflowFromWeb(button);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function approveWorkflowGateFromWeb(button) {
|
|
665
|
+
const response = await fetch("/api/workflow/gate-approve", {
|
|
666
|
+
method: "POST",
|
|
667
|
+
headers: { "content-type": "application/json" },
|
|
668
|
+
body: JSON.stringify({
|
|
669
|
+
run: button.dataset.run,
|
|
670
|
+
gate: button.dataset.gate,
|
|
671
|
+
approver: document.querySelector("#workflow-approver").value,
|
|
672
|
+
rationale: document.querySelector("#workflow-rationale").value
|
|
673
|
+
})
|
|
674
|
+
});
|
|
675
|
+
if (!response.ok) {
|
|
676
|
+
setLoading("Gate approval failed", "warn");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
await loadDashboard();
|
|
680
|
+
}
|
|
681
|
+
async function resumeWorkflowFromWeb(button) {
|
|
682
|
+
const response = await fetch("/api/workflow/resume", {
|
|
683
|
+
method: "POST",
|
|
684
|
+
headers: { "content-type": "application/json" },
|
|
685
|
+
body: JSON.stringify({
|
|
686
|
+
task: button.dataset.task,
|
|
687
|
+
run: button.dataset.run
|
|
688
|
+
})
|
|
689
|
+
});
|
|
690
|
+
if (!response.ok) {
|
|
691
|
+
setLoading("Workflow resume failed", "warn");
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
await loadDashboard();
|
|
695
|
+
}
|
|
276
696
|
function renderCost(usage, budget) {
|
|
277
697
|
const rows = [
|
|
278
698
|
listItem("Requests", String(usage.totals.requests)),
|
|
@@ -723,10 +1143,7 @@ function renderGraph(plan) {
|
|
|
723
1143
|
["Complete", plan.complete]
|
|
724
1144
|
].flatMap(function(group) {
|
|
725
1145
|
return group[1].slice(0, 4).map(function(item) {
|
|
726
|
-
return listItem(
|
|
727
|
-
item.title,
|
|
728
|
-
group[0] + " - " + item.id + " - " + item.ownerRole
|
|
729
|
-
);
|
|
1146
|
+
return listItem(item.title, graphItemDetail(group[0], item));
|
|
730
1147
|
});
|
|
731
1148
|
});
|
|
732
1149
|
replace(
|
|
@@ -739,6 +1156,20 @@ function renderGraph(plan) {
|
|
|
739
1156
|
]
|
|
740
1157
|
);
|
|
741
1158
|
}
|
|
1159
|
+
function graphItemDetail(group, item) {
|
|
1160
|
+
const base = group + " - " + item.id + " - " + item.ownerRole;
|
|
1161
|
+
if (group === "Blocked" && item.incomplete?.length > 0) {
|
|
1162
|
+
return base + " - blockers: " + item.incomplete.map(function(dependency) {
|
|
1163
|
+
return dependency.id + " [" + dependency.status + "]";
|
|
1164
|
+
}).join(", ");
|
|
1165
|
+
}
|
|
1166
|
+
if (group === "Locked" && item.locks?.length > 0) {
|
|
1167
|
+
return base + " - locks: " + item.locks.map(function(lock) {
|
|
1168
|
+
return lock.path + " (" + lock.reason + ")";
|
|
1169
|
+
}).join(", ");
|
|
1170
|
+
}
|
|
1171
|
+
return base;
|
|
1172
|
+
}
|
|
742
1173
|
function renderActions(data) {
|
|
743
1174
|
const next = [];
|
|
744
1175
|
if (!data.validation.valid) {
|
|
@@ -826,6 +1257,7 @@ function renderEmpty(title, detail) {
|
|
|
826
1257
|
replace(workspaceView, []);
|
|
827
1258
|
replace(graph, []);
|
|
828
1259
|
replace(actions, []);
|
|
1260
|
+
replace(taskDetail, []);
|
|
829
1261
|
}
|
|
830
1262
|
function setLoading(label, tone) {
|
|
831
1263
|
statePill.textContent = label;
|