Repository navigation
feat(core): require an explicit user opt-in before the model launches a workflow #9806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
38faa5d
27f0c0e
e235766
d7b6912
18b3390
a59b6cd
c25fca4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,57 @@ describe('WorkflowTool', () => { | |
| expect(description).toContain('.qwen/workflows'); | ||
| }); | ||
|
|
||
| // The policy prose above tells the model how to orchestrate *well*; on its | ||
| // own it reads as encouragement, and the model fans out on tasks nobody | ||
| // asked to spend a fleet on. This gate is the half that says when not to. | ||
| it('description gates the tool on an explicit user request', () => { | ||
| const { description } = new WorkflowTool(fakeConfig()); | ||
|
|
||
| // Ordering is the point, not just presence: a gate placed after the | ||
| // "what a workflow is for" pitch reads as a footnote to it. It has to | ||
| // come first, so it frames everything below rather than qualifying it. | ||
| const gate = description.indexOf('**Only on an explicit request**'); | ||
| const pitch = description.indexOf('**What a workflow is for**'); | ||
| expect(gate).toBeGreaterThanOrEqual(0); | ||
| expect(gate).toBeLessThan(pitch); | ||
|
|
||
| // Each enumerated form is a real qwen trigger. Without the list the gate | ||
| // is unfalsifiable from the model's side — it cannot tell whether the | ||
| // request in front of it qualifies. | ||
| expect(description).toContain( | ||
| 'It counts as requested when any of these holds:', | ||
| ); | ||
| expect(description).toMatch(/contains the word `workflow`/); | ||
| expect(description).toMatch(/in their own words/); | ||
| expect(description).toMatch(/skill or slash command/); | ||
| expect(description).toMatch(/named a saved workflow/); | ||
| expect(description).toMatch(/resume or continue an earlier run/); | ||
|
|
||
| // Upstream's marker for this is `ultracode`, which does not exist here: | ||
| // naming it would enumerate a trigger no qwen user can pull, and the | ||
| // gate would refuse work that a real trigger should have allowed. | ||
| expect(description).not.toMatch(/ultracode/i); | ||
|
|
||
| // The load-bearing half of the gate. Without an offer-and-ask path the | ||
| // model reads "do not call it" as "refuse", and a user who would have | ||
| // said yes never gets asked. Over-blocking is this change's one real | ||
| // failure mode, so the escape hatch is anchored. | ||
| expect(description).toContain( | ||
| 'Do not call this tool unless the user has asked for multi-agent orchestration.', | ||
| ); | ||
| expect(description).toContain( | ||
| 'Otherwise do not call it, however well the task would parallelize.', | ||
| ); | ||
| expect(description).toMatch(/let the user decide/); | ||
| expect(description).toMatch(/skips the ask/); | ||
|
Comment on lines
+176
to
+177
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in c25fca4. The existing gate test now pins both prohibition sentences exactly. Independent mutation reproduction confirmed that deleting them previously left the focused suite green and now fails. Verification: workflow.test.ts 53/53; npm run build; npm run typecheck; targeted ESLint and Prettier; pre-commit hook. |
||
|
|
||
| // Interpolated, not pasted: the gate justifies itself with the fleet | ||
| // size, so a raised cap has to move this sentence too. | ||
| expect(description).toContain( | ||
| `dispatch up to ${DEFAULT_MAX_AGENTS_PER_RUN} subagents`, | ||
| ); | ||
| }); | ||
|
|
||
| // ── Approval dialog ──────────────────────────────────────────────────── | ||
| // | ||
| // What the user is asked to approve is arbitrary model-authored JavaScript | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c25fca4. The gate test now pins the any-of connective exactly. Independent mutation reproduction confirmed that changing any to all previously left the focused suite green and now fails. Verification: workflow.test.ts 53/53; npm run build; npm run typecheck; targeted ESLint and Prettier; pre-commit hook.