@websitelabs/n8n-nodes-software-teams 0.12.3

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 (176) hide show
  1. package/ARCHITECTURE.md +1232 -0
  2. package/CONTRACT.md +450 -0
  3. package/README.md +491 -0
  4. package/dist/agents/software-teams-architect.md +155 -0
  5. package/dist/agents/software-teams-backend.md +93 -0
  6. package/dist/agents/software-teams-codebase-mapper.md +67 -0
  7. package/dist/agents/software-teams-committer.md +90 -0
  8. package/dist/agents/software-teams-debugger.md +91 -0
  9. package/dist/agents/software-teams-dev-planner.md +175 -0
  10. package/dist/agents/software-teams-devops.md +92 -0
  11. package/dist/agents/software-teams-feedback-learner.md +118 -0
  12. package/dist/agents/software-teams-frontend.md +107 -0
  13. package/dist/agents/software-teams-game-ai-engineer.md +179 -0
  14. package/dist/agents/software-teams-game-art-pipeline.md +180 -0
  15. package/dist/agents/software-teams-game-designer.md +245 -0
  16. package/dist/agents/software-teams-game-devops.md +134 -0
  17. package/dist/agents/software-teams-game-engineer.md +146 -0
  18. package/dist/agents/software-teams-game-producer.md +288 -0
  19. package/dist/agents/software-teams-game-qa.md +297 -0
  20. package/dist/agents/software-teams-game-tech-artist.md +186 -0
  21. package/dist/agents/software-teams-head-engineering.md +37 -0
  22. package/dist/agents/software-teams-perf-analyst.md +124 -0
  23. package/dist/agents/software-teams-phase-researcher.md +75 -0
  24. package/dist/agents/software-teams-plan-checker.md +87 -0
  25. package/dist/agents/software-teams-planner.md +456 -0
  26. package/dist/agents/software-teams-pr-feedback.md +127 -0
  27. package/dist/agents/software-teams-pr-generator.md +107 -0
  28. package/dist/agents/software-teams-producer.md +203 -0
  29. package/dist/agents/software-teams-product-lead.md +51 -0
  30. package/dist/agents/software-teams-programmer.md +126 -0
  31. package/dist/agents/software-teams-qa-tester.md +165 -0
  32. package/dist/agents/software-teams-quality.md +153 -0
  33. package/dist/agents/software-teams-researcher.md +151 -0
  34. package/dist/agents/software-teams-security.md +126 -0
  35. package/dist/agents/software-teams-ux-designer.md +92 -0
  36. package/dist/agents/software-teams-verifier.md +87 -0
  37. package/dist/credentials/SoftwareTeamsApi.credentials.d.ts +18 -0
  38. package/dist/credentials/SoftwareTeamsApi.credentials.d.ts.map +1 -0
  39. package/dist/credentials/SoftwareTeamsApi.credentials.js +110 -0
  40. package/dist/credentials/SoftwareTeamsApi.credentials.js.map +1 -0
  41. package/dist/credentials/softwareTeamsApi.svg +14 -0
  42. package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.d.ts +23 -0
  43. package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.d.ts.map +1 -0
  44. package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.js +308 -0
  45. package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.js.map +1 -0
  46. package/dist/nodes/SoftwareTeamsAgent/softwareTeamsAgent.svg +18 -0
  47. package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.d.ts +24 -0
  48. package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.d.ts.map +1 -0
  49. package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.js +2635 -0
  50. package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.js.map +1 -0
  51. package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.svg +6 -0
  52. package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.d.ts +6 -0
  53. package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.d.ts.map +1 -0
  54. package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.js +231 -0
  55. package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.js.map +1 -0
  56. package/dist/nodes/SoftwareTeamsFinaliser/softwareTeamsFinaliser.svg +11 -0
  57. package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.d.ts +25 -0
  58. package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.d.ts.map +1 -0
  59. package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.js +366 -0
  60. package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.js.map +1 -0
  61. package/dist/nodes/SoftwareTeamsHitl/softwareTeamsHitl.svg +11 -0
  62. package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.d.ts +15 -0
  63. package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.d.ts.map +1 -0
  64. package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.js +373 -0
  65. package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.js.map +1 -0
  66. package/dist/nodes/SoftwareTeamsOrchestrator/softwareTeamsOrchestrator.svg +20 -0
  67. package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.d.ts +6 -0
  68. package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.d.ts.map +1 -0
  69. package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.js +2685 -0
  70. package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.js.map +1 -0
  71. package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.svg +6 -0
  72. package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.d.ts +22 -0
  73. package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.d.ts.map +1 -0
  74. package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.js +2655 -0
  75. package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.js.map +1 -0
  76. package/dist/nodes/SoftwareTeamsPrFeedback/softwareTeamsPrFeedback.svg +8 -0
  77. package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.d.ts +19 -0
  78. package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.d.ts.map +1 -0
  79. package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.js +198 -0
  80. package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.js.map +1 -0
  81. package/dist/nodes/SoftwareTeamsSlackHitl/softwareTeamsSlackHitl.svg +10 -0
  82. package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.d.ts +6 -0
  83. package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.d.ts.map +1 -0
  84. package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.js +2601 -0
  85. package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.js.map +1 -0
  86. package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.svg +6 -0
  87. package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.d.ts +20 -0
  88. package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.d.ts.map +1 -0
  89. package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.js +175 -0
  90. package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.js.map +1 -0
  91. package/dist/nodes/SoftwareTeamsWorkspace/softwareTeamsWorkspace.svg +13 -0
  92. package/dist/src/execution/single-turn.d.ts +6 -0
  93. package/dist/src/execution/single-turn.d.ts.map +1 -0
  94. package/dist/src/execution/single-turn.js +2662 -0
  95. package/dist/src/execution/single-turn.js.map +1 -0
  96. package/dist/src/hitl/channels.d.ts +48 -0
  97. package/dist/src/hitl/channels.d.ts.map +1 -0
  98. package/dist/src/hitl/channels.js +297 -0
  99. package/dist/src/hitl/channels.js.map +1 -0
  100. package/dist/src/hitl/conversation-state.d.ts +45 -0
  101. package/dist/src/hitl/conversation-state.d.ts.map +1 -0
  102. package/dist/src/hitl/conversation-state.js +69 -0
  103. package/dist/src/hitl/conversation-state.js.map +1 -0
  104. package/dist/src/hitl/slack.d.ts +32 -0
  105. package/dist/src/hitl/slack.d.ts.map +1 -0
  106. package/dist/src/hitl/slack.js +202 -0
  107. package/dist/src/hitl/slack.js.map +1 -0
  108. package/dist/src/ingestion/context.d.ts +38 -0
  109. package/dist/src/ingestion/context.d.ts.map +1 -0
  110. package/dist/src/ingestion/context.js +2501 -0
  111. package/dist/src/ingestion/context.js.map +1 -0
  112. package/dist/src/ingestion/pr-feedback.d.ts +48 -0
  113. package/dist/src/ingestion/pr-feedback.d.ts.map +1 -0
  114. package/dist/src/ingestion/pr-feedback.js +85 -0
  115. package/dist/src/ingestion/pr-feedback.js.map +1 -0
  116. package/dist/src/n8n-cast.d.ts +11 -0
  117. package/dist/src/n8n-cast.d.ts.map +1 -0
  118. package/dist/src/n8n-cast.js +17 -0
  119. package/dist/src/n8n-cast.js.map +1 -0
  120. package/dist/src/orchestration/run-state/global-store.d.ts +7 -0
  121. package/dist/src/orchestration/run-state/global-store.d.ts.map +1 -0
  122. package/dist/src/orchestration/run-state/global-store.js +27 -0
  123. package/dist/src/orchestration/run-state/global-store.js.map +1 -0
  124. package/dist/src/orchestration/run-state/ordering.d.ts +14 -0
  125. package/dist/src/orchestration/run-state/ordering.d.ts.map +1 -0
  126. package/dist/src/orchestration/run-state/ordering.js +59 -0
  127. package/dist/src/orchestration/run-state/ordering.js.map +1 -0
  128. package/dist/src/orchestration/run-state/persistence.d.ts +9 -0
  129. package/dist/src/orchestration/run-state/persistence.d.ts.map +1 -0
  130. package/dist/src/orchestration/run-state/persistence.js +29 -0
  131. package/dist/src/orchestration/run-state/persistence.js.map +1 -0
  132. package/dist/src/orchestration/run-state/planning.d.ts +17 -0
  133. package/dist/src/orchestration/run-state/planning.d.ts.map +1 -0
  134. package/dist/src/orchestration/run-state/planning.js +117 -0
  135. package/dist/src/orchestration/run-state/planning.js.map +1 -0
  136. package/dist/src/orchestration/run-state/readiness.d.ts +20 -0
  137. package/dist/src/orchestration/run-state/readiness.d.ts.map +1 -0
  138. package/dist/src/orchestration/run-state/readiness.js +105 -0
  139. package/dist/src/orchestration/run-state/readiness.js.map +1 -0
  140. package/dist/src/orchestration/run-state/shapes.d.ts +53 -0
  141. package/dist/src/orchestration/run-state/shapes.d.ts.map +1 -0
  142. package/dist/src/orchestration/run-state/shapes.js +3 -0
  143. package/dist/src/orchestration/run-state/shapes.js.map +1 -0
  144. package/dist/src/orchestration/run-state/transitions.d.ts +46 -0
  145. package/dist/src/orchestration/run-state/transitions.d.ts.map +1 -0
  146. package/dist/src/orchestration/run-state/transitions.js +133 -0
  147. package/dist/src/orchestration/run-state/transitions.js.map +1 -0
  148. package/dist/src/orchestration/run-state.d.ts +8 -0
  149. package/dist/src/orchestration/run-state.d.ts.map +1 -0
  150. package/dist/src/orchestration/run-state.js +35 -0
  151. package/dist/src/orchestration/run-state.js.map +1 -0
  152. package/dist/src/output/github.d.ts +39 -0
  153. package/dist/src/output/github.d.ts.map +1 -0
  154. package/dist/src/output/github.js +2492 -0
  155. package/dist/src/output/github.js.map +1 -0
  156. package/dist/src/repo/git.d.ts +71 -0
  157. package/dist/src/repo/git.d.ts.map +1 -0
  158. package/dist/src/repo/git.js +207 -0
  159. package/dist/src/repo/git.js.map +1 -0
  160. package/dist/src/repo/merge.d.ts +36 -0
  161. package/dist/src/repo/merge.d.ts.map +1 -0
  162. package/dist/src/repo/merge.js +133 -0
  163. package/dist/src/repo/merge.js.map +1 -0
  164. package/dist/src/repo/repo-context.d.ts +23 -0
  165. package/dist/src/repo/repo-context.d.ts.map +1 -0
  166. package/dist/src/repo/repo-context.js +10 -0
  167. package/dist/src/repo/repo-context.js.map +1 -0
  168. package/dist/src/repo/teardown.d.ts +38 -0
  169. package/dist/src/repo/teardown.d.ts.map +1 -0
  170. package/dist/src/repo/teardown.js +171 -0
  171. package/dist/src/repo/teardown.js.map +1 -0
  172. package/dist/src/repo/validate.d.ts +4 -0
  173. package/dist/src/repo/validate.d.ts.map +1 -0
  174. package/dist/src/repo/validate.js +42 -0
  175. package/dist/src/repo/validate.js.map +1 -0
  176. package/package.json +73 -0
