Skip to content

feat: add update-work-item safe output#65

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/update-work-item-safeoutput
Draft

feat: add update-work-item safe output#65
Copilot wants to merge 2 commits intomainfrom
copilot/update-work-item-safeoutput

Conversation

Copy link
Contributor

Copilot AI commented Mar 20, 2026

Adds an update-work-item safe output tool for modifying existing ADO work items, with fine-grained front matter controls mirroring the update-issue pattern from GitHub Agentic Workflows.

Configuration

Each updatable field must be explicitly opted-in. All default to false/disabled:

safe-outputs:
  update-work-item:
    status: true              # System.State
    title: true               # System.Title
    body: true                # System.Description (markdown)
    title-prefix: "[bot] "    # guard: only update items whose current title starts with this
    tag-prefix: "agent-"      # guard: only update items that have a tag starting with this prefix
    max: 3                    # max outputs per run (default: 1)
    target: "*"               # "*" (default) or a specific work item ID
    work-item-type: true      # System.WorkItemType (ADO-specific)
    area-path: true           # System.AreaPath
    iteration-path: true      # System.IterationPath
    assignee: true            # System.AssignedTo
    tags: true                # System.Tags (full replacement)

Key design decisions

  • target accepts "*" (any ID) or an integer (lock to one item) — rejects at execution time if the agent-supplied ID doesn't match
  • title-prefix issues a GET before the PATCH to verify the current title; rejected if it doesn't match
  • tag-prefix issues a GET before the PATCH to verify the current tags; rejected if no tag starts with the prefix. ADO tags are semicolon-separated and each is trimmed before comparison
  • When both title-prefix and tag-prefix are configured, a single GET is issued and both checks are applied before the PATCH
  • max is enforced as a pre-pass in execute_safe_outputs before any entries execute, so a violation aborts the whole batch rather than partially executing
  • Disallowed fields return a failure result (not an error) with a clear message pointing to the missing config key
  • Added to WRITE_REQUIRING_SAFE_OUTPUTS — compilation fails if no permissions.write service connection is configured

Agent parameters

Field Type Notes
id u64 Required; must be > 0
title Option<String> Max 255 chars
body Option<String> Markdown; sets multilineFieldsFormat
state Option<String> e.g. "Active", "Resolved"
work_item_type Option<String> e.g. "Bug", "Task"
area_path Option<String>
iteration_path Option<String>
assignee Option<String> Email or display name
tags Option<Vec<String>> Replaces all existing tags

At least one field must be non-null; validation enforced at Stage 1 (MCP) and sanitization runs at both Stage 1 and Stage 2.


📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
@jamesadevine
Copy link
Collaborator

@copilot We should come up with a way e.g. tag-prefix which allows editing of work items with only tags in the list.

Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot We should come up with a way e.g. tag-prefix which allows editing of work items with only tags in the list.

Done in 8045891. Added tag-prefix to UpdateWorkItemConfig — before patching, the executor fetches the current work item and checks that at least one of its tags starts with the configured prefix (ADO tags are semicolon-separated, each trimmed before comparison). If no matching tag is found the update is rejected with a descriptive message.

safe-outputs:
  update-work-item:
    title: true
    tag-prefix: "agent-"   # only update work items that have a tag starting with "agent-"

