@nanobpm/nano-workforce 0.26.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.
Files changed (115) hide show
  1. package/.github/workflows/ci.yml +60 -0
  2. package/.github/workflows/release.yml +58 -0
  3. package/.releaserc.json +17 -0
  4. package/AGENTS.md +168 -0
  5. package/CHANGELOG.md +231 -0
  6. package/LICENSE +202 -0
  7. package/README.md +303 -0
  8. package/SPEC.md +492 -0
  9. package/actions/abandon.test.ts +93 -0
  10. package/actions/abandon.ts +23 -0
  11. package/actions/blackboard.test.ts +195 -0
  12. package/actions/blackboard.ts +76 -0
  13. package/actions/cancel.ts +29 -0
  14. package/actions/feature-answer-hook.ts +44 -0
  15. package/actions/message.ts +49 -0
  16. package/actions/plan-hook.ts +19 -0
  17. package/actions/plan-start.ts +17 -0
  18. package/actions/start.ts +19 -0
  19. package/actions/status.ts +22 -0
  20. package/actions/webhook-submit.ts +21 -0
  21. package/app/abandon.test.ts +97 -0
  22. package/app/abandon.ts +105 -0
  23. package/app/baseGuard.test.ts +35 -0
  24. package/app/baseGuard.ts +62 -0
  25. package/app/blackboard.test.ts +295 -0
  26. package/app/blackboard.ts +301 -0
  27. package/app/github.test.ts +59 -0
  28. package/app/github.ts +647 -0
  29. package/app/mergeExclusion.test.ts +168 -0
  30. package/app/mergeExclusion.ts +211 -0
  31. package/app/mergeProtocol.test.ts +124 -0
  32. package/app/mergeProtocol.ts +193 -0
  33. package/app/mergeRebaseArm.test.ts +72 -0
  34. package/app/mergeTrain.test.ts +91 -0
  35. package/app/mergeTrain.ts +117 -0
  36. package/app/persist-escalation.test.ts +119 -0
  37. package/app/persist-round.test.ts +65 -0
  38. package/app/plan.test.ts +317 -0
  39. package/app/plan.ts +321 -0
  40. package/app/record-plan-review.test.ts +38 -0
  41. package/app/reviewWait.test.ts +70 -0
  42. package/app/reviewWait.ts +59 -0
  43. package/app/rounds.test.ts +74 -0
  44. package/app/rounds.ts +48 -0
  45. package/app/service.test.ts +101 -0
  46. package/app/service.ts +895 -0
  47. package/app/taskDelta.test.ts +144 -0
  48. package/app/taskDelta.ts +175 -0
  49. package/app/trialMerge.test.ts +15 -0
  50. package/app/trialMerge.ts +102 -0
  51. package/app/waves.test.ts +128 -0
  52. package/app/waves.ts +116 -0
  53. package/assets/icon.svg +13 -0
  54. package/components/review-round.json +69 -0
  55. package/db/migrations/001_init.sql +46 -0
  56. package/db/migrations/002_transcript.sql +7 -0
  57. package/db/migrations/003_open_escalation.sql +8 -0
  58. package/db/migrations/004_merge.sql +36 -0
  59. package/db/migrations/004_planning.sql +37 -0
  60. package/db/migrations/005_job_activation.sql +15 -0
  61. package/db/migrations/005_plan_deps.sql +20 -0
  62. package/db/migrations/006_plan_review.sql +22 -0
  63. package/db/migrations/006_task_escalation.sql +52 -0
  64. package/db/migrations/007_plan_review_job_key.sql +14 -0
  65. package/db/migrations/007_wave_gate.sql +16 -0
  66. package/db/migrations/008_review_nudge.sql +9 -0
  67. package/db/migrations/009_plan_blackboard.sql +46 -0
  68. package/db/migrations/010_plan_task_deltas.sql +27 -0
  69. package/db/migrations/011_plan_merge_exclusions.sql +26 -0
  70. package/db/migrations/012_merge_protocol_attempt.sql +4 -0
  71. package/db/migrations/013_merge_train_waiting_lane.sql +6 -0
  72. package/db/migrations/014_plan_trial_merges.sql +21 -0
  73. package/db/migrations/015_pr_abandon_token.sql +9 -0
  74. package/deno.json +24 -0
  75. package/deno.lock +1776 -0
  76. package/main.ts +71 -0
  77. package/nano-ide.ext.json +7 -0
  78. package/nano.app.json +138 -0
  79. package/nanobpm.project.json +20 -0
  80. package/package.json +56 -0
  81. package/pages/epic.page.json +195 -0
  82. package/pages/home.page.json +296 -0
  83. package/prompts/feature.md +132 -0
  84. package/prompts/fix-ci.md +65 -0
  85. package/prompts/plan-review.md +69 -0
  86. package/prompts/plan.md +183 -0
  87. package/prompts/rebase.md +82 -0
  88. package/prompts/review-round.md +171 -0
  89. package/prompts/trial-merge.md +43 -0
  90. package/renovate.json +21 -0
  91. package/resources/processes/convergence-loop.bpmn +399 -0
  92. package/resources/processes/merge-loop.bpmn +585 -0
  93. package/resources/processes/plan-fanout.bpmn +546 -0
  94. package/scripts/check-agent-prompts.test.ts +84 -0
  95. package/scripts/check-agent-prompts.ts +143 -0
  96. package/scripts/layout-bpmn.ts +99 -0
  97. package/scripts/purge-db.ts +57 -0
  98. package/scripts/upgrade-from-pack.ts +334 -0
  99. package/tsconfig.json +51 -0
  100. package/workers/arm-merge/worker.ts +18 -0
  101. package/workers/finalize/worker.ts +89 -0
  102. package/workers/mark-merged/worker.ts +21 -0
  103. package/workers/merge/worker.ts +119 -0
  104. package/workers/persist-escalation/worker.ts +107 -0
  105. package/workers/persist-round/worker.ts +52 -0
  106. package/workers/persist-task-escalation/worker.ts +112 -0
  107. package/workers/record-plan/worker.ts +135 -0
  108. package/workers/record-plan-review/worker.ts +92 -0
  109. package/workers/record-results/worker.ts +30 -0
  110. package/workers/record-trial-merge/worker.test.ts +104 -0
  111. package/workers/record-trial-merge/worker.ts +88 -0
  112. package/workers/record-wave/worker.test.ts +221 -0
  113. package/workers/record-wave/worker.ts +308 -0
  114. package/workers/select-wave/worker.test.ts +130 -0
  115. package/workers/select-wave/worker.ts +84 -0
