Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1548-workflow-tools-wording.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Workflow Settings says the run's full access is a normal Bot's instead of a per-Bot permission choice, and the delivery template lists only connectors a node really calls (`github`). The README says plainly that naming `bash` grants nothing.
4 changes: 2 additions & 2 deletions core/test/workflow-node-connectors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ test('the built-in delivery nodes make github available exactly where the node n
assert.match(guardFor(bot.id, COMPOSIO[0]), /not granted to this run/, `${nodeId} may not call composio`)
}

// The tester names bash only, and planner/writer name nothing: no connector for either.
// The tester, planner and writer name nothing, so none of them reaches a connector.
const tester = byNode.get('tester')
assert.deepEqual(tester.stepTools, ['bash'])
assert.equal(tester.stepTools, undefined, 'the tester names no connector')
assert.match(guardFor(tester.id, GITHUB[0]), /not granted to this run/)
for (const nodeId of ['planner', 'writer']) {
const bot = byNode.get(nodeId)
Expand Down
4 changes: 2 additions & 2 deletions patches/ds-bot/files/test/workflow-node-connectors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ test('the built-in delivery nodes make github available exactly where the node n
assert.match(guardFor(bot.id, COMPOSIO[0]), /not granted to this run/, `${nodeId} may not call composio`)
}

// The tester names bash only, and planner/writer name nothing: no connector for either.
// The tester, planner and writer name nothing, so none of them reaches a connector.
const tester = byNode.get('tester')
assert.deepEqual(tester.stepTools, ['bash'])
assert.equal(tester.stepTools, undefined, 'the tester names no connector')
assert.match(guardFor(tester.id, GITHUB[0]), /not granted to this run/)
for (const nodeId of ['planner', 'writer']) {
const bot = byNode.get(nodeId)
Expand Down
2 changes: 1 addition & 1 deletion plugins/workflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A required input must be an output of the same name on an ancestor. A root node

On a live run, a successor stays queued until its required inputs are present. If every predecessor is already done or failed and an input is still missing, that node fails and the run stops. The dispatch text includes `Input <name>: <value>`. The Bot reports outputs as `OUTPUT <name>: <value>`. A node with one declared output may put that value in the reply body.

The delivery template passes `task` → `plan` → `copy` → `patch`, then `review` and `report` into `release`. Coder and Tester name `bash`; Coder, Reviewer and Deployer name `github`. The weekly template passes `task` → `notes` → `metrics` → `report`.
The delivery template passes `task` → `plan` → `copy` → `patch`, then `review` and `report` into `release`. Coder, Reviewer and Deployer name `github`; the Tester names none, because `bash` is not a connector and grants nothing here (#1535). The weekly template passes `task` → `notes` → `metrics` → `report`.

## Branches and loops

Expand Down
4 changes: 2 additions & 2 deletions plugins/workflow/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ window.__ModuleLoader__.load({
'Each Bot\'s own model': '跟随各个 Bot 的模型',
'{count} models': '{count} 个模型',
Permissions: '权限',
'Each Bot\'s own permission': '跟随各个 Bot 的权限',
'Full access, same as a normal Bot': '完全访问,与普通 Bot 相同',
'On failure': '失败策略',
'Tester retries once. Any other failed step stops the run.': 'Tester 失败后自动重试 1 次。其他节点失败则停住。',
'A failed step stops the run.': '失败的节点停住,等你处理。',
Expand Down Expand Up @@ -879,7 +879,7 @@ window.__ModuleLoader__.load({
return h('div', { className: 'wf-box' },
h('div', { className: 'wf-kv' }, h('span', null, tr('Trigger')), h('span', null, trigger)),
h('div', { className: 'wf-kv' }, h('span', null, tr('Model')), h('span', null, modelLabel)),
h('div', { className: 'wf-kv' }, h('span', null, tr('Permissions')), h('span', null, tr('Each Bot\'s own permission'))),
h('div', { className: 'wf-kv' }, h('span', null, tr('Permissions')), h('span', null, tr('Full access, same as a normal Bot'))),
h('div', { className: 'wf-kv' }, h('span', null, tr('On failure')), h('span', null, tr(retries ? 'Tester retries once. Any other failed step stops the run.' : 'A failed step stops the run.'))),
h('div', { className: 'wf-kv' }, h('span', null, tr('Concurrent runs')), h('span', null, String(workflow.maxConcurrent ?? 2))),
h('div', { className: 'wf-kv' }, h('span', null, tr('Members')), h('span', null, workflow.chain)))
Expand Down
3 changes: 1 addition & 2 deletions plugins/workflow/src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const writer = {
const coder = {
id: 'coder', name: 'Coder', role: '代码实现',
instructions: '根据 Planner 的子任务和 Writer 的文案修改代码,提交 PR,并在 PR 描述中列出变更。',
tools: ['github', 'bash', 'read', 'edit'],
tools: ['github'],
inputs: [{ name: 'copy', type: 'text', required: true }],
outputs: [{ name: 'patch', type: 'text' }],
}
Expand All @@ -44,7 +44,6 @@ const reviewer = {
const tester = {
id: 'tester', name: 'Tester', role: '自动化测试', failOnce: true,
instructions: '运行单元、快照和 E2E。失败时自动重试 1 次,仍失败则停在本节点。',
tools: ['bash'],
inputs: [{ name: 'patch', type: 'text', required: true }],
outputs: [{ name: 'report', type: 'text' }],
}
Expand Down
36 changes: 35 additions & 1 deletion plugins/workflow/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1721,4 +1721,38 @@ test('#1493: a failed sibling run stays on the stage, and its row carries time a
assert.ok(older, 'a history row was dropped')
assert.equal(textOf(older).includes('Done'), true)
view.unmount()
})
})

test('the drawer details name the permission every node Bot actually runs with', async () => {
// #1535 made every node Bot danger-full-access, so a row claiming each Bot follows
// its own permission was stale (#1548): the details must say the run's full access
// is the same as a normal Bot's, and must not claim a per-Bot choice.
const graph = delivery()
const call = async (name) => {
if (name === 'workflow/state') return { workflows: [graph], templates: [{ id: 'delivery', name: '发布交付流水线' }] }
if (name === 'workflow/messages') return { messages: graph.messages }
return { ok: true, workflows: [graph] }
}
const view = mount(client.Section, { roster: { bots: [] }, call, actions: {} })
await settle()
findAll(view.tree, node => node.type === 'button' && textOf(node).includes('发布交付流水线'))[0].props.onClick()
findAll(view.tree, node => node.type === 'button' && textOf(node) === 'Flow')[0].props.onClick()
await settle()
findAll(view.tree, node => node.type === 'button' && textOf(node) === 'Details')[0].props.onClick()
await settle()
const rows = findAll(view.tree, node => node.props?.className === 'wf-kv')
const permission = rows.find(row => textOf(row.children?.[0] ?? '') === client.tr('Permissions'))
assert.ok(permission, 'the details list a Permissions row')
const value = textOf(permission.children[1])
assert.doesNotMatch(value, /own permission/i, 'a node Bot does not pick its own permission')
assert.match(value, /full access/i, 'the row names the full access a node Bot runs with')
assert.match(value, /same as a normal Bot/i, 'and says it is a normal Bot\'s full access')
const prev = globalThis.__flybot__
globalThis.__flybot__ = { t: key => (key === 'Tasks' ? '任务' : key) }
try {
assert.equal(client.tr('Full access, same as a normal Bot'), '完全访问,与普通 Bot 相同', 'the row is translated')
} finally {
globalThis.__flybot__ = prev
}
view.unmount()
})
4 changes: 2 additions & 2 deletions plugins/workflow/test/ephemeral.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,10 @@ test('the built-in delivery nodes hand their connector list to the host', async
assert.notEqual(state?.ok, false, `${nodeId} advanced: ${state?.error ?? ''}`)
}
const byNode = new Map(created.map(spec => [spec.fields.nodeId, spec]))
assert.deepEqual(byNode.get('coder').tools, ['github', 'bash', 'read', 'edit'], 'Coder names github')
assert.deepEqual(byNode.get('coder').tools, ['github'], 'Coder names github')
assert.deepEqual(byNode.get('reviewer').tools, ['github'], 'Reviewer names github')
assert.deepEqual(byNode.get('deployer').tools, ['github'], 'Deployer names github')
assert.deepEqual(byNode.get('tester').tools, ['bash'], 'Tester names no connector')
assert.deepEqual(byNode.get('tester').tools, [], 'Tester names no connector')
assert.deepEqual(byNode.get('planner').tools, [], 'Planner names no connector')
for (const spec of created) assert.equal(spec.permission, 'danger-full-access', 'file and shell rights are a normal Bot\'s')
})
Expand Down
Loading