When both title-prefix and tag-prefix are set, a single GET fetches the work item and both checks are applied before the PATCH is issued.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.github.com (HTTP Only)
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com /lib64/ld-linux-x86-64.so.2 -pie -z now -fuse-ld=lld -z relro -o b lib/rustlib/x86_/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/thiserror_impl-3723d82190d9318/usr/libexec/gcc/x86_64-linux-gnu/13/collect2 .rlib mbols.o res-29c722fcdd0c/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper res-29c722fcdd0c-plugin res-29c722fcdd0c/usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so res-29c722fcdd0c-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper (packet block)
  • dev.azure.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 --emit=dep-info,link -C prefer-dynamic -C embed-bitcode=no 0gm9bu44kjg0guvo0ok554wy.09b0mqx.rcgu.o exlzyjgsm82gg8lg8nf8td72.09b0mqx.rcgu.o mvo1is9m63x9f7dt29rlwwrk.09b0mqx.rcgu.o 2odb6ajuo4zrdnaasec910m1.09b0mqx.rcgu.o 3jyavcrshjb4p19y4uxetwvs.09b0mqx.rcgu.o 4ky8dyp6axhjtuv3guzwcot1.09b0mqx.rcgu.o 86ts56zqtyuqf4n0r91kt7t6.09b0mqx.rcgu.o kxwezc19jyhh7hzb5br2qd9g.09b0mqx.rcgu.o nkjjsz3wv2bnmq64y633lluc.09b0mqx.rcgu.o u920k80mfg2s198c7bj9lm5h.09b0mqx.rcgu.o uet2cs1tlmkolj5l6x6imla2.09b0mqx.rcgu.o 9dy4e5p3dy37hlgsg6p1ockr.09b0mqx.rcgu.o 9zb5tvlqb5tt29okd9ty3n8v.09b0mqx.rcgu.o gbp02zi6r6wah1ddwpeqlnpu.09b0mqx.rcgu.o (dns block)
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com --allow dev.azure.com --allow *.visualstudio.com -z relro -o /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/mcp_firewall_tests-551a308b9470e408 b lib/rustlib/x86_/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/thiserror_impl-3723d82190d9318/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_64-REDACTED-linux-gnu/bin/gcc-ld/ld.lld .rlib mbols.o res-29c722fcdd0c/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper res-29c722fcdd0c-flavor res-29c722fcdd0cgnu res-29c722fcdd0c-plugin (dns block)
  • msazuresphere.visualstudio.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com --allow dev.azure.com --allow *.visualstudio.com -z relro -o /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/mcp_firewall_tests-551a308b9470e408 b lib/rustlib/x86_/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/thiserror_impl-3723d82190d9318/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_64-REDACTED-linux-gnu/bin/gcc-ld/ld.lld .rlib mbols.o res-29c722fcdd0c/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper res-29c722fcdd0c-flavor res-29c722fcdd0cgnu res-29c722fcdd0c-plugin (dns block)
  • raw.github.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow *.github.com /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/mcp_firewall_tests-551a308b947/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2.0kap0pflzgx6ry7ccfo5lsi4s.0i2s0sp.rcgu.o /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/mcp_firewall_tests-551a308b947/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2.0l4tkat54h55bdhrke0cy4zx1.0i2s0sp.rcgu.o b.05y3skou2n8gywnq1xg30s6nw.1u5xb3x.rcgu.o b.0a9mzns1w8h8hjt5t2ctpscqh.1u5xb3x.rcgu.o b.0dvg5p338ze4yo8sclenf881a.1u5xb3x.rcgu.o b.0dx997ws7aolj1y340bx0d6e5.1u5xb3x.rcgu.o b.0q4dyj2s5hiuc6j4v5i9q6jg7.1u5xb3x.rcgu.o b.0r3pe41pju7n15rf01z828jy9.1u5xb3x.rcgu.o b.0st8ftpil8vvr8aptfgk5l9d4.1u5xb3x.rcgu.o b.115rxut0y51w7elubrycm7rr6.1u5xb3x.rcgu.o b.18jyy94t4oev2upsdapziepnb.1u5xb3x.rcgu.o b.1a3v69ywrr870cv3s0my1xrqv.1u5xb3x.rcgu.o b.1n70h8mnc4lv6myvt7xv1774j.1u5xb3x.rcgu.o b.1rzfl8n5jj6pb28b4t6wqgpti.1u5xb3x.rcgu.o b.220a3tz1x7lsbsl14itqakolp.1u5xb3x.rcgu.o b.2b3v53f7x475pleph3cuavv68.1u5xb3x.rcgu.o (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants