@nanobpm/nano-workforce 0.62.0 → 0.63.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/CHANGELOG.md +7 -0
- package/nano.app.json +1 -1
- package/package.json +1 -1
- package/pages/cockpit.page.json +1 -0
- package/pages/epic-detail.page.json +1 -0
- package/pages/epic.page.json +1 -0
- package/pages/feature.page.json +1 -0
- package/pages/home.page.json +4 -0
- package/pages/overview.page.json +137 -0
- package/scripts/pages-contract.test.ts +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [0.63.0](https://github.com/nanobpm/nano-workforce/compare/v0.62.0...v0.63.0) (2026-08-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Overview landing page with collapsible active-work sections ([#206](https://github.com/nanobpm/nano-workforce/issues/206)) ([64c91df](https://github.com/nanobpm/nano-workforce/commit/64c91dfacd6b9b660a7bbbb1a3c9a8a78603b247)), closes [nanobpm/nano-ide#227](https://github.com/nanobpm/nano-ide/issues/227) [#205](https://github.com/nanobpm/nano-workforce/issues/205)
|
|
7
|
+
|
|
1
8
|
# [0.62.0](https://github.com/nanobpm/nano-workforce/compare/v0.61.1...v0.62.0) (2026-08-13)
|
|
2
9
|
|
|
3
10
|
|
package/nano.app.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"main": "main.ts",
|
package/pages/cockpit.page.json
CHANGED
package/pages/epic.page.json
CHANGED
package/pages/feature.page.json
CHANGED
package/pages/home.page.json
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0",
|
|
3
|
+
"title": "Overview",
|
|
4
|
+
"nodes": [
|
|
5
|
+
{
|
|
6
|
+
"type": "nav",
|
|
7
|
+
"id": "nav",
|
|
8
|
+
"props": {
|
|
9
|
+
"variant": "bar",
|
|
10
|
+
"sticky": true,
|
|
11
|
+
"title": "Nano Workforce",
|
|
12
|
+
"items": [
|
|
13
|
+
{ "label": "Overview", "page": "overview" },
|
|
14
|
+
{ "label": "Convergence", "page": "home" },
|
|
15
|
+
{ "label": "Epics", "page": "epic" },
|
|
16
|
+
{ "label": "Feature", "page": "feature" },
|
|
17
|
+
{ "label": "Cockpit", "page": "cockpit" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "text",
|
|
23
|
+
"id": "title",
|
|
24
|
+
"props": { "text": "Overview", "variant": "heading" }
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "text",
|
|
28
|
+
"id": "subtitle",
|
|
29
|
+
"props": {
|
|
30
|
+
"text": "Everything currently in flight across the three dispatch surfaces \u2014 PR convergences, Epics, and Feature runs. Each section lists only its active items, remembers whether you collapsed it, and shows a live count in its header. Follow any row to its detail page.",
|
|
31
|
+
"variant": "sub"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "dataGrid",
|
|
36
|
+
"id": "overview-prs",
|
|
37
|
+
"props": {
|
|
38
|
+
"title": "Active PR convergences",
|
|
39
|
+
"collapsible": true,
|
|
40
|
+
"defaultCollapsed": false,
|
|
41
|
+
"showCount": true,
|
|
42
|
+
"rowKey": "pr_key",
|
|
43
|
+
"refreshMs": 5000,
|
|
44
|
+
"data": {
|
|
45
|
+
"kind": "datasource",
|
|
46
|
+
"source": "app",
|
|
47
|
+
"table": "pull_requests",
|
|
48
|
+
"orderBy": { "field": "updated_at", "dir": "desc" },
|
|
49
|
+
"filter": [
|
|
50
|
+
{
|
|
51
|
+
"field": "status",
|
|
52
|
+
"in": [
|
|
53
|
+
"converging",
|
|
54
|
+
"waiting_review",
|
|
55
|
+
"escalated",
|
|
56
|
+
"waiting_deps",
|
|
57
|
+
"waiting_merge",
|
|
58
|
+
"queued",
|
|
59
|
+
"merging"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"columns": [
|
|
65
|
+
{ "field": "pr_key", "header": "PR", "link": { "kind": "page", "page": "home", "keyField": "pr_key" } },
|
|
66
|
+
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
67
|
+
{
|
|
68
|
+
"field": "incident_message",
|
|
69
|
+
"header": "Incident",
|
|
70
|
+
"badge": { "tone": "danger", "label": "1" }
|
|
71
|
+
},
|
|
72
|
+
{ "field": "current_round", "header": "Round" },
|
|
73
|
+
{ "field": "active_worker", "header": "Agent" },
|
|
74
|
+
{ "field": "updated_at", "header": "Updated" }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"type": "dataGrid",
|
|
80
|
+
"id": "overview-epics",
|
|
81
|
+
"props": {
|
|
82
|
+
"title": "Active Epics",
|
|
83
|
+
"collapsible": true,
|
|
84
|
+
"defaultCollapsed": false,
|
|
85
|
+
"showCount": true,
|
|
86
|
+
"rowKey": "plan_key",
|
|
87
|
+
"refreshMs": 5000,
|
|
88
|
+
"data": {
|
|
89
|
+
"kind": "datasource",
|
|
90
|
+
"source": "app",
|
|
91
|
+
"table": "plans",
|
|
92
|
+
"orderBy": { "field": "updated_at", "dir": "desc" },
|
|
93
|
+
"filter": [{ "field": "status", "in": ["planning", "dispatched"] }]
|
|
94
|
+
},
|
|
95
|
+
"columns": [
|
|
96
|
+
{ "field": "plan_key", "header": "Epic", "link": { "kind": "page", "page": "epic-detail", "keyField": "plan_key" } },
|
|
97
|
+
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
98
|
+
{ "field": "delivery", "header": "Delivery" },
|
|
99
|
+
{ "field": "base_branch", "header": "Base branch" },
|
|
100
|
+
{ "field": "wave_label", "header": "Wave" },
|
|
101
|
+
{ "field": "task_count", "header": "Tasks" },
|
|
102
|
+
{ "field": "issue_number", "header": "Issue", "linkField": "issue_url" },
|
|
103
|
+
{ "field": "updated_at", "header": "Updated" }
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"type": "dataGrid",
|
|
109
|
+
"id": "overview-features",
|
|
110
|
+
"props": {
|
|
111
|
+
"title": "Active Features",
|
|
112
|
+
"collapsible": true,
|
|
113
|
+
"defaultCollapsed": false,
|
|
114
|
+
"showCount": true,
|
|
115
|
+
"rowKey": "feature_key",
|
|
116
|
+
"refreshMs": 5000,
|
|
117
|
+
"data": {
|
|
118
|
+
"kind": "datasource",
|
|
119
|
+
"source": "app",
|
|
120
|
+
"table": "feature_runs",
|
|
121
|
+
"orderBy": { "field": "updated_at", "dir": "desc" },
|
|
122
|
+
"filter": [{ "field": "status", "in": ["running", "awaiting_operator"] }]
|
|
123
|
+
},
|
|
124
|
+
"columns": [
|
|
125
|
+
{ "field": "feature_key", "header": "Feature", "link": { "kind": "page", "page": "feature", "keyField": "feature_key" } },
|
|
126
|
+
{ "field": "status", "header": "Status", "link": { "kind": "processExplorer", "keyField": "process_key" } },
|
|
127
|
+
{ "field": "base_branch", "header": "Base branch" },
|
|
128
|
+
{ "field": "pr_key", "header": "PR", "link": { "kind": "page", "page": "home", "keyField": "pr_key" } },
|
|
129
|
+
{ "field": "converge", "header": "Converge" },
|
|
130
|
+
{ "field": "auto_merge", "header": "Auto-merge" },
|
|
131
|
+
{ "field": "outcome", "header": "Outcome" },
|
|
132
|
+
{ "field": "updated_at", "header": "Updated" }
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
@@ -230,3 +230,57 @@ test("issue #87: plan_reviews is surfaced on the per-epic detail page", async ()
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
|
+
|
|
234
|
+
test("issue #205: overview is the landing page and first nav item", async () => {
|
|
235
|
+
const overview = JSON.parse(readFileSync(`${ROOT}pages/overview.page.json`, "utf8"));
|
|
236
|
+
|
|
237
|
+
// The overview must be the pages-surface home so it's the default destination.
|
|
238
|
+
const app = JSON.parse(readFileSync(`${ROOT}nano.app.json`, "utf8"));
|
|
239
|
+
assert(
|
|
240
|
+
app?.surfaces?.pages?.homePage === "overview",
|
|
241
|
+
"nano.app.json surfaces.pages.homePage must be \"overview\" (the landing page)",
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Every page's nav must lead with Overview so it's the first tab everywhere.
|
|
245
|
+
for (const e of readdirSync(`${ROOT}pages`, { withFileTypes: true })) {
|
|
246
|
+
if (!e.isFile() || !e.name.endsWith(".page.json")) continue;
|
|
247
|
+
const page = JSON.parse(readFileSync(`${ROOT}pages/${e.name}`, "utf8"));
|
|
248
|
+
const nav = (page.nodes ?? []).find((n: Json) => n.type === "nav");
|
|
249
|
+
if (!nav) continue;
|
|
250
|
+
const first = nav.props?.items?.[0];
|
|
251
|
+
assert(
|
|
252
|
+
first?.page === "overview" && first?.label === "Overview",
|
|
253
|
+
`${e.name}: nav must lead with the Overview tab (first item page "overview")`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Three collapsible active-work sections, one per dispatch surface, each with a
|
|
258
|
+
// live count in its header (showCount) and a persisted collapse toggle (collapsible).
|
|
259
|
+
const expected: Record<string, string[]> = {
|
|
260
|
+
pull_requests: [
|
|
261
|
+
"converging",
|
|
262
|
+
"waiting_review",
|
|
263
|
+
"escalated",
|
|
264
|
+
"waiting_deps",
|
|
265
|
+
"waiting_merge",
|
|
266
|
+
"queued",
|
|
267
|
+
"merging",
|
|
268
|
+
],
|
|
269
|
+
plans: ["planning", "dispatched"],
|
|
270
|
+
feature_runs: ["running", "awaiting_operator"],
|
|
271
|
+
};
|
|
272
|
+
const grids = (overview.nodes ?? []).filter((n: Json) => n.type === "dataGrid");
|
|
273
|
+
for (const [table, statuses] of Object.entries(expected)) {
|
|
274
|
+
const grid = grids.find((g: Json) => g.props?.data?.table === table);
|
|
275
|
+
assert(grid, `overview.page.json must have a section bound to "${table}"`);
|
|
276
|
+
assert(grid.props.collapsible === true, `overview "${table}" section must be collapsible`);
|
|
277
|
+
assert(grid.props.showCount === true, `overview "${table}" section must show a live count`);
|
|
278
|
+
const filter = grid.props?.data?.filter?.find((f: Json) => f.field === "status");
|
|
279
|
+
assert(filter, `overview "${table}" section must filter on status`);
|
|
280
|
+
assert(
|
|
281
|
+
JSON.stringify([...filter.in].sort()) === JSON.stringify([...statuses].sort()),
|
|
282
|
+
`overview "${table}" section must filter to the active statuses ${JSON.stringify(statuses)}`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|