@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.
- package/ARCHITECTURE.md +1232 -0
- package/CONTRACT.md +450 -0
- package/README.md +491 -0
- package/dist/agents/software-teams-architect.md +155 -0
- package/dist/agents/software-teams-backend.md +93 -0
- package/dist/agents/software-teams-codebase-mapper.md +67 -0
- package/dist/agents/software-teams-committer.md +90 -0
- package/dist/agents/software-teams-debugger.md +91 -0
- package/dist/agents/software-teams-dev-planner.md +175 -0
- package/dist/agents/software-teams-devops.md +92 -0
- package/dist/agents/software-teams-feedback-learner.md +118 -0
- package/dist/agents/software-teams-frontend.md +107 -0
- package/dist/agents/software-teams-game-ai-engineer.md +179 -0
- package/dist/agents/software-teams-game-art-pipeline.md +180 -0
- package/dist/agents/software-teams-game-designer.md +245 -0
- package/dist/agents/software-teams-game-devops.md +134 -0
- package/dist/agents/software-teams-game-engineer.md +146 -0
- package/dist/agents/software-teams-game-producer.md +288 -0
- package/dist/agents/software-teams-game-qa.md +297 -0
- package/dist/agents/software-teams-game-tech-artist.md +186 -0
- package/dist/agents/software-teams-head-engineering.md +37 -0
- package/dist/agents/software-teams-perf-analyst.md +124 -0
- package/dist/agents/software-teams-phase-researcher.md +75 -0
- package/dist/agents/software-teams-plan-checker.md +87 -0
- package/dist/agents/software-teams-planner.md +456 -0
- package/dist/agents/software-teams-pr-feedback.md +127 -0
- package/dist/agents/software-teams-pr-generator.md +107 -0
- package/dist/agents/software-teams-producer.md +203 -0
- package/dist/agents/software-teams-product-lead.md +51 -0
- package/dist/agents/software-teams-programmer.md +126 -0
- package/dist/agents/software-teams-qa-tester.md +165 -0
- package/dist/agents/software-teams-quality.md +153 -0
- package/dist/agents/software-teams-researcher.md +151 -0
- package/dist/agents/software-teams-security.md +126 -0
- package/dist/agents/software-teams-ux-designer.md +92 -0
- package/dist/agents/software-teams-verifier.md +87 -0
- package/dist/credentials/SoftwareTeamsApi.credentials.d.ts +18 -0
- package/dist/credentials/SoftwareTeamsApi.credentials.d.ts.map +1 -0
- package/dist/credentials/SoftwareTeamsApi.credentials.js +110 -0
- package/dist/credentials/SoftwareTeamsApi.credentials.js.map +1 -0
- package/dist/credentials/softwareTeamsApi.svg +14 -0
- package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.d.ts +23 -0
- package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.js +308 -0
- package/dist/nodes/SoftwareTeamsAgent/SoftwareTeamsAgent.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsAgent/softwareTeamsAgent.svg +18 -0
- package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.d.ts +24 -0
- package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.js +2635 -0
- package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsCleanup/SoftwareTeamsCleanup.svg +6 -0
- package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.d.ts +6 -0
- package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.js +231 -0
- package/dist/nodes/SoftwareTeamsFinaliser/SoftwareTeamsFinaliser.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsFinaliser/softwareTeamsFinaliser.svg +11 -0
- package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.d.ts +25 -0
- package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.js +366 -0
- package/dist/nodes/SoftwareTeamsHitl/SoftwareTeamsHitl.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsHitl/softwareTeamsHitl.svg +11 -0
- package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.d.ts +15 -0
- package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.js +373 -0
- package/dist/nodes/SoftwareTeamsOrchestrator/SoftwareTeamsOrchestrator.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsOrchestrator/softwareTeamsOrchestrator.svg +20 -0
- package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.d.ts +6 -0
- package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.js +2685 -0
- package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsOutput/SoftwareTeamsOutput.svg +6 -0
- package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.d.ts +22 -0
- package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.js +2655 -0
- package/dist/nodes/SoftwareTeamsPrFeedback/SoftwareTeamsPrFeedback.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsPrFeedback/softwareTeamsPrFeedback.svg +8 -0
- package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.d.ts +19 -0
- package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.js +198 -0
- package/dist/nodes/SoftwareTeamsSlackHitl/SoftwareTeamsSlackHitl.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsSlackHitl/softwareTeamsSlackHitl.svg +10 -0
- package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.d.ts +6 -0
- package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.js +2601 -0
- package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsTrigger/SoftwareTeamsTrigger.node.svg +6 -0
- package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.d.ts +20 -0
- package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.d.ts.map +1 -0
- package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.js +175 -0
- package/dist/nodes/SoftwareTeamsWorkspace/SoftwareTeamsWorkspace.node.js.map +1 -0
- package/dist/nodes/SoftwareTeamsWorkspace/softwareTeamsWorkspace.svg +13 -0
- package/dist/src/execution/single-turn.d.ts +6 -0
- package/dist/src/execution/single-turn.d.ts.map +1 -0
- package/dist/src/execution/single-turn.js +2662 -0
- package/dist/src/execution/single-turn.js.map +1 -0
- package/dist/src/hitl/channels.d.ts +48 -0
- package/dist/src/hitl/channels.d.ts.map +1 -0
- package/dist/src/hitl/channels.js +297 -0
- package/dist/src/hitl/channels.js.map +1 -0
- package/dist/src/hitl/conversation-state.d.ts +45 -0
- package/dist/src/hitl/conversation-state.d.ts.map +1 -0
- package/dist/src/hitl/conversation-state.js +69 -0
- package/dist/src/hitl/conversation-state.js.map +1 -0
- package/dist/src/hitl/slack.d.ts +32 -0
- package/dist/src/hitl/slack.d.ts.map +1 -0
- package/dist/src/hitl/slack.js +202 -0
- package/dist/src/hitl/slack.js.map +1 -0
- package/dist/src/ingestion/context.d.ts +38 -0
- package/dist/src/ingestion/context.d.ts.map +1 -0
- package/dist/src/ingestion/context.js +2501 -0
- package/dist/src/ingestion/context.js.map +1 -0
- package/dist/src/ingestion/pr-feedback.d.ts +48 -0
- package/dist/src/ingestion/pr-feedback.d.ts.map +1 -0
- package/dist/src/ingestion/pr-feedback.js +85 -0
- package/dist/src/ingestion/pr-feedback.js.map +1 -0
- package/dist/src/n8n-cast.d.ts +11 -0
- package/dist/src/n8n-cast.d.ts.map +1 -0
- package/dist/src/n8n-cast.js +17 -0
- package/dist/src/n8n-cast.js.map +1 -0
- package/dist/src/orchestration/run-state/global-store.d.ts +7 -0
- package/dist/src/orchestration/run-state/global-store.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/global-store.js +27 -0
- package/dist/src/orchestration/run-state/global-store.js.map +1 -0
- package/dist/src/orchestration/run-state/ordering.d.ts +14 -0
- package/dist/src/orchestration/run-state/ordering.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/ordering.js +59 -0
- package/dist/src/orchestration/run-state/ordering.js.map +1 -0
- package/dist/src/orchestration/run-state/persistence.d.ts +9 -0
- package/dist/src/orchestration/run-state/persistence.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/persistence.js +29 -0
- package/dist/src/orchestration/run-state/persistence.js.map +1 -0
- package/dist/src/orchestration/run-state/planning.d.ts +17 -0
- package/dist/src/orchestration/run-state/planning.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/planning.js +117 -0
- package/dist/src/orchestration/run-state/planning.js.map +1 -0
- package/dist/src/orchestration/run-state/readiness.d.ts +20 -0
- package/dist/src/orchestration/run-state/readiness.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/readiness.js +105 -0
- package/dist/src/orchestration/run-state/readiness.js.map +1 -0
- package/dist/src/orchestration/run-state/shapes.d.ts +53 -0
- package/dist/src/orchestration/run-state/shapes.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/shapes.js +3 -0
- package/dist/src/orchestration/run-state/shapes.js.map +1 -0
- package/dist/src/orchestration/run-state/transitions.d.ts +46 -0
- package/dist/src/orchestration/run-state/transitions.d.ts.map +1 -0
- package/dist/src/orchestration/run-state/transitions.js +133 -0
- package/dist/src/orchestration/run-state/transitions.js.map +1 -0
- package/dist/src/orchestration/run-state.d.ts +8 -0
- package/dist/src/orchestration/run-state.d.ts.map +1 -0
- package/dist/src/orchestration/run-state.js +35 -0
- package/dist/src/orchestration/run-state.js.map +1 -0
- package/dist/src/output/github.d.ts +39 -0
- package/dist/src/output/github.d.ts.map +1 -0
- package/dist/src/output/github.js +2492 -0
- package/dist/src/output/github.js.map +1 -0
- package/dist/src/repo/git.d.ts +71 -0
- package/dist/src/repo/git.d.ts.map +1 -0
- package/dist/src/repo/git.js +207 -0
- package/dist/src/repo/git.js.map +1 -0
- package/dist/src/repo/merge.d.ts +36 -0
- package/dist/src/repo/merge.d.ts.map +1 -0
- package/dist/src/repo/merge.js +133 -0
- package/dist/src/repo/merge.js.map +1 -0
- package/dist/src/repo/repo-context.d.ts +23 -0
- package/dist/src/repo/repo-context.d.ts.map +1 -0
- package/dist/src/repo/repo-context.js +10 -0
- package/dist/src/repo/repo-context.js.map +1 -0
- package/dist/src/repo/teardown.d.ts +38 -0
- package/dist/src/repo/teardown.d.ts.map +1 -0
- package/dist/src/repo/teardown.js +171 -0
- package/dist/src/repo/teardown.js.map +1 -0
- package/dist/src/repo/validate.d.ts +4 -0
- package/dist/src/repo/validate.d.ts.map +1 -0
- package/dist/src/repo/validate.js +42 -0
- package/dist/src/repo/validate.js.map +1 -0
- 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.
|