package/CONTRACT.md ADDED
@@ -0,0 +1,450 @@
1
+ # Inter-node data contract — `NodeEnvelope`
2
+
3
+ > **Status:** Accepted (W1 foundation). Normative source of truth for AC3.
4
+ > **Plan:** `1-01-n8n-nodes` · **Task:** T1 (`software-teams-architect`).
5
+ > **Implemented verbatim by T3**; the `contract-check` gate asserts this shape;
6
+ > T8 tests it. Pairs with [`ARCHITECTURE.md`](./ARCHITECTURE.md).
7
+ >
8
+ > Every agent node, the Orchestrator node, the trigger nodes, and the GitHub
9
+ > output node **emit on their output port and accept on their input port the
10
+ > single JSON object defined here.** A node consuming an upstream node's output
11
+ > needs only this contract — nothing about the upstream node's internals.
12
+
13
+ ---
14
+
15
+ ## 1. The envelope (normative)
16
+
17
+ ```ts
18
+ /**
19
+ * The one and only object passed between every Software Teams n8n node.
20
+ * No field is optional except where stated; no field is `undefined`.
21
+ */
22
+ export interface NodeEnvelope {
23
+ /** Stable run/conversation id. Carried UNCHANGED node-to-node. The key the
24
+ * Slack resume (T10) and run-state (T9) match on. (R-05) */
25
+ correlationId: string;
26
+
27
+ /** The specialist that produced (output) / should consume (input) this
28
+ * envelope — e.g. "software-teams-frontend". Matches a name in agents/*.md. */
29
+ agentId: string;
30
+
31
+ /** Exactly these three values — string-literal union, nothing else.
32
+ * 'needs-input' triggers the Slack HITL flow (T10). */
33
+ status: 'ok' | 'error' | 'needs-input';
34
+
35
+ /** The turn's inputs. */
36
+ input: {
37
+ /** The user-turn instruction for this node. */
38
+ prompt: string;
39
+ /** The upstream agent's structured handoff. `unknown` on the wire; folded
40
+ * into the prompt by the merge strategy in §4. */
41
+ context: unknown;
42
+ };
43
+
44
+ /** The agent's structured turn output. */
45
+ result: {
46
+ /** The primary result body (the agent's final text). */
47
+ text: string;
48
+ };
49
+
50
+ /** Produced references. MAY be empty `[]`, never absent. T7 appends the
51
+ * created PR/issue URL here. */
52
+ artifacts: Array<{
53
+ /** e.g. "pr" | "issue" | "comment" — open vocabulary. */
54
+ type: string;
55
+ /** Absolute URL of the artifact, when one exists. */
56
+ url?: string;
57
+ }>;
58
+ }
59
+ ```
60
+
61
+ ### Field table (the table `contract-check` asserts)
62
+
63
+ | Field | Type | Required | Notes |
64
+ |-------|------|----------|-------|
65
+ | `correlationId` | `string` | **yes** | Stable run/conversation id; carried unchanged node-to-node; the key the Slack resume (T10) and run-state (T9) match on (R-05). |
66
+ | `agentId` | `string` | **yes** | The specialist that produced/should consume this envelope (e.g. `software-teams-frontend`), matching a name in `agents/*.md`. |
67
+ | `status` | `'ok' \| 'error' \| 'needs-input'` | **yes** | String-literal union — **exactly these three values**. `needs-input` triggers the Slack HITL flow (T10). |
68
+ | `input` | `{ prompt: string; context: unknown }` | **yes** | `prompt` is the user-turn instruction; `context` carries the upstream agent's structured handoff (see §4). |
69
+ | `result` | `{ text: string }` | **yes** | The agent's structured turn output; `text` is the primary result body. |
70
+ | `artifacts` | `Array<{ type: string; url?: string }>` | **yes** (may be empty `[]`) | Produced refs — e.g. `{ type: 'pr', url: '…' }`, `{ type: 'issue', url: '…' }`; T7 appends the created PR/issue URL here. |
71
+
72
+ **Invariants the gate checks:**
73
+
74
+ - All six top-level fields are present; none is `undefined`.
75
+ - `status` ∈ `{ 'ok', 'error', 'needs-input' }` — no other value passes.
76
+ - `input` has both `prompt: string` and `context` (any JSON value, incl. `null`).
77
+ - `result.text` is a `string` (possibly `""`).
78
+ - `artifacts` is an array (possibly `[]`); each element has `type: string` and an
79
+ optional `url: string`.
80
+ - `correlationId` is identical across every envelope of one run (carry-through).
81
+
82
+ ### Status semantics
83
+
84
+ | `status` | Meaning | Canvas effect |
85
+ |----------|---------|---------------|
86
+ | `ok` | Turn completed; `result.text` is the answer. | Flows to the next node. |
87
+ | `error` | Turn failed (non-zero `claude` exit, or the agent reported failure). | Short-circuits the branch; Orchestrator surfaces it (R-05). |
88
+ | `needs-input` | The agent needs a human decision/answer; the question is in `result.text`. | Parks the run for Slack HITL (T10); resumes the same `correlationId`. |
89
+
90
+ ---
91
+
92
+ ## 2. Producer / consumer rules
93
+
94
+ - **`correlationId` is immutable.** Every node copies it through verbatim. It is
95
+ generated once at run start (trigger or Orchestrator) and is the join key for
96
+ run-state (T9) and Slack resume (T10).
97
+ - **`agentId` is rewritten per hop.** On a node's *output* it names the producing
98
+ specialist. When the next node consumes it, that node sets `agentId` to **its
99
+ own** specialist before invoking (the consumer owns its identity).
100
+ - **`input.context` is the only handoff channel.** A consumer never reaches into
101
+ the producer's internals — it reads the upstream envelope and folds it into its
102
+ own `input.context` per §4.
103
+ - **`artifacts` accretes.** Downstream nodes append; they do not drop upstream
104
+ refs. The final GitHub node (T7) appends the PR/issue URL.
105
+ - **Secrets never appear** in any field (R-02). Credentials reach the worker via
106
+ the `SoftwareTeamsApi` credential type only; they are never written into the
107
+ envelope or logged.
108
+
109
+ ---
110
+
111
+ ## 3. Worked example — agent → agent handoff (context carry-through)
112
+
113
+ A frontend specialist implements a component, then hands off to QA. The
114
+ correlation id is constant; the **frontend's `result` + `artifacts` become the QA
115
+ node's `input.context`** — that is the carry-through `contract-check`/T8 assert.
116
+
117
+ **(a) Envelope emitted by the `software-teams-frontend` Agent node:**
118
+
119
+ ```json
120
+ {
121
+ "correlationId": "run-2026-06-03-CU-4821",
122
+ "agentId": "software-teams-frontend",
123
+ "status": "ok",
124
+ "input": {
125
+ "prompt": "Add a dismissible cookie-consent banner to the marketing site.",
126
+ "context": null
127
+ },
128
+ "result": {
129
+ "text": "Added <CookieBanner/> in src/components/CookieBanner.tsx, wired it into App.tsx, and persisted consent to localStorage under key `cookie-consent`."
130
+ },
131
+ "artifacts": [
132
+ { "type": "branch", "url": "https://github.com/acme/site/tree/feat/cookie-banner" }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ **(b) The canvas wires that output into the `software-teams-qa-tester` node. The QA
138
+ node builds its input envelope, folding the upstream envelope into `input.context`
139
+ (carry-through), and sets `agentId` to its own specialist:**
140
+
141
+ ```json
142
+ {
143
+ "correlationId": "run-2026-06-03-CU-4821",
144
+ "agentId": "software-teams-qa-tester",
145
+ "status": "ok",
146
+ "input": {
147
+ "prompt": "Write a regression checklist and edge cases for the new cookie-consent banner.",
148
+ "context": {
149
+ "from": "software-teams-frontend",
150
+ "upstreamStatus": "ok",
151
+ "result": {
152
+ "text": "Added <CookieBanner/> in src/components/CookieBanner.tsx, wired it into App.tsx, and persisted consent to localStorage under key `cookie-consent`."
153
+ },
154
+ "artifacts": [
155
+ { "type": "branch", "url": "https://github.com/acme/site/tree/feat/cookie-banner" }
156
+ ]
157
+ }
158
+ },
159
+ "result": { "text": "" },
160
+ "artifacts": []
161
+ }
162
+ ```
163
+
164
+ Note: `correlationId` is unchanged; `agentId` flipped to the consumer; the whole
165
+ upstream `result`/`artifacts` rode through into `input.context`; `result.text` is
166
+ empty until the QA turn runs.
167
+
168
+ **(c) After the QA node runs its single turn, it emits:**
169
+
170
+ ```json
171
+ {
172
+ "correlationId": "run-2026-06-03-CU-4821",
173
+ "agentId": "software-teams-qa-tester",
174
+ "status": "ok",
175
+ "input": { "prompt": "Write a regression checklist and edge cases for the new cookie-consent banner.", "context": { "from": "software-teams-frontend", "...": "(unchanged from (b))" } },
176
+ "result": {
177
+ "text": "Regression checklist:\n1. Banner shows on first visit (no stored consent).\n2. Dismiss persists across reload.\n3. localStorage disabled → banner still dismissable for the session.\n4. Keyboard focus trap + ESC dismiss.\n..."
178
+ },
179
+ "artifacts": []
180
+ }
181
+ ```
182
+
183
+ ---
184
+
185
+ ## 4. Upstream-context merge
186
+
187
+ > **Owned by T3** (the merge strategy is T3's to implement); **pinned here** so
188
+ > T3 implements it verbatim, T8 tests it, and every node assembles the prompt the
189
+ > same way. `input.context` is `unknown` on the wire; this section defines how it
190
+ > becomes part of the next agent's single-turn prompt.
191
+
192
+ **Chosen strategy: prepend a fenced JSON block to the user turn.**
193
+
194
+ When a node invokes `claude -p` (via `runAgentTurn` → `spawnClaude`), the wrapper
195
+ assembles the prompt **string** as:
196
+
197
+ ```
198
+ ## Upstream context
199
+ ```json
200
+ <JSON.stringify(input.context, null, 2)>
201
+ ```
202
+
203
+ ## Task
204
+ <input.prompt>
205
+ ```
206
+
207
+ Rules:
208
+
209
+ 1. If `input.context` is `null`/`undefined`/`{}`, the `## Upstream context`
210
+ block is **omitted** entirely — the prompt is just the `## Task` body. (A
211
+ first/root node has no upstream context.)
212
+ 2. `input.context` is serialised with `JSON.stringify(value, null, 2)` and fenced
213
+ as ```` ```json ````. This is deterministic, greppable, and contract-testable.
214
+ 3. The block is **prepended** so the agent reads its inheritance before the
215
+ instruction; `input.prompt` is always the final, imperative section.
216
+ 4. The merge is **read-only** on `input` — it does not mutate the envelope. The
217
+ assembled string is passed to `spawnClaude`'s `prompt` argument; the envelope
218
+ carries the structured `input.context` unchanged for the next hop.
219
+
220
+ **Worked merge** — the prompt string `runAgentTurn` builds for the QA node in §3(b):
221
+
222
+ ````text
223
+ ## Upstream context
224
+ ```json
225
+ {
226
+ "from": "software-teams-frontend",
227
+ "upstreamStatus": "ok",
228
+ "result": {
229
+ "text": "Added <CookieBanner/> in src/components/CookieBanner.tsx, wired it into App.tsx, and persisted consent to localStorage under key `cookie-consent`."
230
+ },
231
+ "artifacts": [
232
+ { "type": "branch", "url": "https://github.com/acme/site/tree/feat/cookie-banner" }
233
+ ]
234
+ }
235
+ ```
236
+
237
+ ## Task
238
+ Write a regression checklist and edge cases for the new cookie-consent banner.
239
+ ````
240
+
241
+ **Alternative considered & rejected:** appending the context as a *system* message
242
+ (`claude --append-system-prompt`). Rejected because the `spawnClaude` primitive
243
+ ([`src/utils/claude.ts`](../src/utils/claude.ts)) exposes only a single `prompt`
244
+ argument and no system-prompt channel; routing context through the user turn keeps
245
+ the node on the existing primitive with zero CLI-surface coupling, and keeps the
246
+ merged prompt fully visible to the contract tests.
247
+
248
+ ---
249
+
250
+ ## 5. `needs-input` marker convention (HITL hook)
251
+
252
+ So the adapter can set `status: 'needs-input'` deterministically, the single-turn
253
+ prompt instructs the agent to end its turn with a machine-detectable marker when it
254
+ needs a human decision — recommended form:
255
+
256
+ ```
257
+ NEEDS_INPUT: <the question for the human>
258
+ ```
259
+
260
+ `runAgentTurn` (T3) detects the marker, sets `status: 'needs-input'`, and places
261
+ the question in `result.text`; T10's Slack HITL posts it, parks the run on the
262
+ `correlationId`, and resumes the same agent with the human's reply folded back in
263
+ as `input.context`. The exact marker grammar is finalised by T3/T10; this contract
264
+ fixes only that `needs-input` is signalled in-band and surfaced via `result.text`.
265
+
266
+ ---
267
+
268
+ ## 6. Addendum — repo-execution additive fields (plan `1-04`, ADR-002)
269
+
270
+ > **Status:** Accepted. Pairs with [`ARCHITECTURE.md`](./ARCHITECTURE.md) ADR-002.
271
+ > **Implemented by** T2 (`RepoContext` threading), T4 (`changeRef` capture/apply),
272
+ > T8 (run-state aggregation), T9 (Finaliser). **Additive and optional ONLY.**
273
+
274
+ ### 6.1 The six top-level invariants are UNCHANGED
275
+
276
+ The six fields of §1 (`correlationId`, `agentId`, `status`, `input`, `result`,
277
+ `artifacts`), their types, the invariants the `contract-check` gate asserts (§1), and
278
+ the **§4 upstream-context merge are all unchanged**. Existing envelope/contract tests
279
+ stay green; T11 adds new-field assertions on top.
280
+
281
+ Two additive optional top-level fields are introduced below (`repo`, `changeRef`).
282
+ Because they are top-level siblings of `input` — and `assemblePrompt` reads only
283
+ `input` — neither field is ever serialised into the model prompt.
284
+
285
+ ### 6.2 `RepoContext` is OFF-WIRE — not an envelope field
286
+
287
+ The run's repository checkout (`RepoContext`, ADR-002 Decision D) is threaded as a
288
+ **typed optional parameter** on `runAgentTurn(input, repoContext?)` — it is **never
289
+ serialised onto the envelope**, never part of `input`/`input.context`, and never
290
+ reaches `assemblePrompt` or the model prompt. It is documented here only to state that
291
+ it does **not** touch the wire contract. (R-18: no §4-merge bleed; R-02: no secret
292
+ path.)
293
+
294
+ ### 6.3 `repo` — non-secret repo coordinates (additive, optional)
295
+
296
+ The Workspace node seeds and Agent nodes read the target repo's non-secret coordinates
297
+ via the optional `repo` top-level field. It is **additive and optional**; an envelope
298
+ without it is valid exactly as today. It **never** carries a token or `worktreePath`
299
+ (R-02). Because it is a top-level sibling of `input`, `assemblePrompt` never
300
+ serialises it into the model prompt.
301
+
302
+ ```ts
303
+ /** Non-secret repo coordinates — seeded by the Workspace node, read by Agent nodes.
304
+ * Additive, optional. Never secret. */
305
+ export interface RepoDescriptor {
306
+ cloneUrl: string;
307
+ ownerRepo: string;
308
+ baseBranch: string;
309
+ }
310
+ ```
311
+
312
+ | Field | Type | Required | Notes |
313
+ |-------|------|----------|-------|
314
+ | `repo` | `RepoDescriptor` | **no** (optional) | Non-secret coordinates for the target repo. Absent on runs that don't involve repo checkout. Never contains a token or `worktreePath` (R-02). |
315
+
316
+ ### 6.4 `changeRef` — the SINGLE portable-change artifact (additive, optional)
317
+
318
+ The one canonical portable-change representation (ADR-002 Decision E) is carried as an
319
+ optional `changeRef` field. It is **additive and optional**; an envelope without it is
320
+ valid exactly as today. The `ChangeRef` interface is now defined in the **shared
321
+ contract** (`packages/cli/src/contract/envelope.ts`) — the n8n package imports it from
322
+ `@websitelabs/software-teams` rather than defining it locally, so there is exactly one
323
+ source of truth.
324
+
325
+ ```ts
326
+ /** ADR-002 Decision E — the ONE canonical portable change. Additive, optional. */
327
+ export interface ChangeRef {
328
+ kind: 'format-patch';
329
+ /** base64 of `git format-patch` bytes; re-applied on any worker (queue-safe). */
330
+ patchBase64: string;
331
+ }
332
+ ```
333
+
334
+ | Field | Type | Required | Notes |
335
+ |-------|------|----------|-------|
336
+ | `changeRef` | `ChangeRef` | **no** (optional) | The agent turn's captured portable change. Absent ⇒ no change produced. Self-contained base64 `format-patch`; needs no shared storage (AC9, R-15). |
337
+
338
+ `changeRef` also rides the Orchestrator's run-state as an additive optional member of
339
+ `RunTaskState` (`src/orchestration/run-state/shapes.ts`): `changeRef?: ChangeRef`. T8
340
+ writes it there per `correlationId` + `taskId`; T9 reads it (ADR-002 Decision F). This
341
+ is a run-state field, not a top-level envelope field — the §1 invariants are untouched.
342
+
343
+ ### 6.5 `branch` artifact type (already open-vocabulary)
344
+
345
+ The Finaliser (T9) appends `{ type: 'branch', url: '…/tree/<branch>' }` to `artifacts`.
346
+ `artifacts[].type` is already an **open vocabulary** per §1 (`type: string`), so this is
347
+ **not** a contract change — `branch` is the value `extractBranchName`/`resolveOutputRef`
348
+ consume to take the PR path (AC7, R-17). The CONTRACT.md §3 example already shows a
349
+ `branch` artifact.
350
+
351
+ ### 6.6 Additive-only rule (binding on all `1-04` slices)
352
+
353
+ Any new field introduced by `1-04` is **additive and optional**. No slice may make an
354
+ existing field optional, change a type, add a required field, or alter the §4 merge.
355
+ The GitHub token is **never** a field on the envelope or in `RepoContext` (R-02); it
356
+ travels only via the `SoftwareTeamsApi` credential into the child-process env (T3). The
357
+ `contract-check` gate and existing tests MUST stay green; a changed existing test
358
+ signals a regression, not a test to edit.
359
+
360
+ ---
361
+
362
+ ## 7. Addendum — PR-feedback and HITL additive fields (plan `1-01`, AC2/AC7)
363
+
364
+ > **Status:** Accepted. Pairs with plan `1-01-n8n-e2e-gaps`.
365
+ > **Implemented by** T3 (contract surface), T7 (PR-Feedback node), T11 (Output node tag writer).
366
+ > **Registration and node-load gate verified by** T12 (`software-teams-devops`).
367
+ > **Architecture decision:** see [`ARCHITECTURE.md`](./ARCHITECTURE.md) ADR-005 (PR-tag
368
+ > correlationId re-entry + merge-triggered cleanup) for the full decision record covering
369
+ > the PR-tag mechanism (Decision P), PR-feedback re-entry (Decision Q), multi-round
370
+ > multi-channel HITL (Decision R), and cleanup (Decision S).
371
+ > **Additive and optional ONLY.**
372
+
373
+ ### 7.1 The six top-level invariants are UNCHANGED
374
+
375
+ The six fields of §1 (`correlationId`, `agentId`, `status`, `input`, `result`,
376
+ `artifacts`), their types, the invariants the `contract-check` gate asserts (§1), and
377
+ the **§4 upstream-context merge are all unchanged**. Existing envelope/contract tests
378
+ stay green.
379
+
380
+ Two additive optional top-level fields are introduced below (`feedback`, `hitlChannel`).
381
+ Because they are top-level siblings of `input` — and `assemblePrompt` reads only
382
+ `input` — neither field is ever serialised into the model prompt.
383
+
384
+ ### 7.2 `feedback` — categorised PR-review comments (additive, optional)
385
+
386
+ The PR-Feedback node (T7) ingests GitHub PR review comments, categorises them (reusing
387
+ `feedback --json` from T4), and emits a continue-run `NodeEnvelope` carrying the
388
+ categorised comments in the optional `feedback` field. This is how PR review feedback
389
+ re-enters the Orchestrator's continue path (AC2).
390
+
391
+ ```ts
392
+ /** A single categorised PR-review comment. */
393
+ export interface FeedbackComment {
394
+ path: string;
395
+ line: number | null;
396
+ body: string;
397
+ author: string;
398
+ category: string;
399
+ action: string;
400
+ }
401
+ ```
402
+
403
+ | Field | Type | Required | Notes |
404
+ |-------|------|----------|-------|
405
+ | `feedback` | `{ comments: FeedbackComment[] }` | **no** (optional) | Categorised PR-review comments from the PR-Feedback node. Absent on envelopes that are not PR-feedback re-entries. |
406
+
407
+ ### 7.3 `hitlChannel` — upstream HITL channel hint (additive, optional)
408
+
409
+ An upstream node may hint which HITL channel should be used for human interaction. The
410
+ HITL node's own parameter always wins; this field is a default/hint only.
411
+
412
+ ```ts
413
+ hitlChannel?: 'slack' | 'email' | 'notify' | 'discord';
414
+ ```
415
+
416
+ | Field | Type | Required | Notes |
417
+ |-------|------|----------|-------|
418
+ | `hitlChannel` | `'slack' \| 'email' \| 'notify' \| 'discord'` | **no** (optional) | Upstream hint for HITL channel selection. The HITL node param overrides. |
419
+
420
+ ### 7.4 PR correlation tag
421
+
422
+ The Output node stamps a machine-parseable `correlationId` tag into the PR body when
423
+ it opens a PR. The PR-Feedback node parses that tag to recover the originating run's
424
+ `correlationId`, enabling the feedback re-entry loop (AC2).
425
+
426
+ The tag is an invisible HTML comment:
427
+
428
+ ```
429
+ <!-- software-teams:correlationId=run-2026-06-03-CU-4821 -->
430
+ ```
431
+
432
+ Two canonical helpers are exported from the shared contract
433
+ (`packages/cli/src/contract/envelope.ts`) so that writer (Output node, T11) and
434
+ reader (PR-Feedback node, T7) import the same code (R-06):
435
+
436
+ | Helper | Signature | Purpose |
437
+ |--------|-----------|---------|
438
+ | `buildCorrelationTag` | `(correlationId: string) => string` | Wraps the id in an HTML comment. Used by the Output node when building the PR body. |
439
+ | `parseCorrelationTag` | `(body: string) => string \| null` | Extracts the correlationId from a PR body. Returns `null` if no tag is present. Used by the PR-Feedback node. |
440
+ | `CORRELATION_TAG_PREFIX` | `string` (constant) | The prefix string `"software-teams:correlationId="`. |
441
+
442
+ Round-trip invariant: `parseCorrelationTag(buildCorrelationTag(id)) === id` for any
443
+ non-empty `id` that contains no whitespace or `>`.
444
+
445
+ ### 7.5 Additive-only rule (binding on all `1-01` slices)
446
+
447
+ Any new field introduced by `1-01` is **additive and optional**. No slice may make an
448
+ existing field optional, change a type, add a required field, or alter the §4 merge.
449
+ The `contract-check` gate and existing tests MUST stay green; a changed existing test
450
+ signals a regression, not a test to edit.