@jterrats/open-orchestra 0.1.0 → 0.2.1

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.
Files changed (187) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +17 -2
  3. package/dist/assets/web-console.js +743 -0
  4. package/dist/cli.js +157 -4
  5. package/dist/cli.js.map +1 -1
  6. package/dist/collaboration-flows.d.ts +5 -0
  7. package/dist/collaboration-flows.js +256 -0
  8. package/dist/collaboration-flows.js.map +1 -0
  9. package/dist/command-manifest.d.ts +11 -0
  10. package/dist/command-manifest.js +52 -0
  11. package/dist/command-manifest.js.map +1 -0
  12. package/dist/commands.d.ts +31 -0
  13. package/dist/commands.js +644 -2
  14. package/dist/commands.js.map +1 -1
  15. package/dist/constants.d.ts +4 -0
  16. package/dist/constants.js +22 -0
  17. package/dist/constants.js.map +1 -1
  18. package/dist/defaults.d.ts +7 -11
  19. package/dist/defaults.js +7 -625
  20. package/dist/defaults.js.map +1 -1
  21. package/dist/delegation-decision.d.ts +14 -0
  22. package/dist/delegation-decision.js +391 -0
  23. package/dist/delegation-decision.js.map +1 -0
  24. package/dist/detect-commands.d.ts +3 -0
  25. package/dist/detect-commands.js +28 -0
  26. package/dist/detect-commands.js.map +1 -0
  27. package/dist/diagram-validation.d.ts +36 -0
  28. package/dist/diagram-validation.js +118 -0
  29. package/dist/diagram-validation.js.map +1 -0
  30. package/dist/fs-utils.d.ts +2 -0
  31. package/dist/fs-utils.js +75 -6
  32. package/dist/fs-utils.js.map +1 -1
  33. package/dist/health-checks.d.ts +28 -0
  34. package/dist/health-checks.js +219 -0
  35. package/dist/health-checks.js.map +1 -0
  36. package/dist/health-commands.d.ts +2 -0
  37. package/dist/health-commands.js +18 -0
  38. package/dist/health-commands.js.map +1 -0
  39. package/dist/instruction-apply.d.ts +34 -0
  40. package/dist/instruction-apply.js +150 -0
  41. package/dist/instruction-apply.js.map +1 -0
  42. package/dist/instruction-blocks.d.ts +22 -0
  43. package/dist/instruction-blocks.js +120 -0
  44. package/dist/instruction-blocks.js.map +1 -0
  45. package/dist/instruction-imports.d.ts +12 -0
  46. package/dist/instruction-imports.js +45 -0
  47. package/dist/instruction-imports.js.map +1 -0
  48. package/dist/instruction-stale.d.ts +9 -0
  49. package/dist/instruction-stale.js +106 -0
  50. package/dist/instruction-stale.js.map +1 -0
  51. package/dist/instruction-types.d.ts +66 -0
  52. package/dist/instruction-types.js +2 -0
  53. package/dist/instruction-types.js.map +1 -0
  54. package/dist/instruction-updates.d.ts +4 -0
  55. package/dist/instruction-updates.js +5 -0
  56. package/dist/instruction-updates.js.map +1 -0
  57. package/dist/knowledge-base.d.ts +10 -0
  58. package/dist/knowledge-base.js +117 -0
  59. package/dist/knowledge-base.js.map +1 -0
  60. package/dist/mcp-oauth-proxy.d.ts +39 -0
  61. package/dist/mcp-oauth-proxy.js +80 -0
  62. package/dist/mcp-oauth-proxy.js.map +1 -0
  63. package/dist/pr-review.d.ts +20 -0
  64. package/dist/pr-review.js +142 -0
  65. package/dist/pr-review.js.map +1 -0
  66. package/dist/project-detection.d.ts +22 -0
  67. package/dist/project-detection.js +174 -0
  68. package/dist/project-detection.js.map +1 -0
  69. package/dist/prompt-registry.d.ts +56 -0
  70. package/dist/prompt-registry.js +163 -0
  71. package/dist/prompt-registry.js.map +1 -0
  72. package/dist/release-candidate.d.ts +41 -0
  73. package/dist/release-candidate.js +196 -0
  74. package/dist/release-candidate.js.map +1 -0
  75. package/dist/release-commands.d.ts +4 -0
  76. package/dist/release-commands.js +50 -0
  77. package/dist/release-commands.js.map +1 -0
  78. package/dist/roles/ai-support-roles.d.ts +11 -0
  79. package/dist/roles/ai-support-roles.js +67 -0
  80. package/dist/roles/ai-support-roles.js.map +1 -0
  81. package/dist/roles/core-roles.d.ts +11 -0
  82. package/dist/roles/core-roles.js +144 -0
  83. package/dist/roles/core-roles.js.map +1 -0
  84. package/dist/roles/engineering-roles.d.ts +11 -0
  85. package/dist/roles/engineering-roles.js +176 -0
  86. package/dist/roles/engineering-roles.js.map +1 -0
  87. package/dist/roles/governance-roles.d.ts +11 -0
  88. package/dist/roles/governance-roles.js +117 -0
  89. package/dist/roles/governance-roles.js.map +1 -0
  90. package/dist/roles/index.d.ts +11 -0
  91. package/dist/roles/index.js +17 -0
  92. package/dist/roles/index.js.map +1 -0
  93. package/dist/roles/platform-ops-roles.d.ts +11 -0
  94. package/dist/roles/platform-ops-roles.js +158 -0
  95. package/dist/roles/platform-ops-roles.js.map +1 -0
  96. package/dist/roles/qa-ux-roles.d.ts +11 -0
  97. package/dist/roles/qa-ux-roles.js +193 -0
  98. package/dist/roles/qa-ux-roles.js.map +1 -0
  99. package/dist/roles/release-ops-roles.d.ts +11 -0
  100. package/dist/roles/release-ops-roles.js +109 -0
  101. package/dist/roles/release-ops-roles.js.map +1 -0
  102. package/dist/runtime-adapters.d.ts +6 -0
  103. package/dist/runtime-adapters.js +88 -0
  104. package/dist/runtime-adapters.js.map +1 -0
  105. package/dist/runtime-bootstrap.d.ts +12 -0
  106. package/dist/runtime-bootstrap.js +85 -0
  107. package/dist/runtime-bootstrap.js.map +1 -0
  108. package/dist/skills.d.ts +36 -0
  109. package/dist/skills.js +665 -0
  110. package/dist/skills.js.map +1 -0
  111. package/dist/subagent-protocol.d.ts +41 -0
  112. package/dist/subagent-protocol.js +179 -0
  113. package/dist/subagent-protocol.js.map +1 -0
  114. package/dist/telemetry-consent.d.ts +24 -0
  115. package/dist/telemetry-consent.js +95 -0
  116. package/dist/telemetry-consent.js.map +1 -0
  117. package/dist/telemetry-export.d.ts +14 -0
  118. package/dist/telemetry-export.js +126 -0
  119. package/dist/telemetry-export.js.map +1 -0
  120. package/dist/telemetry-records.d.ts +3 -0
  121. package/dist/telemetry-records.js +96 -0
  122. package/dist/telemetry-records.js.map +1 -0
  123. package/dist/telemetry-redaction.d.ts +9 -0
  124. package/dist/telemetry-redaction.js +55 -0
  125. package/dist/telemetry-redaction.js.map +1 -0
  126. package/dist/telemetry-types.d.ts +52 -0
  127. package/dist/telemetry-types.js +2 -0
  128. package/dist/telemetry-types.js.map +1 -0
  129. package/dist/telemetry.d.ts +4 -0
  130. package/dist/telemetry.js +4 -0
  131. package/dist/telemetry.js.map +1 -0
  132. package/dist/types.d.ts +176 -1
  133. package/dist/validation.d.ts +3 -1
  134. package/dist/validation.js +28 -5
  135. package/dist/validation.js.map +1 -1
  136. package/dist/web-api.js +167 -3
  137. package/dist/web-api.js.map +1 -1
  138. package/dist/web-console.js +6 -160
  139. package/dist/web-console.js.map +1 -1
  140. package/dist/workflow-gates.js +4 -2
  141. package/dist/workflow-gates.js.map +1 -1
  142. package/dist/workflow-services.js +125 -67
  143. package/dist/workflow-services.js.map +1 -1
  144. package/dist/workflow-templates.d.ts +10 -0
  145. package/dist/workflow-templates.js +141 -0
  146. package/dist/workflow-templates.js.map +1 -0
  147. package/dist/workspace-classification.d.ts +5 -0
  148. package/dist/workspace-classification.js +127 -0
  149. package/dist/workspace-classification.js.map +1 -0
  150. package/dist/workspace-validator.js +11 -1
  151. package/dist/workspace-validator.js.map +1 -1
  152. package/dist/workspace.d.ts +8 -4
  153. package/dist/workspace.js +111 -4
  154. package/dist/workspace.js.map +1 -1
  155. package/docs/dev-team-specialist-role-profiles.md +171 -0
  156. package/docs/mcp-oauth-proxy-evaluation.md +44 -0
  157. package/docs/multi-agent-orchestrator-backlog.md +413 -1
  158. package/docs/open-orchestra-dogfooding-findings.md +66 -0
  159. package/docs/orchestra-mvp.md +46 -1
  160. package/docs/runtime-adapters.md +86 -0
  161. package/docs/runtime-llm-flow.md +124 -0
  162. package/docs/setup-agents-dogfooding-findings.md +101 -0
  163. package/docs/skill-loading-strategy.md +114 -0
  164. package/docs/source-of-truth-and-agent-learning.md +83 -0
  165. package/package.json +9 -5
  166. package/rules/agent-roles.mdc +30 -0
  167. package/rules/ai-assisted-development.mdc +22 -0
  168. package/skills/agent-learning/SKILL.md +24 -0
  169. package/skills/agent-learning/manifest.json +40 -0
  170. package/skills/backlog-sync/SKILL.md +24 -0
  171. package/skills/backlog-sync/manifest.json +41 -0
  172. package/skills/diagram-export/SKILL.md +35 -0
  173. package/skills/diagram-export/manifest.json +40 -0
  174. package/skills/model-evaluation/SKILL.md +25 -0
  175. package/skills/model-evaluation/manifest.json +41 -0
  176. package/skills/playwright-evidence/SKILL.md +28 -0
  177. package/skills/playwright-evidence/manifest.json +46 -0
  178. package/skills/pr-review/SKILL.md +23 -0
  179. package/skills/pr-review/manifest.json +43 -0
  180. package/skills/prompt-registry/SKILL.md +24 -0
  181. package/skills/prompt-registry/manifest.json +45 -0
  182. package/skills/release-readiness/SKILL.md +25 -0
  183. package/skills/release-readiness/manifest.json +45 -0
  184. package/skills/source-of-truth/SKILL.md +24 -0
  185. package/skills/source-of-truth/manifest.json +47 -0
  186. package/skills/static-analysis/SKILL.md +26 -0
  187. package/skills/static-analysis/manifest.json +46 -0