package/main.ts ADDED
@@ -0,0 +1,71 @@
1
+ // nano-workforce — Urban App entrypoint (ADR 0055).
2
+ //
3
+ // The whole app is declared in `nano.app.json` (models, sqlite datasource, app-hosted record
4
+ // workers, the schema-driven pages surface, and the action overrides) and materialized by the
5
+ // `@nanobpm/urban` runtime via `runFromEnv`:
6
+ // • deploys the BPMN + hosts the `pr.*` record workers (workers/*/worker.ts),
7
+ // • serves the schema-driven page runtime (ADR 0042) from `pages/home.page.json`,
8
+ // • mounts the app-specific action overrides (actions/*.ts) that wrap the generic
9
+ // start/cancel/message actions, plus the `/hooks/submit` webhook.
10
+ //
11
+ // The only thing that isn't declarative is the review-ready poller: it does arbitrary GitHub
12
+ // polling and then correlates a `review-ready` message. A cron trigger can only fire an engine
13
+ // start/message action, not this custom I/O glue, so it stays app-side here — driving the same
14
+ // engine client the runtime uses, over `app.data`.
15
+ //
16
+ // The reviewer agent (job type `senior:pr-review`) is deliberately NOT hosted here — it is an
17
+ // EXTERNAL worker. Point a coding-agent harness at that job type (the same one that services
18
+ // the code-first twin) so the automated review stays decoupled from the orchestration.
19
+ import { createNanoSdkEngineClient, runFromEnv, selectHost } from "@nanobpm/urban";
20
+ import { MAX_ROUNDS, pollOnce } from "./app/service.ts";
21
+
22
+ const PORT = Number(process.env.PR_REVIEW_PORT ?? 3000);
23
+ const POLL_MS = Number(process.env.NANO_PR_POLL_MS ?? 60_000);
24
+ const GITHUB_TOKEN = process.env.GITHUB_TOKEN ?? "";
25
+
26
+ const host = selectHost();
27
+
28
+ // One engine client, shared by the runtime (surfaces/actions/workers) and the poller. Honour
29
+ // the app's documented NANOBPMN_BASE_URL as well as the runtime's CAMUNDA_REST_ADDRESS.
30
+ const restAddress = process.env.CAMUNDA_REST_ADDRESS ??
31
+ `${(process.env.NANOBPMN_BASE_URL ?? "http://localhost:8080").replace(/\/+$/, "")}/v2`;
32
+ const engine = await createNanoSdkEngineClient({
33
+ restAddress,
34
+ token: process.env.CAMUNDA_TOKEN,
35
+ transport: process.env.CAMUNDA_TRANSPORT ?? "auto",
36
+ log: host.log,
37
+ });
38
+
39
+ // Manage our own shutdown so the poller is stopped and the process exits (the runtime
40
+ // signal handler would only stop the HTTP server, leaving the poller keeping us alive).
41
+ const app = await runFromEnv({ engine, host, port: PORT, handleSignals: false });
42
+
43
+ // Review-ready poller. Self-scheduling (not setInterval) so a slow GitHub call can never
44
+ // overlap two passes (which could double-signal `review-ready`); the next pass is scheduled
45
+ // only after the previous one settles.
46
+ let shuttingDown = false;
47
+ let pollTimer: ReturnType<typeof setTimeout> | null = null;
48
+ async function pollLoop(): Promise<void> {
49
+ try {
50
+ if (app.data) await pollOnce(app.data, engine, GITHUB_TOKEN, { restAddress, token: process.env.CAMUNDA_TOKEN });
51
+ } catch (err) {
52
+ console.error("poll error:", err);
53
+ }
54
+ if (!shuttingDown) pollTimer = setTimeout(() => void pollLoop(), POLL_MS);
55
+ }
56
+ if (app.data) pollTimer = setTimeout(() => void pollLoop(), POLL_MS);
57
+
58
+ async function drainAndExit(): Promise<void> {
59
+ if (shuttingDown) return;
60
+ shuttingDown = true;
61
+ if (pollTimer) clearTimeout(pollTimer);
62
+ try {
63
+ await app.stop();
64
+ } catch { /* already stopped */ }
65
+ process.exit(0);
66
+ }
67
+ for (const sig of ["SIGINT", "SIGTERM"] as const) {
68
+ process.on(sig, () => void drainAndExit());
69
+ }
70
+
71
+ console.log(`nano-workforce serving on :${PORT} (poll ${POLL_MS}ms, maxRounds ${MAX_ROUNDS})`);
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": "nano-workforce",
3
+ "kind": "example",
4
+ "displayName": "Nano Workforce",
5
+ "appDir": ".",
6
+ "summary": "Agent Graph Orchestration for Agentic SDLC: durable BPMN processes that coordinate a graph of AI agents across the software delivery lifecycle — planning and fanning a fleet of agents out to implement an issue, driving each pull request to review convergence, and merging, escalating to a human when an agent is stuck or a round cap is hit."
7
+ }
package/nano.app.json ADDED
@@ -0,0 +1,138 @@
1
+ {
2
+ "$schema": "https://nanobpm.io/spec-app/nano-app.schema.json",
3
+ "schemaVersion": 1,
4
+ "id": "nano-workforce",
5
+ "name": "Nano Workforce",
6
+ "codename": "nano-workforce",
7
+ "models": {
8
+ "processes": [
9
+ "resources/processes/*.bpmn"
10
+ ],
11
+ "templates": [
12
+ "prompts/*.md"
13
+ ]
14
+ },
15
+ "data": {
16
+ "default": "app",
17
+ "sources": {
18
+ "app": {
19
+ "driver": "sqlite",
20
+ "url": "${NANO_APP_DB_URL:-file:./app.db}",
21
+ "migrations": "db/migrations"
22
+ }
23
+ }
24
+ },
25
+ "workers": [
26
+ {
27
+ "taskType": "pr.persist-round",
28
+ "handler": "workers/persist-round/worker.ts"
29
+ },
30
+ {
31
+ "taskType": "pr.persist-escalation",
32
+ "handler": "workers/persist-escalation/worker.ts"
33
+ },
34
+ {
35
+ "taskType": "pr.finalize",
36
+ "handler": "workers/finalize/worker.ts"
37
+ },
38
+ {
39
+ "taskType": "pr.arm-merge",
40
+ "handler": "workers/arm-merge/worker.ts"
41
+ },
42
+ {
43
+ "taskType": "pr.merge",
44
+ "handler": "workers/merge/worker.ts"
45
+ },
46
+ {
47
+ "taskType": "pr.mark-merged",
48
+ "handler": "workers/mark-merged/worker.ts"
49
+ },
50
+ {
51
+ "taskType": "pr.record-plan",
52
+ "handler": "workers/record-plan/worker.ts"
53
+ },
54
+ {
55
+ "taskType": "pr.record-plan-review",
56
+ "handler": "workers/record-plan-review/worker.ts"
57
+ },
58
+ {
59
+ "taskType": "pr.select-wave",
60
+ "handler": "workers/select-wave/worker.ts"
61
+ },
62
+ {
63
+ "taskType": "pr.record-wave",
64
+ "handler": "workers/record-wave/worker.ts"
65
+ },
66
+ {
67
+ "taskType": "pr.record-trial-merge",
68
+ "handler": "workers/record-trial-merge/worker.ts"
69
+ },
70
+ {
71
+ "taskType": "pr.record-results",
72
+ "handler": "workers/record-results/worker.ts"
73
+ },
74
+ {
75
+ "taskType": "pr.persist-task-escalation",
76
+ "handler": "workers/persist-task-escalation/worker.ts"
77
+ }
78
+ ],
79
+ "surfaces": {
80
+ "pages": {
81
+ "enabled": true,
82
+ "pagesDir": "pages",
83
+ "homePage": "home",
84
+ "sourceName": "app"
85
+ }
86
+ },
87
+ "ui": {
88
+ "enabled": true,
89
+ "portEnv": "PR_REVIEW_PORT",
90
+ "path": "/",
91
+ "label": "Nano Workforce",
92
+ "icon": "assets/icon.svg"
93
+ },
94
+ "actions": [
95
+ {
96
+ "path": "/app/actions/start/convergence-loop",
97
+ "module": "actions/start.ts"
98
+ },
99
+ {
100
+ "path": "/app/actions/start/plan-fanout",
101
+ "module": "actions/plan-start.ts"
102
+ },
103
+ {
104
+ "path": "/app/actions/cancel",
105
+ "module": "actions/cancel.ts"
106
+ },
107
+ {
108
+ "path": "/app/status",
109
+ "module": "actions/status.ts",
110
+ "method": "GET"
111
+ },
112
+ {
113
+ "path": "/app/actions/message",
114
+ "module": "actions/message.ts"
115
+ },
116
+ {
117
+ "path": "/hooks/submit",
118
+ "module": "actions/webhook-submit.ts"
119
+ },
120
+ {
121
+ "path": "/hooks/plan",
122
+ "module": "actions/plan-hook.ts"
123
+ },
124
+ {
125
+ "path": "/hooks/feature-answer",
126
+ "module": "actions/feature-answer-hook.ts"
127
+ },
128
+ {
129
+ "path": "/hooks/blackboard",
130
+ "module": "actions/blackboard.ts"
131
+ },
132
+ {
133
+ "path": "/hooks/abandon",
134
+ "module": "actions/abandon.ts",
135
+ "method": "GET"
136
+ }
137
+ ]
138
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "nano-workforce",
3
+ "description": "Nano Workforce — Agent Graph Orchestration for Agentic SDLC, orchestrated by Nano.",
4
+ "main": "main.ts",
5
+ "lang": "node",
6
+ "app": "urban",
7
+ "autoDeploy": [],
8
+ "toolchain": {
9
+ "run": [
10
+ "node",
11
+ "--experimental-strip-types",
12
+ "main.ts"
13
+ ],
14
+ "compile": [
15
+ "deno",
16
+ "task",
17
+ "compile"
18
+ ]
19
+ }
20
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@nanobpm/nano-workforce",
3
+ "version": "0.26.0",
4
+ "description": "Nano Workforce — an Agent Graph Orchestration application for Agentic SDLC: durable BPMN processes that coordinate a graph of AI agents across the software delivery lifecycle.",
5
+ "type": "module",
6
+ "main": "main.ts",
7
+ "engines": {
8
+ "node": ">=22.6"
9
+ },
10
+ "author": "Josh Wulf <josh.wulf@camunda.com>",
11
+ "license": "Apache-2.0",
12
+ "homepage": "https://github.com/nanobpm/nano-workforce#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/nanobpm/nano-workforce.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/nanobpm/nano-workforce/issues"
19
+ },
20
+ "keywords": [
21
+ "nano-ide-ext",
22
+ "nano-ide-agentic-sdlc"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "start": "node --experimental-strip-types main.ts",
29
+ "purge": "node --experimental-strip-types scripts/purge-db.ts",
30
+ "upgrade": "node --experimental-strip-types scripts/upgrade-from-pack.ts",
31
+ "check": "urban check",
32
+ "typecheck": "tsc --noEmit",
33
+ "check:prompts": "deno run -A scripts/check-agent-prompts.ts",
34
+ "gen": "urban gen",
35
+ "gen:check": "urban gen --check",
36
+ "layout": "node --experimental-strip-types scripts/layout-bpmn.ts",
37
+ "layout:check": "node --experimental-strip-types scripts/layout-bpmn.ts --check",
38
+ "dev": "urban dev",
39
+ "compile": "deno task compile",
40
+ "test": "deno test -A"
41
+ },
42
+ "dependencies": {
43
+ "@nanobpm/urban": "^0.24.0"
44
+ },
45
+ "devDependencies": {
46
+ "@semantic-release/changelog": "^6.0.3",
47
+ "@semantic-release/git": "^10.0.1",
48
+ "@semantic-release/npm": "^13.1.5",
49
+ "@types/node": "^22",
50
+ "semantic-release": "^24.2.9",
51
+ "typescript": "^5.6.0"
52
+ },
53
+ "overrides": {
54
+ "@semantic-release/npm": "^13.1.5"
55
+ }
56
+ }
@@ -0,0 +1,195 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "title": "Epic Coordination",
4
+ "nodes": [
5
+ {
6
+ "type": "text",
7
+ "id": "title",
8
+ "props": { "text": "Epic Coordination", "variant": "heading" }
9
+ },
10
+ {
11
+ "type": "text",
12
+ "id": "subtitle",
13
+ "props": {
14
+ "text": "Read-only observability over each plan's wave state, merge-exclusion graph, coordination notes, and trial-merge gate results.",
15
+ "variant": "sub"
16
+ }
17
+ },
18
+ {
19
+ "type": "dataGrid",
20
+ "id": "epic-plans",
21
+ "props": {
22
+ "title": "Plans",
23
+ "rowKey": "plan_key",
24
+ "refreshMs": 5000,
25
+ "data": {
26
+ "kind": "datasource",
27
+ "source": "app",
28
+ "table": "plans",
29
+ "orderBy": { "field": "updated_at", "dir": "desc" },
30
+ "filter": [{ "field": "status", "in": ["planning", "dispatched"] }]
31
+ },
32
+ "tabs": [
33
+ {
34
+ "label": "Active",
35
+ "filter": [{ "field": "status", "in": ["planning", "dispatched"] }]
36
+ },
37
+ {
38
+ "label": "History",
39
+ "filter": [{ "field": "status", "in": ["done", "failed", "abandoned"] }]
40
+ },
41
+ { "label": "All", "filter": [] }
42
+ ],
43
+ "columns": [
44
+ { "field": "plan_key", "header": "Issue", "linkField": "issue_url" },
45
+ { "field": "status", "header": "Status" },
46
+ { "field": "task_count", "header": "Tasks" },
47
+ { "field": "open_task_id", "header": "Open escalation" },
48
+ { "field": "updated_at", "header": "Updated" }
49
+ ],
50
+ "detail": {
51
+ "linkField": "issue_url",
52
+ "fields": [
53
+ { "field": "repo", "label": "Repository" },
54
+ { "field": "issue_number", "label": "Issue number" },
55
+ { "field": "outcome", "label": "Outcome" },
56
+ { "field": "open_task_question", "label": "Open escalation question" }
57
+ ]
58
+ }
59
+ }
60
+ },
61
+ {
62
+ "type": "dataGrid",
63
+ "id": "wave-state",
64
+ "props": {
65
+ "title": "Wave state",
66
+ "rowKey": "id",
67
+ "refreshMs": 5000,
68
+ "data": {
69
+ "kind": "datasource",
70
+ "source": "app",
71
+ "table": "plan_tasks",
72
+ "orderBy": { "field": "wave", "dir": "asc" },
73
+ "filter": [
74
+ { "field": "status", "in": ["pending", "opened", "escalated", "waiting-for-lane", "blocked"] }
75
+ ]
76
+ },
77
+ "tabs": [
78
+ {
79
+ "label": "Active",
80
+ "filter": [
81
+ { "field": "status", "in": ["pending", "opened", "escalated", "waiting-for-lane", "blocked"] }
82
+ ]
83
+ },
84
+ {
85
+ "label": "Skipped",
86
+ "filter": [{ "field": "status", "in": ["skipped"] }]
87
+ },
88
+ { "label": "All", "filter": [] }
89
+ ],
90
+ "columns": [
91
+ { "field": "plan_key", "header": "Plan" },
92
+ { "field": "wave", "header": "Wave" },
93
+ { "field": "task_id", "header": "Task" },
94
+ { "field": "title", "header": "Title" },
95
+ { "field": "status", "header": "Status" },
96
+ { "field": "pr_key", "header": "PR" },
97
+ { "field": "summary", "header": "Summary" },
98
+ { "field": "updated_at", "header": "Updated" }
99
+ ],
100
+ "detail": {
101
+ "fields": [
102
+ { "field": "open_question", "label": "Open question" },
103
+ { "field": "answer", "label": "Answer" },
104
+ { "field": "draft_pr_key", "label": "Draft PR" },
105
+ { "field": "prompt", "label": "Prompt" }
106
+ ]
107
+ }
108
+ }
109
+ },
110
+ {
111
+ "type": "dataGrid",
112
+ "id": "merge-exclusions",
113
+ "props": {
114
+ "title": "Conflict graph / who blocks whom",
115
+ "rowKey": "id",
116
+ "refreshMs": 5000,
117
+ "data": {
118
+ "kind": "datasource",
119
+ "source": "app",
120
+ "table": "plan_merge_exclusions",
121
+ "orderBy": { "field": "id", "dir": "asc" }
122
+ },
123
+ "columns": [
124
+ { "field": "plan_key", "header": "Plan" },
125
+ { "field": "task_a", "header": "Task A" },
126
+ { "field": "task_b", "header": "Task B" },
127
+ { "field": "files", "header": "Shared files" },
128
+ { "field": "source", "header": "Source" },
129
+ { "field": "updated_at", "header": "Updated" }
130
+ ]
131
+ }
132
+ },
133
+ {
134
+ "type": "dataGrid",
135
+ "id": "coordination-notes",
136
+ "props": {
137
+ "title": "Coordination notes",
138
+ "rowKey": "id",
139
+ "refreshMs": 5000,
140
+ "data": {
141
+ "kind": "datasource",
142
+ "source": "app",
143
+ "table": "plan_blackboard",
144
+ "orderBy": { "field": "id", "dir": "asc" }
145
+ },
146
+ "columns": [
147
+ { "field": "plan_key", "header": "Plan" },
148
+ { "field": "wave", "header": "Wave" },
149
+ { "field": "author_task", "header": "Agent" },
150
+ { "field": "kind", "header": "Kind" },
151
+ { "field": "files", "header": "Files" },
152
+ { "field": "body", "header": "Note" },
153
+ { "field": "created_at", "header": "Posted" }
154
+ ]
155
+ }
156
+ },
157
+ {
158
+ "type": "dataGrid",
159
+ "id": "trial-merge-results",
160
+ "props": {
161
+ "title": "Trial-merge results",
162
+ "rowKey": "id",
163
+ "refreshMs": 5000,
164
+ "data": {
165
+ "kind": "datasource",
166
+ "source": "app",
167
+ "table": "plan_trial_merges",
168
+ "orderBy": { "field": "created_at", "dir": "desc" },
169
+ "filter": [{ "field": "result", "in": ["merge-conflict", "suite-failed"] }]
170
+ },
171
+ "tabs": [
172
+ {
173
+ "label": "Needs attention",
174
+ "filter": [{ "field": "result", "in": ["merge-conflict", "suite-failed"] }]
175
+ },
176
+ {
177
+ "label": "Clean",
178
+ "filter": [{ "field": "result", "in": ["clean"] }]
179
+ },
180
+ { "label": "All", "filter": [] }
181
+ ],
182
+ "columns": [
183
+ { "field": "plan_key", "header": "Plan" },
184
+ { "field": "wave", "header": "Wave" },
185
+ { "field": "result", "header": "Result" },
186
+ { "field": "heads", "header": "Heads" },
187
+ { "field": "conflicts", "header": "Conflicts" },
188
+ { "field": "failing", "header": "Failing" },
189
+ { "field": "summary", "header": "Summary" },
190
+ { "field": "created_at", "header": "Recorded" }
191
+ ]
192
+ }
193
+ }
194
+ ]
195
+ }