@nanobpm/nano-workforce 0.62.0 → 0.64.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 +14 -0
- package/nano.app.json +1 -1
- package/package.json +2 -2
- 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/resources/processes/convergence-loop.bpmn +10 -0
- package/resources/processes/feature.bpmn +25 -1
- package/resources/processes/merge-loop.bpmn +2 -0
- package/resources/processes/plan-fanout.bpmn +42 -1
- package/resources/processes/retro.bpmn +20 -0
- package/scripts/pages-contract.test.ts +54 -0
- package/workers/answer-escalation/worker.ts +3 -5
- package/workers/arm-merge/worker.ts +3 -3
- package/workers/converge-feature/worker.ts +3 -5
- package/workers/ensure-base-branch/worker.ts +3 -4
- package/workers/finalize/worker.ts +4 -16
- package/workers/mark-merged/worker.ts +3 -3
- package/workers/merge/worker.ts +3 -11
- package/workers/persist-escalation/worker.ts +5 -22
- package/workers/persist-round/worker.ts +6 -16
- package/workers/record-dependency/worker.ts +5 -4
- package/workers/record-feature/worker.ts +3 -6
- package/workers/record-plan/worker.ts +3 -0
- package/workers/record-plan-review/worker.ts +3 -6
- package/workers/record-results/worker.ts +3 -3
- package/workers/record-trial-merge/worker.ts +4 -0
- package/workers/record-wave/worker.ts +3 -0
- package/workers/resolve-trial-attention/worker.ts +3 -5
- package/workers/retro-gather/worker.ts +3 -3
- package/workers/retro-record/worker.ts +4 -8
- package/workers/select-wave/worker.ts +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.64.0](https://github.com/nanobpm/nano-workforce/compare/v0.63.0...v0.64.0) (2026-08-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* type workers off the generated data envelope ([#201](https://github.com/nanobpm/nano-workforce/issues/201)) ([#208](https://github.com/nanobpm/nano-workforce/issues/208)) ([792083f](https://github.com/nanobpm/nano-workforce/commit/792083f153050862a11224288b0e27e769ab06ba)), closes [nano-ide#225](https://github.com/nano-ide/issues/225) [#225](https://github.com/nanobpm/nano-workforce/issues/225) [#228](https://github.com/nanobpm/nano-workforce/issues/228) [#211](https://github.com/nanobpm/nano-workforce/issues/211) [#211](https://github.com/nanobpm/nano-workforce/issues/211)
|
|
7
|
+
|
|
8
|
+
# [0.63.0](https://github.com/nanobpm/nano-workforce/compare/v0.62.0...v0.63.0) (2026-08-13)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* 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)
|
|
14
|
+
|
|
1
15
|
# [0.62.0](https://github.com/nanobpm/nano-workforce/compare/v0.61.1...v0.62.0) (2026-08-13)
|
|
2
16
|
|
|
3
17
|
|
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.64.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",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@nanobpm/agentic": "^0.1.0",
|
|
54
|
-
"@nanobpm/urban": "^0.
|
|
54
|
+
"@nanobpm/urban": "^0.48.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@biomejs/biome": "^2.4.11",
|
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
|
+
}
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
<nano:extend name="round" type="integer" />
|
|
29
29
|
<nano:extend name="status" type="string" optional="true" />
|
|
30
30
|
<nano:extend name="summary" type="string" optional="true" />
|
|
31
|
+
<nano:extend name="repo" type="string" optional="true" />
|
|
32
|
+
<nano:extend name="prNumber" type="integer" optional="true" />
|
|
33
|
+
<nano:extend name="prUrl" type="string" optional="true" />
|
|
34
|
+
<nano:extend name="abandonUrl" type="string" optional="true" />
|
|
31
35
|
</nano:shape>
|
|
32
36
|
<nano:shape id="EscalationIn" name="Record escalation — input">
|
|
33
37
|
<nano:extend name="prKey" type="string" />
|
|
@@ -36,6 +40,10 @@
|
|
|
36
40
|
<nano:extend name="summary" type="string" optional="true" />
|
|
37
41
|
<nano:extend name="question" type="string" optional="true" />
|
|
38
42
|
<nano:extend name="recordRound" type="boolean" optional="true" />
|
|
43
|
+
<nano:extend name="repo" type="string" optional="true" />
|
|
44
|
+
<nano:extend name="prNumber" type="integer" optional="true" />
|
|
45
|
+
<nano:extend name="prUrl" type="string" optional="true" />
|
|
46
|
+
<nano:extend name="abandonUrl" type="string" optional="true" />
|
|
39
47
|
</nano:shape>
|
|
40
48
|
<nano:shape id="EscalationOut" name="Record escalation — result">
|
|
41
49
|
<nano:extend name="escalationId" type="integer" optional="true" />
|
|
@@ -53,6 +61,8 @@
|
|
|
53
61
|
<nano:extend name="prUrl" type="string" />
|
|
54
62
|
<nano:extend name="round" type="integer" />
|
|
55
63
|
<nano:extend name="summary" type="string" optional="true" />
|
|
64
|
+
<nano:extend name="convergeOnly" type="boolean" optional="true" />
|
|
65
|
+
<nano:extend name="abandonUrl" type="string" optional="true" />
|
|
56
66
|
</nano:shape>
|
|
57
67
|
<nano:shape id="ReviewReady" name="review-ready message payload">
|
|
58
68
|
<nano:extend name="reviewId" type="integer" />
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" id="Definitions_nano_workforce_feature" targetNamespace="http://nanobpm.io/nano-workforce">
|
|
2
|
+
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:nano="https://nanobpm.io/schema/shapes/1.0" id="Definitions_nano_workforce_feature" targetNamespace="http://nanobpm.io/nano-workforce">
|
|
3
3
|
<bpmn:process id="feature" name="Single-issue feature run" isExecutable="true">
|
|
4
|
+
<bpmn:extensionElements>
|
|
5
|
+
<nano:shapes>
|
|
6
|
+
<nano:shape id="RecordFeatureIn" name="Record feature result — input">
|
|
7
|
+
<nano:extend name="featureKey" type="string" />
|
|
8
|
+
<nano:extend name="status" type="string" optional="true" />
|
|
9
|
+
<nano:extend name="pr" type="string" optional="true" />
|
|
10
|
+
<nano:extend name="summary" type="string" optional="true" />
|
|
11
|
+
</nano:shape>
|
|
12
|
+
<nano:shape id="ConvergeFeatureIn" name="Converge feature PR — input">
|
|
13
|
+
<nano:extend name="featureKey" type="string" />
|
|
14
|
+
<nano:extend name="prKey" type="string" optional="true" />
|
|
15
|
+
<nano:extend name="autoMerge" type="boolean" optional="true" />
|
|
16
|
+
</nano:shape>
|
|
17
|
+
</nano:shapes>
|
|
18
|
+
</bpmn:extensionElements>
|
|
4
19
|
<bpmn:startEvent id="Start" name="Issue submitted">
|
|
5
20
|
<bpmn:outgoing>f_toEnsureBase</bpmn:outgoing>
|
|
6
21
|
</bpmn:startEvent>
|
|
7
22
|
<bpmn:serviceTask id="ensure-base-branch" name="Ensure base branch">
|
|
8
23
|
<bpmn:extensionElements>
|
|
9
24
|
<zeebe:taskDefinition type="pr.ensure-base-branch" />
|
|
25
|
+
<zeebe:properties>
|
|
26
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="EnsureBaseBranchIn" />
|
|
27
|
+
</zeebe:properties>
|
|
10
28
|
<zeebe:ioMapping>
|
|
11
29
|
<zeebe:input source="=repo" target="repo" />
|
|
12
30
|
<zeebe:input source="=baseBranch" target="baseBranch" />
|
|
@@ -59,6 +77,9 @@
|
|
|
59
77
|
<bpmn:serviceTask id="record-feature" name="Record result">
|
|
60
78
|
<bpmn:extensionElements>
|
|
61
79
|
<zeebe:taskDefinition type="pr.record-feature" />
|
|
80
|
+
<zeebe:properties>
|
|
81
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="RecordFeatureIn" />
|
|
82
|
+
</zeebe:properties>
|
|
62
83
|
</bpmn:extensionElements>
|
|
63
84
|
<bpmn:incoming>w_done</bpmn:incoming>
|
|
64
85
|
<bpmn:incoming>w_abandon</bpmn:incoming>
|
|
@@ -98,6 +119,9 @@
|
|
|
98
119
|
<bpmn:serviceTask id="converge" name="Hand off to convergence loop">
|
|
99
120
|
<bpmn:extensionElements>
|
|
100
121
|
<zeebe:taskDefinition type="pr.converge-feature" />
|
|
122
|
+
<zeebe:properties>
|
|
123
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="ConvergeFeatureIn" />
|
|
124
|
+
</zeebe:properties>
|
|
101
125
|
</bpmn:extensionElements>
|
|
102
126
|
<bpmn:incoming>f_converge</bpmn:incoming>
|
|
103
127
|
<bpmn:outgoing>f_toEndConverged</bpmn:outgoing>
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
<nano:extend name="prKey" type="string" />
|
|
42
42
|
<nano:extend name="repo" type="string" />
|
|
43
43
|
<nano:extend name="prNumber" type="integer" />
|
|
44
|
+
<nano:extend name="round" type="integer" optional="true" />
|
|
45
|
+
<nano:extend name="abandonUrl" type="string" optional="true" />
|
|
44
46
|
</nano:shape>
|
|
45
47
|
<nano:shape id="MergeAttemptOut" name="Attempt merge — result">
|
|
46
48
|
<nano:extend name="mergeStatus" type="string" />
|
|
@@ -1,17 +1,46 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" id="Definitions_nano_workforce_plan" targetNamespace="http://nanobpm.io/nano-workforce">
|
|
2
|
+
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:nano="https://nanobpm.io/schema/shapes/1.0" id="Definitions_nano_workforce_plan" targetNamespace="http://nanobpm.io/nano-workforce">
|
|
3
3
|
<bpmn:message id="Message_waveMerged" name="wave-merged">
|
|
4
4
|
<bpmn:extensionElements>
|
|
5
5
|
<zeebe:subscription correlationKey="=planKey" />
|
|
6
6
|
</bpmn:extensionElements>
|
|
7
7
|
</bpmn:message>
|
|
8
8
|
<bpmn:process id="plan-fanout" name="Agent Fleet — plan & implement" isExecutable="true">
|
|
9
|
+
<bpmn:extensionElements>
|
|
10
|
+
<nano:shapes>
|
|
11
|
+
<nano:shape id="EnsureBaseBranchIn" name="Ensure base branch — input">
|
|
12
|
+
<nano:extend name="repo" type="string" />
|
|
13
|
+
<nano:extend name="baseBranch" type="string" />
|
|
14
|
+
</nano:shape>
|
|
15
|
+
<nano:shape id="RecordPlanReviewIn" name="Record plan review — input">
|
|
16
|
+
<nano:extend name="planKey" type="string" />
|
|
17
|
+
<nano:extend name="approved" type="boolean" optional="true" />
|
|
18
|
+
<nano:extend name="findings" type="string" optional="true" />
|
|
19
|
+
<nano:extend name="planReviewEpoch" type="integer" optional="true" />
|
|
20
|
+
</nano:shape>
|
|
21
|
+
<nano:shape id="SelectWaveIn" name="Select wave — input">
|
|
22
|
+
<nano:extend name="planKey" type="string" />
|
|
23
|
+
<nano:extend name="currentWave" type="integer" />
|
|
24
|
+
</nano:shape>
|
|
25
|
+
<nano:shape id="ResolveTrialAttentionIn" name="Clear trial attention — input">
|
|
26
|
+
<nano:extend name="planKey" type="string" />
|
|
27
|
+
<nano:extend name="currentWave" type="integer" optional="true" />
|
|
28
|
+
<nano:extend name="trialMergeWave" type="integer" optional="true" />
|
|
29
|
+
</nano:shape>
|
|
30
|
+
<nano:shape id="RecordResultsIn" name="Finalize plan — input">
|
|
31
|
+
<nano:extend name="planKey" type="string" />
|
|
32
|
+
</nano:shape>
|
|
33
|
+
</nano:shapes>
|
|
34
|
+
</bpmn:extensionElements>
|
|
9
35
|
<bpmn:startEvent id="Start" name="Issue submitted">
|
|
10
36
|
<bpmn:outgoing>f_toEnsureBase</bpmn:outgoing>
|
|
11
37
|
</bpmn:startEvent>
|
|
12
38
|
<bpmn:serviceTask id="ensure-base-branch" name="Ensure base branch">
|
|
13
39
|
<bpmn:extensionElements>
|
|
14
40
|
<zeebe:taskDefinition type="pr.ensure-base-branch" />
|
|
41
|
+
<zeebe:properties>
|
|
42
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="EnsureBaseBranchIn" />
|
|
43
|
+
</zeebe:properties>
|
|
15
44
|
<zeebe:ioMapping>
|
|
16
45
|
<zeebe:input source="=repo" target="repo" />
|
|
17
46
|
<zeebe:input source="=baseBranch" target="baseBranch" />
|
|
@@ -56,6 +85,9 @@
|
|
|
56
85
|
<bpmn:serviceTask id="record-plan-review" name="Record review & decide">
|
|
57
86
|
<bpmn:extensionElements>
|
|
58
87
|
<zeebe:taskDefinition type="pr.record-plan-review" />
|
|
88
|
+
<zeebe:properties>
|
|
89
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="RecordPlanReviewIn" />
|
|
90
|
+
</zeebe:properties>
|
|
59
91
|
</bpmn:extensionElements>
|
|
60
92
|
<bpmn:incoming>f_toRecordPlanReview</bpmn:incoming>
|
|
61
93
|
<bpmn:outgoing>f_toGwPlanReview</bpmn:outgoing>
|
|
@@ -93,6 +125,9 @@
|
|
|
93
125
|
<bpmn:serviceTask id="select-wave" name="Select wave">
|
|
94
126
|
<bpmn:extensionElements>
|
|
95
127
|
<zeebe:taskDefinition type="pr.select-wave" />
|
|
128
|
+
<zeebe:properties>
|
|
129
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="SelectWaveIn" />
|
|
130
|
+
</zeebe:properties>
|
|
96
131
|
</bpmn:extensionElements>
|
|
97
132
|
<bpmn:incoming>f_plan_proceed</bpmn:incoming>
|
|
98
133
|
<bpmn:incoming>f_plan_answer_proceed</bpmn:incoming>
|
|
@@ -238,6 +273,9 @@
|
|
|
238
273
|
<bpmn:serviceTask id="resolve-trial-attention" name="Clear trial attention">
|
|
239
274
|
<bpmn:extensionElements>
|
|
240
275
|
<zeebe:taskDefinition type="pr.resolve-trial-attention" />
|
|
276
|
+
<zeebe:properties>
|
|
277
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="ResolveTrialAttentionIn" />
|
|
278
|
+
</zeebe:properties>
|
|
241
279
|
</bpmn:extensionElements>
|
|
242
280
|
<bpmn:incoming>f_toGwTrialAnswer</bpmn:incoming>
|
|
243
281
|
<bpmn:outgoing>f_resolveToTrialAnswer</bpmn:outgoing>
|
|
@@ -263,6 +301,9 @@
|
|
|
263
301
|
<bpmn:serviceTask id="record-results" name="Finalize plan">
|
|
264
302
|
<bpmn:extensionElements>
|
|
265
303
|
<zeebe:taskDefinition type="pr.record-results" />
|
|
304
|
+
<zeebe:properties>
|
|
305
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="RecordResultsIn" />
|
|
306
|
+
</zeebe:properties>
|
|
266
307
|
</bpmn:extensionElements>
|
|
267
308
|
<bpmn:incoming>f_done</bpmn:incoming>
|
|
268
309
|
<bpmn:incoming>f_trialAnswerAbandon</bpmn:incoming>
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:nano="https://nanobpm.io/schema/shapes/1.0" id="Definitions_nano_workforce_retro" targetNamespace="http://nanobpm.io/nano-workforce">
|
|
3
3
|
<bpmn:process id="retro" name="Epic Retrospective" isExecutable="true">
|
|
4
|
+
<bpmn:extensionElements>
|
|
5
|
+
<nano:shapes>
|
|
6
|
+
<nano:shape id="RetroGatherIn" name="Gather learnings — input">
|
|
7
|
+
<nano:extend name="planKey" type="string" />
|
|
8
|
+
</nano:shape>
|
|
9
|
+
<nano:shape id="RetroRecordIn" name="Record retro — input">
|
|
10
|
+
<nano:extend name="planKey" type="string" />
|
|
11
|
+
<nano:extend name="retroLearnings" type="integer" optional="true" />
|
|
12
|
+
<nano:extend name="status" type="string" optional="true" />
|
|
13
|
+
<nano:extend name="pr" type="string" optional="true" />
|
|
14
|
+
<nano:extend name="summary" type="string" optional="true" />
|
|
15
|
+
</nano:shape>
|
|
16
|
+
</nano:shapes>
|
|
17
|
+
</bpmn:extensionElements>
|
|
4
18
|
<bpmn:startEvent id="Start" name="Epic complete">
|
|
5
19
|
<bpmn:outgoing>f_start</bpmn:outgoing>
|
|
6
20
|
</bpmn:startEvent>
|
|
7
21
|
<bpmn:serviceTask id="gather" name="Gather learnings">
|
|
8
22
|
<bpmn:extensionElements>
|
|
9
23
|
<zeebe:taskDefinition type="pr.retro-gather" />
|
|
24
|
+
<zeebe:properties>
|
|
25
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="RetroGatherIn" />
|
|
26
|
+
</zeebe:properties>
|
|
10
27
|
</bpmn:extensionElements>
|
|
11
28
|
<bpmn:incoming>f_start</bpmn:incoming>
|
|
12
29
|
<bpmn:outgoing>f_toSynthesize</bpmn:outgoing>
|
|
@@ -27,6 +44,9 @@
|
|
|
27
44
|
<bpmn:serviceTask id="record" name="Record retro">
|
|
28
45
|
<bpmn:extensionElements>
|
|
29
46
|
<zeebe:taskDefinition type="pr.retro-record" />
|
|
47
|
+
<zeebe:properties>
|
|
48
|
+
<zeebe:property name="io.nanobpm.dataEnvelope.in" value="RetroRecordIn" />
|
|
49
|
+
</zeebe:properties>
|
|
30
50
|
</bpmn:extensionElements>
|
|
31
51
|
<bpmn:incoming>f_toRecord</bpmn:incoming>
|
|
32
52
|
<bpmn:outgoing>f_toEnd</bpmn:outgoing>
|
|
@@ -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
|
+
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// durable rows; it returns no variables, leaving the submitted `answer` untouched so it flows on to
|
|
18
18
|
// the next review round.
|
|
19
19
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
20
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
20
21
|
|
|
21
22
|
interface Escalation extends Record<string, unknown> {
|
|
22
23
|
id: number;
|
|
@@ -34,11 +35,8 @@ interface PullRequest extends Record<string, unknown> {
|
|
|
34
35
|
updated_at: string;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
answer?: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
38
|
+
// Input typed off the model data envelope (`PrAnswerEscalationIn` in convergence-loop.bpmn) — ADR 0040.
|
|
39
|
+
type In = WorkerInputs["pr.answer-escalation"];
|
|
42
40
|
// A string variable, or undefined when it is absent/blank. The form marks `answer` required, so a
|
|
43
41
|
// blank here would be an out-of-band completion; record it as NULL rather than an empty string.
|
|
44
42
|
function nonBlank(v: unknown): string | undefined {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// evaluates its mergeability and correlates `merge-ready`. Reached both on entry to the merge
|
|
3
3
|
// stage (after dependencies clear) and after a human answers a merge escalation (re-check).
|
|
4
4
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
5
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
7
|
+
// Input typed off the model data envelope (`MergeArmIn` in merge-loop.bpmn) — ADR 0040.
|
|
8
|
+
type In = WorkerInputs["pr.arm-merge"];
|
|
9
9
|
|
|
10
10
|
const handler: AppJobHandler<In> = async (job, app) => {
|
|
11
11
|
await app.data.table("pull_requests", "pr_key").update(job.variables.prKey, {
|
|
@@ -13,12 +13,10 @@
|
|
|
13
13
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
14
14
|
import { featureRuns } from "../../app/feature.ts";
|
|
15
15
|
import { MAX_ROUNDS, parsePr, submitPr } from "../../app/service.ts";
|
|
16
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
prKey?: unknown;
|
|
20
|
-
autoMerge?: unknown;
|
|
21
|
-
}
|
|
18
|
+
// Input typed off the model data envelope (`ConvergeFeatureIn` in feature.bpmn) — ADR 0040.
|
|
19
|
+
type In = WorkerInputs["pr.converge-feature"];
|
|
22
20
|
|
|
23
21
|
const handler: AppJobHandler<In, Record<string, never>> = async (job, app) => {
|
|
24
22
|
const featureKey = job.variables.featureKey;
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
// wrong-rooted branch).
|
|
11
11
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
12
12
|
import { type EnsureBaseBranchResult, ensureBaseBranch } from "../../app/github.ts";
|
|
13
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
baseBranch: string;
|
|
17
|
-
}
|
|
15
|
+
// Input typed off the model data envelope (`EnsureBaseBranchIn` in plan-fanout.bpmn) — ADR 0040.
|
|
16
|
+
type In = WorkerInputs["pr.ensure-base-branch"];
|
|
18
17
|
interface Out extends Record<string, unknown> {
|
|
19
18
|
baseBranchResult: EnsureBaseBranchResult;
|
|
20
19
|
}
|
|
@@ -5,23 +5,11 @@ import type { AppJobHandler } from "@nanobpm/urban";
|
|
|
5
5
|
import { abandonTokenFromUrl } from "../../app/abandon.ts";
|
|
6
6
|
import { maybeStartRetro } from "../../app/retro.ts";
|
|
7
7
|
import { AUTO_MERGE, ensurePr, startMerge } from "../../app/service.ts";
|
|
8
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
prKey: string;
|
|
13
|
-
repo: string;
|
|
14
|
-
prNumber: number;
|
|
15
|
-
prUrl: string;
|
|
16
|
-
round: number;
|
|
17
|
-
summary?: string;
|
|
18
|
-
// Per-request review-only override: when true, stop at `converged` and never hand off to the
|
|
19
|
-
// merge-loop even if auto-merge is on globally. Set at submit time, carried on the instance.
|
|
20
|
-
convergeOnly?: boolean;
|
|
21
|
-
// The per-PR abandon capability URL the agent was handed; its token is preserved on a heal so
|
|
22
|
-
// the agent's cooperative-abort check keeps resolving (see ensurePr).
|
|
23
|
-
abandonUrl?: string;
|
|
24
|
-
}
|
|
10
|
+
// Input typed off the model data envelope (`PrFinalizeIn` in convergence-loop.bpmn) — ADR 0040.
|
|
11
|
+
// Framework-injected `io.nanobpm.agentResult` is read through the `Record`-typed `transcriptOf`.
|
|
12
|
+
type In = WorkerInputs["pr.finalize"];
|
|
25
13
|
|
|
26
14
|
const AGENT_RESULT_KEY = "io.nanobpm.agentResult";
|
|
27
15
|
function transcriptOf(vars: Record<string, unknown>): string | null {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// `merged` state; the merge audit trail is written by pr.merge, so this only closes the row out.
|
|
3
3
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
4
4
|
import { maybeStartRetro } from "../../app/retro.ts";
|
|
5
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
7
|
+
// Input typed off the model data envelope (`MarkMergedIn` in merge-loop.bpmn) — ADR 0040.
|
|
8
|
+
type In = WorkerInputs["pr.mark-merged"];
|
|
9
9
|
|
|
10
10
|
const handler: AppJobHandler<In> = async (job, app) => {
|
|
11
11
|
const now = new Date().toISOString();
|
package/workers/merge/worker.ts
CHANGED
|
@@ -15,18 +15,10 @@ import { checkBaseTarget, classifyBaseGuard } from "../../app/baseGuard.ts";
|
|
|
15
15
|
import { enqueueViaComment, fetchPrState, mergePr } from "../../app/github.ts";
|
|
16
16
|
import { classifyMergeLanding, DEFAULT_MERGE_PROTOCOL, loadMergeProtocol } from "../../app/mergeProtocol.ts";
|
|
17
17
|
import { ensurePr, MERGE_ADMIN, MERGE_METHOD } from "../../app/service.ts";
|
|
18
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
repo: string;
|
|
22
|
-
prNumber: number;
|
|
23
|
-
// Carried by the merge-loop instance (startMerge sets it to the converged round); passed to
|
|
24
|
-
// the heal so a reconstructed row reflects the real round, not a 1-based default.
|
|
25
|
-
round?: number;
|
|
26
|
-
// The per-PR abandon capability URL the agent was handed; its token is preserved on a heal so
|
|
27
|
-
// the agent's cooperative-abort check keeps resolving (see ensurePr).
|
|
28
|
-
abandonUrl?: string;
|
|
29
|
-
}
|
|
20
|
+
// Input typed off the model data envelope (`MergeAttemptIn` in merge-loop.bpmn) — ADR 0040.
|
|
21
|
+
type In = WorkerInputs["pr.merge"];
|
|
30
22
|
|
|
31
23
|
interface Out extends Record<string, unknown> {
|
|
32
24
|
mergeStatus: "merged" | "queued" | "blocked";
|
|
@@ -17,29 +17,12 @@ import type { AppJobHandler } from "@nanobpm/urban";
|
|
|
17
17
|
import { abandonTokenFromUrl } from "../../app/abandon.ts";
|
|
18
18
|
import { classifyEscalation } from "../../app/escalationTaxonomy.ts";
|
|
19
19
|
import { ensurePr, parsePr } from "../../app/service.ts";
|
|
20
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
20
21
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
round: number;
|
|
26
|
-
status?: string;
|
|
27
|
-
summary?: string;
|
|
28
|
-
question?: string;
|
|
29
|
-
// Carried by the convergence-loop instance so a missing `pull_requests` parent can be
|
|
30
|
-
// reconstructed before the FK-child `rounds`/`escalations` inserts.
|
|
31
|
-
repo?: string;
|
|
32
|
-
prNumber?: number;
|
|
33
|
-
prUrl?: string;
|
|
34
|
-
// The per-PR abandon capability URL the agent was handed; its token is preserved on a heal so
|
|
35
|
-
// the agent's cooperative-abort check keeps resolving (see ensurePr).
|
|
36
|
-
abandonUrl?: string;
|
|
37
|
-
// False on the "review stalled" arm: `persist-round` already recorded this `round` as
|
|
38
|
-
// `addressed`, so this escalation must not insert a second `rounds` row for the same
|
|
39
|
-
// `pr_key`/`round_no` (which would record one round as both addressed and blocked). Absent
|
|
40
|
-
// on the agent-raised / max-rounds arms, where no prior round row exists — so it defaults on.
|
|
41
|
-
recordRound?: boolean;
|
|
42
|
-
}
|
|
22
|
+
// Input typed off the model data envelope (`EscalationIn` in convergence-loop.bpmn) — ADR 0040.
|
|
23
|
+
// Framework-injected variables (`io.nanobpm.agentResult`, `agent`) are read through the
|
|
24
|
+
// `Record`-typed helpers below rather than the envelope.
|
|
25
|
+
type In = WorkerInputs["pr.persist-escalation"];
|
|
43
26
|
|
|
44
27
|
// A string variable, or undefined when it is absent, empty, or whitespace-only.
|
|
45
28
|
// The write boundary owns *type* defaults (undefined -> column DEFAULT/NULL); this
|
|
@@ -7,23 +7,13 @@
|
|
|
7
7
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
8
8
|
import { abandonTokenFromUrl } from "../../app/abandon.ts";
|
|
9
9
|
import { ensurePr, parsePr } from "../../app/service.ts";
|
|
10
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
10
11
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
status?: string;
|
|
17
|
-
summary?: string;
|
|
18
|
-
// Carried by the convergence-loop instance (set at createInstance) so a missing
|
|
19
|
-
// `pull_requests` parent can be reconstructed before the FK-child `rounds` insert.
|
|
20
|
-
repo?: string;
|
|
21
|
-
prNumber?: number;
|
|
22
|
-
prUrl?: string;
|
|
23
|
-
// The per-PR abandon capability URL the review agent was handed; its token is preserved on a
|
|
24
|
-
// heal so the agent's cooperative-abort check keeps resolving (see ensurePr).
|
|
25
|
-
abandonUrl?: string;
|
|
26
|
-
}
|
|
12
|
+
// Input is typed off the model data envelope (`PrPersistRoundIn` in convergence-loop.bpmn),
|
|
13
|
+
// the single source of truth for this worker's wire contract (ADR 0040). Framework-injected
|
|
14
|
+
// variables the handler still reads (`io.nanobpm.agentResult`, `agent`) are accessed through the
|
|
15
|
+
// `Record<string, unknown>`-typed helpers below rather than the envelope.
|
|
16
|
+
type In = WorkerInputs["pr.persist-round"];
|
|
27
17
|
|
|
28
18
|
// The harness records the agent's full (byte-capped) stdout on the result envelope; keep it
|
|
29
19
|
// for audit so a human can see what the agent did this round.
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
// already-recorded edge is a no-op.
|
|
14
14
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
15
15
|
import { ensurePr, parsePr } from "../../app/service.ts";
|
|
16
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// Input typed off the model data envelope (`RecordDepIn` in merge-loop.bpmn) — ADR 0040. The
|
|
19
|
+
// scalar `dependsOn` carries the agent's `owner/repo#N` refs as one string; `parseDependsOn` below
|
|
20
|
+
// still tolerates the legacy array shape defensively.
|
|
21
|
+
type In = WorkerInputs["pr.record-dependency"];
|
|
21
22
|
|
|
22
23
|
interface DependencyRow {
|
|
23
24
|
pr_key: string;
|
|
@@ -14,13 +14,10 @@
|
|
|
14
14
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
15
15
|
import { type FeatureRunStatus, featureRuns } from "../../app/feature.ts";
|
|
16
16
|
import { parsePr } from "../../app/service.ts";
|
|
17
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
status?: unknown;
|
|
21
|
-
pr?: unknown;
|
|
22
|
-
summary?: unknown;
|
|
23
|
-
}
|
|
19
|
+
// Input typed off the model data envelope (`RecordFeatureIn` in feature.bpmn) — ADR 0040.
|
|
20
|
+
type In = WorkerInputs["pr.record-feature"];
|
|
24
21
|
interface Out extends Record<string, unknown> {
|
|
25
22
|
featureStatus: FeatureRunStatus;
|
|
26
23
|
prKey: string | null;
|
|
@@ -25,6 +25,9 @@ interface RawTask {
|
|
|
25
25
|
prompt?: unknown;
|
|
26
26
|
dependsOn?: unknown;
|
|
27
27
|
}
|
|
28
|
+
// NOTE (issue #211): this worker reads an ARRAY input (`tasks: RawTask[]`), which the scalar-only
|
|
29
|
+
// `nano:dataEnvelope` cannot express, so it keeps a hand-written `interface In` pending a decision
|
|
30
|
+
// on extending the envelope schema to arrays. See the PR body's "array inputs" escalation.
|
|
28
31
|
interface In extends Record<string, unknown> {
|
|
29
32
|
planKey: string;
|
|
30
33
|
tasks?: RawTask[];
|
|
@@ -24,13 +24,10 @@ import {
|
|
|
24
24
|
type PlanReview,
|
|
25
25
|
planReviews,
|
|
26
26
|
} from "../../app/plan.ts";
|
|
27
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
approved?: unknown;
|
|
31
|
-
findings?: unknown;
|
|
32
|
-
planReviewEpoch?: unknown;
|
|
33
|
-
}
|
|
29
|
+
// Input typed off the model data envelope (`RecordPlanReviewIn` in plan-fanout.bpmn) — ADR 0040.
|
|
30
|
+
type In = WorkerInputs["pr.record-plan-review"];
|
|
34
31
|
interface Out extends Record<string, unknown> {
|
|
35
32
|
planApproved: boolean;
|
|
36
33
|
planEscalated: boolean;
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
15
15
|
import { BpmnError } from "@nanobpm/urban";
|
|
16
16
|
import { planTasks } from "../../app/plan.ts";
|
|
17
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
19
|
+
// Input typed off the model data envelope (`RecordResultsIn` in plan-fanout.bpmn) — ADR 0040.
|
|
20
|
+
type In = WorkerInputs["pr.record-results"];
|
|
21
21
|
|
|
22
22
|
const handler: AppJobHandler<In> = async (job, app) => {
|
|
23
23
|
const { planKey } = job.variables;
|
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
trialMergeTaskId,
|
|
10
10
|
} from "../../app/trialMerge.ts";
|
|
11
11
|
|
|
12
|
+
// NOTE (issue #211): this worker reads ARRAY inputs (`waveOpenHeads`, `conflicts`, `failing`), which
|
|
13
|
+
// the scalar-only `nano:dataEnvelope` cannot express, so it keeps a hand-written `interface In`
|
|
14
|
+
// pending a decision on extending the envelope schema to arrays. See the PR body's "array inputs"
|
|
15
|
+
// escalation.
|
|
12
16
|
interface In extends Record<string, unknown> {
|
|
13
17
|
planKey: string;
|
|
14
18
|
currentWave?: unknown;
|
|
@@ -38,6 +38,9 @@ interface Result {
|
|
|
38
38
|
interface WaveTaskIn {
|
|
39
39
|
id?: unknown;
|
|
40
40
|
}
|
|
41
|
+
// NOTE (issue #211): this worker reads ARRAY inputs (`waveTasks[]`, `waveResults[]`), which the
|
|
42
|
+
// scalar-only `nano:dataEnvelope` cannot express, so it keeps a hand-written `interface In` pending
|
|
43
|
+
// a decision on extending the envelope schema to arrays. See the PR body's "array inputs" escalation.
|
|
41
44
|
interface In extends Record<string, unknown> {
|
|
42
45
|
planKey: string;
|
|
43
46
|
currentWave: number;
|
|
@@ -14,12 +14,10 @@
|
|
|
14
14
|
// and logged rather than thrown.
|
|
15
15
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
16
16
|
import { resolveTrialMergeAttention } from "../../app/trialMerge.ts";
|
|
17
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
currentWave?: unknown;
|
|
21
|
-
trialMergeWave?: unknown;
|
|
22
|
-
}
|
|
19
|
+
// Input typed off the model data envelope (`ResolveTrialAttentionIn` in plan-fanout.bpmn) — ADR 0040.
|
|
20
|
+
type In = WorkerInputs["pr.resolve-trial-attention"];
|
|
23
21
|
|
|
24
22
|
const waveNo = (v: unknown): number => {
|
|
25
23
|
const n = Math.trunc(Number(v));
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// agent reflects on real material rather than re-deriving it.
|
|
6
6
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
7
7
|
import { gatherRetro, renderRetroBrief } from "../../app/retro.ts";
|
|
8
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
10
|
+
// Input typed off the model data envelope (`RetroGatherIn` in retro.bpmn) — ADR 0040.
|
|
11
|
+
type In = WorkerInputs["pr.retro-gather"];
|
|
12
12
|
|
|
13
13
|
interface Out extends Record<string, unknown> {
|
|
14
14
|
retroDigest: string;
|
|
@@ -8,16 +8,12 @@
|
|
|
8
8
|
// exposes the raw transcript under the `io.nanobpm.agentResult` envelope's `.output`.
|
|
9
9
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
10
10
|
import { recordRetro } from "../../app/retro.ts";
|
|
11
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
11
12
|
|
|
12
13
|
const AGENT_RESULT_KEY = "io.nanobpm.agentResult";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
retroLearnings?: number;
|
|
17
|
-
status?: unknown; // filed | skipped | blocked
|
|
18
|
-
pr?: unknown; // "<owner>/<repo>#<n>" of the promotion PR, when filed
|
|
19
|
-
summary?: unknown;
|
|
20
|
-
}
|
|
15
|
+
// Input typed off the model data envelope (`RetroRecordIn` in retro.bpmn) — ADR 0040.
|
|
16
|
+
type In = WorkerInputs["pr.retro-record"];
|
|
21
17
|
|
|
22
18
|
function asStr(v: unknown): string | null {
|
|
23
19
|
return typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
@@ -42,7 +38,7 @@ const handler: AppJobHandler<In> = async (job, app) => {
|
|
|
42
38
|
const summary = asStr(job.variables.summary);
|
|
43
39
|
|
|
44
40
|
// biome-ignore lint/plugin: runtime/framework contract boundary for external data shape
|
|
45
|
-
const env = job.variables[AGENT_RESULT_KEY] as { output?: unknown } | undefined;
|
|
41
|
+
const env = (job.variables as Record<string, unknown>)[AGENT_RESULT_KEY] as { output?: unknown } | undefined;
|
|
46
42
|
const report = typeof env?.output === "string" ? env.output : null;
|
|
47
43
|
|
|
48
44
|
await recordRetro(app.data, planKey, {
|
|
@@ -16,11 +16,10 @@
|
|
|
16
16
|
// immediately (the same 0-task path the flat fan-out already relied on).
|
|
17
17
|
import type { AppJobHandler } from "@nanobpm/urban";
|
|
18
18
|
import { plans, planTaskDeps, planTasks } from "../../app/plan.ts";
|
|
19
|
+
import type { WorkerInputs } from "../../nano-generated/worker-io.d.ts";
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
currentWave: number;
|
|
23
|
-
}
|
|
21
|
+
// Input typed off the model data envelope (`SelectWaveIn` in plan-fanout.bpmn) — ADR 0040.
|
|
22
|
+
type In = WorkerInputs["pr.select-wave"];
|
|
24
23
|
interface WaveTaskOut {
|
|
25
24
|
id: string;
|
|
26
25
|
title: string;
|