@@ -0,0 +1,743 @@
1
+ // src/web-chart-contracts.ts
2
+ var GRAPH_STATE_LABELS = ["ready", "blocked", "locked", "complete"];
3
+ function buildDashboardChartData(input) {
4
+ return {
5
+ taskStatus: Object.entries(input.status.tasks.byStatus).map(([label, value]) => ({ label, value })).filter((slice) => slice.value > 0),
6
+ graphState: GRAPH_STATE_LABELS.map((label) => ({
7
+ label,
8
+ value: input.graph[label].length
9
+ })).filter((slice) => slice.value > 0)
10
+ };
11
+ }
12
+
13
+ // src/web-console-client.js
14
+ var endpoints = [
15
+ ["status", "/api/status"],
16
+ ["validation", "/api/validate"],
17
+ ["graph", "/api/graph/plan"],
18
+ ["roles", "/api/roles"],
19
+ ["evidence", "/api/evidence"],
20
+ ["approvals", "/api/approvals"],
21
+ ["tasks", "/api/tasks"],
22
+ ["evidenceView", "/api/evidence/view"],
23
+ ["roleActivation", "/api/roles/activation"],
24
+ ["skills", "/api/skills"],
25
+ ["sources", "/api/sources"],
26
+ ["lessons", "/api/lessons"],
27
+ ["workspaceClassification", "/api/workspace/classification"],
28
+ ["runtimeAdapters", "/api/runtime/adapters"]
29
+ ];
30
+ var statePill = document.querySelector("#load-state");
31
+ var lastUpdated = document.querySelector("#last-updated");
32
+ var metrics = document.querySelector("#metrics");
33
+ var validation = document.querySelector("#validation");
34
+ var workspaceView = document.querySelector("#workspace-view");
35
+ var graph = document.querySelector("#graph");
36
+ var actions = document.querySelector("#actions");
37
+ var charts = document.querySelector("#charts");
38
+ var evidenceTask = document.querySelector("#evidence-task");
39
+ var evidenceType = document.querySelector("#evidence-type");
40
+ var evidenceList = document.querySelector("#evidence-list");
41
+ var rolesView = document.querySelector("#roles-view");
42
+ var planningView = document.querySelector("#planning-view");
43
+ var playwrightTask = document.querySelector("#playwright-task");
44
+ var playwrightPlan = document.querySelector("#playwright-plan");
45
+ var skillsTask = document.querySelector("#skills-task");
46
+ var skillsTarget = document.querySelector("#skills-target");
47
+ var skillsPlanList = document.querySelector("#skills-plan-list");
48
+ var skillsList = document.querySelector("#skills-list");
49
+ var skillsPreview = document.querySelector("#skills-preview");
50
+ var sourcesList = document.querySelector("#sources-list");
51
+ var lessonsList = document.querySelector("#lessons-list");
52
+ var lessonTask = document.querySelector("#lesson-task");
53
+ var lessonPromoteFilter = document.querySelector("#lesson-promote-filter");
54
+ var lessonPromotionResult = document.querySelector(
55
+ "#lesson-promotion-result"
56
+ );
57
+ var taskStatusChart;
58
+ var graphStateChart;
59
+ var themeToggle = document.querySelector("#theme-toggle");
60
+ applyStoredTheme();
61
+ themeToggle.addEventListener("click", toggleTheme);
62
+ document.querySelector("#refresh").addEventListener("click", loadDashboard);
63
+ document.querySelector("#evidence-filter").addEventListener("click", loadEvidence);
64
+ document.querySelector("#playwright-task").addEventListener("change", loadPlaywrightPlan);
65
+ document.querySelector("#playwright-attach").addEventListener("click", attachPlaywrightEvidence);
66
+ document.querySelector("#skills-task").addEventListener("change", loadSkillPlan);
67
+ document.querySelector("#skills-target").addEventListener("change", loadSkillRender);
68
+ document.querySelector("#skills-plan").addEventListener("click", loadSkillPlan);
69
+ document.querySelector("#skills-render").addEventListener("click", loadSkillRender);
70
+ document.querySelector("#lesson-add").addEventListener("click", addLesson);
71
+ document.querySelector("#lesson-promote").addEventListener("click", promoteLessons);
72
+ loadDashboard();
73
+ function applyStoredTheme() {
74
+ const stored = localStorage.getItem("open-orchestra-theme");
75
+ if (stored === "dark" || stored === "light") {
76
+ document.documentElement.dataset.theme = stored;
77
+ }
78
+ }
79
+ function toggleTheme() {
80
+ const current = document.documentElement.dataset.theme || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
81
+ const next = current === "dark" ? "light" : "dark";
82
+ document.documentElement.dataset.theme = next;
83
+ localStorage.setItem("open-orchestra-theme", next);
84
+ loadDashboard();
85
+ }
86
+ async function loadDashboard() {
87
+ setLoading("Loading workflow", "");
88
+ try {
89
+ const entries = await Promise.all(
90
+ endpoints.map(async function(entry) {
91
+ const response = await fetch(entry[1]);
92
+ if (!response.ok) {
93
+ throw new Error(entry[1] + " returned " + response.status);
94
+ }
95
+ return [entry[0], await response.json()];
96
+ })
97
+ );
98
+ render(Object.fromEntries(entries));
99
+ setLoading("Workflow loaded", "ok");
100
+ lastUpdated.textContent = "Updated " + (/* @__PURE__ */ new Date()).toLocaleTimeString();
101
+ } catch (error) {
102
+ setLoading("Console unavailable", "warn");
103
+ renderEmpty(
104
+ "The local API is not ready yet.",
105
+ String(error.message || error)
106
+ );
107
+ }
108
+ }
109
+ function render(data) {
110
+ replace(metrics, [
111
+ metric(String(data.status.tasks.total), "Tasks"),
112
+ metric(String(data.graph.ready.length), "Ready"),
113
+ metric(String(data.graph.blocked.length), "Blocked"),
114
+ metric(String(data.graph.locked.length), "Locked"),
115
+ metric(String(data.roles.length), "Roles"),
116
+ metric(String(data.evidence.length), "Evidence"),
117
+ metric(String(data.approvals.length), "Approvals"),
118
+ metric(data.validation.valid ? "Valid" : "Needs work", "Workspace")
119
+ ]);
120
+ renderValidation(data.validation);
121
+ renderWorkspace(data.workspaceClassification, data.runtimeAdapters);
122
+ renderGraph(data.graph);
123
+ renderActions(data);
124
+ renderCharts(data);
125
+ renderEvidenceControls(data);
126
+ renderEvidence(data.evidenceView);
127
+ renderRoles(data.roleActivation);
128
+ renderPlanning(data.roleActivation);
129
+ renderPlaywrightTasks(data.tasks);
130
+ renderSkillControls(data);
131
+ renderSkillCatalog(data.skills, data.sources);
132
+ renderSourceCatalog(data.sources);
133
+ renderLessons(data.lessons);
134
+ loadPlaywrightPlan();
135
+ loadSkillPlan();
136
+ }
137
+ function renderWorkspace(classification, adapters) {
138
+ const adapterNames = adapters.map(function(adapter) {
139
+ return adapter.label;
140
+ });
141
+ replace(workspaceView, [
142
+ listItem(
143
+ classification.kind + " workspace",
144
+ classification.recommendedAction
145
+ ),
146
+ listItem("Write policy", classification.writePolicy),
147
+ listItem("Runtime targets", adapterNames.join(", "))
148
+ ]);
149
+ }
150
+ function renderCharts(data) {
151
+ const chartData = buildDashboardChartData(data);
152
+ const hasChartData = chartData.taskStatus.length > 0 || chartData.graphState.length > 0;
153
+ charts.hidden = !hasChartData;
154
+ if (!hasChartData || !globalThis.Chart) {
155
+ return;
156
+ }
157
+ taskStatusChart = renderChart(
158
+ taskStatusChart,
159
+ "task-status-chart",
160
+ "doughnut",
161
+ chartData.taskStatus
162
+ );
163
+ graphStateChart = renderChart(
164
+ graphStateChart,
165
+ "graph-state-chart",
166
+ "bar",
167
+ chartData.graphState
168
+ );
169
+ }
170
+ function renderChart(current, canvasId, type, slices) {
171
+ const style = getComputedStyle(document.documentElement);
172
+ const colors = [
173
+ style.getPropertyValue("--accent").trim(),
174
+ style.getPropertyValue("--ok").trim(),
175
+ style.getPropertyValue("--warn").trim(),
176
+ "#8a6fdf",
177
+ "#d36d8c",
178
+ "#5f9ea0"
179
+ ];
180
+ const textColor = style.getPropertyValue("--ink").trim();
181
+ const lineColor = style.getPropertyValue("--line").trim();
182
+ if (current) {
183
+ current.destroy();
184
+ }
185
+ return new globalThis.Chart(document.getElementById(canvasId), {
186
+ type,
187
+ data: {
188
+ labels: slices.map(function(slice) {
189
+ return slice.label;
190
+ }),
191
+ datasets: [
192
+ {
193
+ data: slices.map(function(slice) {
194
+ return slice.value;
195
+ }),
196
+ backgroundColor: colors,
197
+ borderColor: lineColor,
198
+ borderWidth: 1
199
+ }
200
+ ]
201
+ },
202
+ options: {
203
+ responsive: true,
204
+ maintainAspectRatio: false,
205
+ plugins: { legend: { position: "bottom", labels: { color: textColor } } },
206
+ scales: type === "bar" ? {
207
+ x: { ticks: { color: textColor }, grid: { color: lineColor } },
208
+ y: {
209
+ beginAtZero: true,
210
+ ticks: { color: textColor, precision: 0 },
211
+ grid: { color: lineColor }
212
+ }
213
+ } : {}
214
+ }
215
+ });
216
+ }
217
+ async function loadEvidence() {
218
+ const query = new URLSearchParams();
219
+ if (evidenceTask.value) {
220
+ query.set("task", evidenceTask.value);
221
+ }
222
+ if (evidenceType.value) {
223
+ query.set("type", evidenceType.value);
224
+ }
225
+ const response = await fetch("/api/evidence/view?" + query.toString());
226
+ renderEvidence(await response.json());
227
+ }
228
+ function renderEvidenceControls(data) {
229
+ replace(
230
+ evidenceTask,
231
+ [option("", "All tasks")].concat(
232
+ data.tasks.map(function(task) {
233
+ return option(task.id, task.id);
234
+ })
235
+ )
236
+ );
237
+ replace(
238
+ evidenceType,
239
+ [option("", "All types")].concat(
240
+ ["command", "file", "screenshot", "trace", "video", "log", "report"].map(
241
+ function(type) {
242
+ return option(type, type);
243
+ }
244
+ )
245
+ )
246
+ );
247
+ replace(
248
+ playwrightTask,
249
+ data.tasks.map(function(task) {
250
+ return option(task.id, task.id);
251
+ })
252
+ );
253
+ }
254
+ function renderEvidence(items) {
255
+ replace(
256
+ evidenceList,
257
+ items.length > 0 ? items.map(evidenceItem) : [
258
+ empty(
259
+ "No evidence yet",
260
+ "Attach command, report, screenshot, trace, video, or log evidence."
261
+ )
262
+ ]
263
+ );
264
+ }
265
+ function evidenceItem(item) {
266
+ const node = listItem(
267
+ item.summary,
268
+ (item.taskId || "no-task") + " - " + item.type + " - " + item.actor
269
+ );
270
+ const row = document.createElement("div");
271
+ row.className = "tag-row";
272
+ item.artifacts.forEach(function(artifact) {
273
+ if (artifact.exists) {
274
+ const link = document.createElement("a");
275
+ link.className = "artifact-link";
276
+ link.href = artifact.url;
277
+ link.target = "_blank";
278
+ link.rel = "noreferrer";
279
+ link.textContent = artifact.path;
280
+ row.append(link);
281
+ } else {
282
+ const miss = document.createElement("span");
283
+ miss.className = "missing";
284
+ miss.textContent = "Missing: " + artifact.path;
285
+ row.append(miss);
286
+ }
287
+ });
288
+ node.append(row);
289
+ return node;
290
+ }
291
+ function renderRoles(report) {
292
+ replace(
293
+ rolesView,
294
+ report.roles.slice(0, 12).map(function(entry) {
295
+ const node = listItem(
296
+ entry.role.name,
297
+ entry.state + " - " + entry.role.description
298
+ );
299
+ const tags = document.createElement("div");
300
+ tags.className = "tag-row";
301
+ ["activationCriteria", "expectedEvidence", "gateParticipation"].forEach(
302
+ function(field) {
303
+ (entry.role[field] || []).slice(0, 3).forEach(function(value) {
304
+ const tag = document.createElement("span");
305
+ tag.className = "tag";
306
+ tag.textContent = value;
307
+ tags.append(tag);
308
+ });
309
+ }
310
+ );
311
+ node.append(tags);
312
+ return node;
313
+ })
314
+ );
315
+ }
316
+ function renderPlanning(report) {
317
+ const totals = report.planning.totals;
318
+ const rows = [
319
+ listItem(
320
+ "Graph totals",
321
+ "ready " + totals.ready + " - blocked " + totals.blocked + " - locked " + totals.locked + " - complete " + totals.complete
322
+ )
323
+ ].concat(
324
+ report.planning.byRole.map(function(role) {
325
+ return listItem(
326
+ role.roleId,
327
+ "ready " + role.ready + " - blocked " + role.blocked + " - locked " + role.locked + " - complete " + role.complete
328
+ );
329
+ })
330
+ );
331
+ replace(planningView, rows);
332
+ }
333
+ function renderSkillControls(data) {
334
+ const optionsForTasks = function() {
335
+ return data.tasks.length > 0 ? data.tasks.map(function(task) {
336
+ return option(task.id, task.id);
337
+ }) : [option("", "No tasks")];
338
+ };
339
+ replace(skillsTask, optionsForTasks());
340
+ replace(lessonTask, [option("", "No task")].concat(optionsForTasks()));
341
+ }
342
+ function renderSkillCatalog(skills, sources) {
343
+ const sourceMap = new Map(
344
+ sources.map(function(source) {
345
+ return [source.id, source.name];
346
+ })
347
+ );
348
+ replace(
349
+ skillsList,
350
+ skills.slice(0, 10).map(function(skill) {
351
+ const node = listItem(skill.name, skill.summary);
352
+ const tags = document.createElement("div");
353
+ tags.className = "tag-row";
354
+ skill.sourceGroups.slice(0, 4).forEach(function(group) {
355
+ const tag = document.createElement("span");
356
+ tag.className = "tag";
357
+ tag.textContent = sourceMap.get(group) || group;
358
+ tags.append(tag);
359
+ });
360
+ node.append(tags);
361
+ return node;
362
+ })
363
+ );
364
+ }
365
+ async function loadSkillPlan() {
366
+ if (!skillsTask.value) {
367
+ replace(skillsPlanList, [
368
+ empty("No task selected", "Create a task to plan task-scoped skills.")
369
+ ]);
370
+ skillsPreview.textContent = "No skill render yet.";
371
+ return;
372
+ }
373
+ const response = await fetch(
374
+ "/api/skills/plan?task=" + encodeURIComponent(skillsTask.value)
375
+ );
376
+ if (!response.ok) {
377
+ replace(skillsPlanList, [
378
+ empty(
379
+ "Skill plan unavailable",
380
+ "The local API could not plan skills for this task."
381
+ )
382
+ ]);
383
+ return;
384
+ }
385
+ const plan = await response.json();
386
+ replace(
387
+ skillsPlanList,
388
+ plan.selected.length > 0 ? plan.selected.map(function(item) {
389
+ const node = listItem(
390
+ item.skill.name,
391
+ item.rationale.join("; ") || "selected"
392
+ );
393
+ const tags = document.createElement("div");
394
+ tags.className = "tag-row";
395
+ item.skill.sourceGroups.slice(0, 4).forEach(function(group) {
396
+ const tag = document.createElement("span");
397
+ tag.className = "tag";
398
+ tag.textContent = group;
399
+ tags.append(tag);
400
+ });
401
+ node.append(tags);
402
+ return node;
403
+ }) : [
404
+ empty(
405
+ "No skills selected",
406
+ "No task signal matched the skill catalog."
407
+ )
408
+ ]
409
+ );
410
+ await loadSkillRender();
411
+ }
412
+ async function loadSkillRender() {
413
+ if (!skillsTask.value) {
414
+ skillsPreview.textContent = "No skill render yet.";
415
+ return;
416
+ }
417
+ const query = new URLSearchParams({
418
+ task: skillsTask.value,
419
+ target: skillsTarget.value
420
+ });
421
+ const response = await fetch("/api/skills/render?" + query.toString());
422
+ if (!response.ok) {
423
+ skillsPreview.textContent = "Skill render unavailable.";
424
+ return;
425
+ }
426
+ const rendered = await response.json();
427
+ skillsPreview.textContent = rendered.content;
428
+ }
429
+ function renderSourceCatalog(sources) {
430
+ replace(
431
+ sourcesList,
432
+ sources.length > 0 ? sources.map(function(source) {
433
+ const node = listItem(
434
+ source.name,
435
+ "priority " + source.priority + " - " + source.description
436
+ );
437
+ const tags = document.createElement("div");
438
+ tags.className = "tag-row";
439
+ source.locations.forEach(function(location) {
440
+ const tag = document.createElement("span");
441
+ tag.className = "tag";
442
+ tag.textContent = location;
443
+ tags.append(tag);
444
+ });
445
+ node.append(tags);
446
+ return node;
447
+ }) : [
448
+ empty(
449
+ "No source catalog",
450
+ "Run orchestra init to scaffold source-of-truth metadata."
451
+ )
452
+ ]
453
+ );
454
+ }
455
+ function renderLessons(lessons) {
456
+ replace(
457
+ lessonsList,
458
+ lessons.length > 0 ? lessons.slice(0, 12).map(function(lesson) {
459
+ const node = listItem(
460
+ lesson.operation,
461
+ lesson.errorSignature + " - " + lesson.rootCause
462
+ );
463
+ const tags = document.createElement("div");
464
+ tags.className = "tag-row";
465
+ const appliesTo = Array.isArray(lesson.appliesTo) ? lesson.appliesTo : [];
466
+ appliesTo.slice(0, 4).forEach(function(tool) {
467
+ const tag = document.createElement("span");
468
+ tag.className = "tag";
469
+ tag.textContent = tool;
470
+ tags.append(tag);
471
+ });
472
+ node.append(tags);
473
+ return node;
474
+ }) : [
475
+ empty(
476
+ "No lessons yet",
477
+ "Record reusable tool, syntax, workflow, or permission failures."
478
+ )
479
+ ]
480
+ );
481
+ }
482
+ async function addLesson() {
483
+ const missingRequiredInput = lessonRequiredInputs().find(function(input) {
484
+ return !input.value.trim();
485
+ });
486
+ if (missingRequiredInput) {
487
+ setLoading("Complete required lesson fields", "warn");
488
+ missingRequiredInput.focus();
489
+ return;
490
+ }
491
+ const body = {
492
+ taskId: lessonTask.value || void 0,
493
+ actor: document.querySelector("#lesson-actor").value,
494
+ operation: document.querySelector("#lesson-operation").value,
495
+ failedAction: document.querySelector("#lesson-failed-action").value,
496
+ errorSignature: document.querySelector("#lesson-error-signature").value,
497
+ rootCause: document.querySelector("#lesson-root-cause").value,
498
+ fix: document.querySelector("#lesson-fix").value,
499
+ prevention: document.querySelector("#lesson-prevention").value,
500
+ appliesTo: csvValues(document.querySelector("#lesson-applies-to").value),
501
+ verifiedBy: csvValues(document.querySelector("#lesson-verified-by").value)
502
+ };
503
+ const response = await fetch("/api/lessons/add", {
504
+ method: "POST",
505
+ headers: { "content-type": "application/json" },
506
+ body: JSON.stringify(body)
507
+ });
508
+ if (!response.ok) {
509
+ setLoading("Lesson add failed", "warn");
510
+ return;
511
+ }
512
+ clearLessonForm();
513
+ await loadDashboard();
514
+ }
515
+ async function promoteLessons() {
516
+ const response = await fetch("/api/lessons/promote", {
517
+ method: "POST",
518
+ headers: { "content-type": "application/json" },
519
+ body: JSON.stringify({
520
+ to: document.querySelector("#lesson-promote-target").value,
521
+ filter: lessonPromoteFilter.value || void 0
522
+ })
523
+ });
524
+ if (!response.ok) {
525
+ setLoading("Lesson promotion failed", "warn");
526
+ return;
527
+ }
528
+ const promotion = await response.json();
529
+ renderLessonPromotion(promotion);
530
+ setLoading("Promoted " + promotion.lessons.length + " lessons", "ok");
531
+ lessonPromoteFilter.value = "";
532
+ await loadDashboard();
533
+ }
534
+ function renderLessonPromotion(promotion) {
535
+ if (!lessonPromotionResult) {
536
+ return;
537
+ }
538
+ const rows = [];
539
+ if (promotion.artifact) {
540
+ rows.push(listItem("Review artifact", promotion.artifact));
541
+ }
542
+ rows.push(listItem("Promoted lessons", String(promotion.lessons.length)));
543
+ replace(lessonPromotionResult, rows);
544
+ }
545
+ function lessonRequiredInputs() {
546
+ return Array.from(document.querySelectorAll("[data-lesson-required]"));
547
+ }
548
+ function clearLessonForm() {
549
+ [
550
+ "#lesson-operation",
551
+ "#lesson-failed-action",
552
+ "#lesson-error-signature",
553
+ "#lesson-root-cause",
554
+ "#lesson-fix",
555
+ "#lesson-prevention",
556
+ "#lesson-applies-to",
557
+ "#lesson-verified-by"
558
+ ].forEach(function(selector) {
559
+ document.querySelector(selector).value = "";
560
+ });
561
+ }
562
+ function csvValues(value) {
563
+ return value.split(",").map(function(item) {
564
+ return item.trim();
565
+ }).filter(Boolean);
566
+ }
567
+ function renderPlaywrightTasks(tasks) {
568
+ if (tasks.length === 0) {
569
+ replace(playwrightTask, [option("", "No tasks")]);
570
+ }
571
+ }
572
+ async function loadPlaywrightPlan() {
573
+ if (!playwrightTask.value) {
574
+ replace(playwrightPlan, [
575
+ empty("No task selected", "Create a task to generate a Playwright plan.")
576
+ ]);
577
+ return;
578
+ }
579
+ const response = await fetch(
580
+ "/api/playwright/plan?task=" + encodeURIComponent(playwrightTask.value)
581
+ );
582
+ const plan = await response.json();
583
+ replace(
584
+ playwrightPlan,
585
+ plan.scenarios.map(function(scenario) {
586
+ return listItem(scenario.name, scenario.assertions.join(" | "));
587
+ })
588
+ );
589
+ }
590
+ async function attachPlaywrightEvidence() {
591
+ const body = {
592
+ task: playwrightTask.value,
593
+ kind: document.querySelector("#playwright-kind").value,
594
+ path: document.querySelector("#playwright-path").value,
595
+ summary: document.querySelector("#playwright-summary").value
596
+ };
597
+ const response = await fetch("/api/playwright/evidence", {
598
+ method: "POST",
599
+ headers: { "content-type": "application/json" },
600
+ body: JSON.stringify(body)
601
+ });
602
+ if (!response.ok) {
603
+ setLoading("Evidence attach failed", "warn");
604
+ return;
605
+ }
606
+ await loadDashboard();
607
+ }
608
+ function option(value, label) {
609
+ const node = document.createElement("option");
610
+ node.value = value;
611
+ node.textContent = label;
612
+ return node;
613
+ }
614
+ function renderValidation(report) {
615
+ if (report.valid && report.warnings.length === 0) {
616
+ replace(validation, [
617
+ empty("Workspace is valid", "No validation issues were reported.")
618
+ ]);
619
+ return;
620
+ }
621
+ replace(
622
+ validation,
623
+ report.errors.concat(report.warnings).map(function(message) {
624
+ return listItem(
625
+ message,
626
+ report.errors.includes(message) ? "Error" : "Warning"
627
+ );
628
+ })
629
+ );
630
+ }
631
+ function renderGraph(plan) {
632
+ const rows = [
633
+ ["Ready", plan.ready],
634
+ ["Blocked", plan.blocked],
635
+ ["Locked", plan.locked],
636
+ ["Complete", plan.complete]
637
+ ].flatMap(function(group) {
638
+ return group[1].slice(0, 4).map(function(item) {
639
+ return listItem(
640
+ item.title,
641
+ group[0] + " - " + item.id + " - " + item.ownerRole
642
+ );
643
+ });
644
+ });
645
+ replace(
646
+ graph,
647
+ rows.length > 0 ? rows : [
648
+ empty(
649
+ "No tasks yet",
650
+ "Add tasks with orchestra task add to start planning."
651
+ )
652
+ ]
653
+ );
654
+ }
655
+ function renderActions(data) {
656
+ const next = [];
657
+ if (!data.validation.valid) {
658
+ next.push(
659
+ listItem(
660
+ "Fix workspace validation",
661
+ "Run orchestra validate for full details."
662
+ )
663
+ );
664
+ }
665
+ if (data.graph.ready.length > 0) {
666
+ next.push(
667
+ listItem(
668
+ "Start ready work",
669
+ data.graph.ready.length + " task(s) can run now."
670
+ )
671
+ );
672
+ }
673
+ if (data.approvals.length > 0) {
674
+ next.push(
675
+ listItem(
676
+ "Review pending approvals",
677
+ data.approvals.length + " approval artifact(s) found."
678
+ )
679
+ );
680
+ }
681
+ if (data.status.tasks.total === 0) {
682
+ next.push(
683
+ listItem(
684
+ "Create the first task",
685
+ "Use a backlog item, owner role, scope, and acceptance criteria."
686
+ )
687
+ );
688
+ }
689
+ replace(
690
+ actions,
691
+ next.length > 0 ? next : [
692
+ empty(
693
+ "No immediate action",
694
+ "The workflow has no blocking console actions."
695
+ )
696
+ ]
697
+ );
698
+ }
699
+ function metric(value, label) {
700
+ const node = document.createElement("div");
701
+ node.className = "metric";
702
+ const strong = document.createElement("strong");
703
+ strong.textContent = value;
704
+ const span = document.createElement("span");
705
+ span.textContent = label;
706
+ node.append(strong, span);
707
+ return node;
708
+ }
709
+ function listItem(title, detail) {
710
+ const node = document.createElement("li");
711
+ node.className = "list-item";
712
+ const strong = document.createElement("strong");
713
+ strong.textContent = title;
714
+ const span = document.createElement("span");
715
+ span.textContent = detail;
716
+ node.append(strong, span);
717
+ return node;
718
+ }
719
+ function empty(title, detail) {
720
+ const node = document.createElement("div");
721
+ node.className = "empty-state";
722
+ const strong = document.createElement("strong");
723
+ strong.textContent = title;
724
+ const p = document.createElement("p");
725
+ p.textContent = detail;
726
+ node.append(strong, p);
727
+ return node;
728
+ }
729
+ function replace(parent, nodes) {
730
+ parent.replaceChildren(...nodes);
731
+ }
732
+ function renderEmpty(title, detail) {
733
+ replace(metrics, [empty(title, detail)]);
734
+ charts.hidden = true;
735
+ replace(validation, []);
736
+ replace(workspaceView, []);
737
+ replace(graph, []);
738
+ replace(actions, []);
739
+ }
740
+ function setLoading(label, tone) {
741
+ statePill.textContent = label;
742
+ statePill.className = ("pill " + tone).trim();
743
+ }