refactor(#23): extract Azure DevOps data-plane calls into azdevops_db.ps1#27
Conversation
🐚 Senior Bash EngineerSummaryNo bash files ( Changed Files Inspected (vs
|
| File | Type | Bash impact |
|---|---|---|
.claude/commands/azdevops-diagrams-check.md |
new — markdown | none (skill instructions) |
.claude/commands/pr-flow.md |
modified — markdown | none |
CLAUDE.md |
modified — markdown | none |
powcuts_by_cli/azdevops_db.ps1 |
new — PowerShell | none |
powcuts_by_cli/azdevops_workitems.ps1 |
modified — PowerShell | none |
powcuts_by_cli/pow_az_cli.ps1 |
modified — PowerShell | none |
powcuts_home.ps1 |
modified — PowerShell | none |
Markdown Bash-Snippet Spot-Check
The new skill .claude/commands/azdevops-diagrams-check.md contains several bash-fenced snippets used as Claude-executed pipeline steps (not user-facing shortcuts, not sourced into .bashrc). I scanned them for portability issues since they run during the skill:
- File-loop variables (
"$f") are correctly quoted inside thecase/grep/[ -f ]constructs. ✅ /tmp/azdo-*paths are bare-word, no spaces, safe across macOS and Linux. ✅grep -E,awk,comm,sed -E,sort -uare all POSIX-portable; no GNU-only flags spotted. ✅case "$f" in bashcuts_by_cli/*|powcuts_by_cli/*)— quoted scrutinee, glob patterns unquoted as required. ✅- No
mapfile/readarray/associative-array bash 4 features that would break on macOS stock/bin/bash3.2. ✅ - The
{ git diff ...; git diff ...; } | sort -u > /tmp/...group commands are well-formed. ✅
Nothing in pr-flow.md or CLAUDE.md introduces new shell snippets that warrant blocking concerns.
Sourcing Wire-Up
N/A — no new files under bashcuts_by_cli/, so .bcut_home does not need updating.
Naming Convention
N/A — no bash aliases or functions added.
Verdict
APPROVE — no bash files in this diff. The PR's scope is correctly PowerShell-only per issue #23's Shell Parity Note, and the embedded bash snippets in the new diagram-check skill are portable and properly quoted.
Generated by Claude Code
🛡️ Senior Security EngineerScope: PR #27 vs Verdict: ✅ Approve with one minor hardening recommendation. No blocking findings. 1. Shell-injection via
|
| Concern | Status |
|---|---|
& az @ArgList shell-injection |
✅ Not exploitable (no shell involved) |
$Fields argument-injection |
New-AzDevOpsWorkItem |
| Stderr tempfile race | ✅ Safe (atomic create, owner-only, cleaned in finally) |
| Secret leakage in stdout/stderr | ✅ Clean (note: warn against echoing Error if --debug ever enabled) |
| Wire-up ordering | ✅ Correct |
| Markdown payloads | ✅ Clean |
Recommendation: Merge after (optionally) adding the $Fields validator. None of the findings rise to a merge-blocker.
https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
Generated by Claude Code
💠 Senior PowerShell EngineerSummaryReviewed Findings
Dot-Sourcing Wire-UpWIRED — Approved VerbsPASS — all seven new functions use approved verbs:
Parse-CheckSKIPPED — VerdictAPPROVE — no critical or high issues. Four LOW findings (one load-order suggestion, one return-shape observation, one informational verb-naming note already covered by AC, one comment-clarity nit) and several INFO confirmations. The refactor cleanly achieves the stated goal of decoupling orchestrators from Generated by Claude Code |
🧼 Senior Clean-Code EngineerSummaryThe wrapper layer in Findings
Duplication Map
Function Sizes
VerdictREQUEST CHANGES — 1 HIGH (parallel-pair extraction explicitly named in CLAUDE.md), 3 MEDIUM (in-function repetition + magic-string drift across two callers + duplicated field-array build). The HIGH finding is mechanical to fix and shrinks the diff; the MEDIUM findings prevent the casing drift from compounding. Once Generated by Claude Code |
Clean-code HIGH (parallel-pair extraction):
- Collapse Get-AzDevOpsIterationList + Get-AzDevOpsAreaList into a
single Get-AzDevOpsClassificationList -Kind <Iteration|Area>
- Drop the if/else dispatch in Invoke-AzDevOpsClassificationLive
- Update both Sync-AzDevOpsCache dataset Fetches to pass -Kind
Clean-code MEDIUM (in-function repetition):
- Replace 5 back-to-back `if ($Param) { $argList += @('--flag', $Param) }`
blocks in New-AzDevOpsWorkItem with an ordered-hashtable loop. Function
body drops from ~56 to ~46 lines and adding a future optional flag is
a one-line hashtable entry instead of a new if-block.
Security minor (defense in depth):
- Validate every -Fields token against ^[A-Za-z][A-Za-z0-9_.]*= so a
stray `--`-prefixed value cannot escape the variadic --fields slot
and be reinterpreted by az's argparse as a new flag.
LOW findings (load-order hint, return-shape note on az-create-userstory,
verb-noun rename of az-create-userstory, comment-clarity nit, casing
mismatch on 'User Story') deferred to a follow-up issue per PR plan.
https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
✅ Criteria Check — Issue #23
Syntax Checks
Acceptance Criteria
Summary
Manual Verification Checklist (run in a fresh PowerShell terminal)
Verdict✅ PASS — every binding acceptance criterion is satisfied. 2 example-list wrappers ( https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo Generated by Claude Code |
🗺️ AzDO Diagrams Check — Issue #23Trigger✅ TRIGGERED — three Azure DevOps source files changed in this branch:
Diagram Doc State
Function Inventory
Applied EditsDiagram 4 (Sync) —
Diagram 7 (
Diagram 9 (dependency map):
Net diff: +20 / -12 lines.
|
New skill self-gates on Azure DevOps source changes (azdevops_*.ps1, pow_az_cli.ps1, .az_bashcuts, or any file invoking `az boards|devops| repos|pipelines`). When triggered, diffs the function inventory and `az` subcommand coverage between source and docs/azure-devops-diagrams.md, then proposes targeted mermaid edits per finding using a diagram-to- subsystem map (architecture / Connect / Auth / Sync / cache consumers / Tree / NewStory / Schedule / dependency map) and applies on confirmation. Wired as Phase 5b of /pr-flow between Criteria Check and PR Body, and added to the slash-commands table in CLAUDE.md. https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
….ps1
Every `az boards ...` invocation now routes through a thin wrapper in the
new powcuts_by_cli/azdevops_db.ps1, decoupling higher-level orchestrators
from `az` argument shapes, JSON deserialization, and stderr handling.
New wrappers:
- Invoke-AzDevOpsAzJson, Invoke-AzDevOpsBoardsQuery (relocated from
azdevops_workitems.ps1; canonical { Json, Error, ExitCode } envelope)
- Get-AzDevOpsIterationList, Get-AzDevOpsAreaList
- New-AzDevOpsWorkItem, Add-AzDevOpsWorkItemRelation
- Get-AzDevOpsWorkItemTypeDefinition
Call-site routing:
- Invoke-AzDevOpsSmokeQuery (line 78) -> Invoke-AzDevOpsBoardsQuery
- Sync-AzDevOpsCache iteration/area datasets -> Get-AzDevOps{Iteration,Area}List
- Invoke-AzDevOpsClassificationLive -> Get-AzDevOps{Iteration,Area}List
- Invoke-AzDevOpsWorkItemCreate -> New-AzDevOpsWorkItem
- Invoke-AzDevOpsParentLink -> Add-AzDevOpsWorkItemRelation
- Invoke-AzDevOpsWorkItemTypeShow -> Get-AzDevOpsWorkItemTypeDefinition
- pow_az_cli.ps1's az-create-userstory -> New-AzDevOpsWorkItem (with -Open)
Session/admin calls (`az login`, `az account show`, `az extension *`,
`az devops configure`, `az version`) remain in azdevops_workitems.ps1
per the issue's out-of-scope carve-out. AC grep gate passes:
`grep -nE '\baz boards' azdevops_workitems.ps1 pow_az_cli.ps1` returns
only comments and Write-Host hints, no active call sites.
No public-facing function signatures changed.
Closes #23
https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
Clean-code HIGH (parallel-pair extraction):
- Collapse Get-AzDevOpsIterationList + Get-AzDevOpsAreaList into a
single Get-AzDevOpsClassificationList -Kind <Iteration|Area>
- Drop the if/else dispatch in Invoke-AzDevOpsClassificationLive
- Update both Sync-AzDevOpsCache dataset Fetches to pass -Kind
Clean-code MEDIUM (in-function repetition):
- Replace 5 back-to-back `if ($Param) { $argList += @('--flag', $Param) }`
blocks in New-AzDevOpsWorkItem with an ordered-hashtable loop. Function
body drops from ~56 to ~46 lines and adding a future optional flag is
a one-line hashtable entry instead of a new if-block.
Security minor (defense in depth):
- Validate every -Fields token against ^[A-Za-z][A-Za-z0-9_.]*= so a
stray `--`-prefixed value cannot escape the variadic --fields slot
and be reinterpreted by az's argparse as a new flag.
LOW findings (load-order hint, return-shape note on az-create-userstory,
verb-noun rename of az-create-userstory, comment-clarity nit, casing
mismatch on 'User Story') deferred to a follow-up issue per PR plan.
https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
Issue #23 introduced a wrapper layer in azdevops_db.ps1 that the diagrams must mirror. Updates Diagrams 4, 7, and 9 to show the wrappers between callers and az. Diagram 4 (Sync): - Iteration / area dataset descriptors now go through Get-AzDevOpsClassificationList -Kind <Iteration|Area> - Per-descriptor Fetch summary updated accordingly Diagram 7 (New-AzDevOpsUserStory): - Invoke-AzDevOpsWorkItemCreate -> New-AzDevOpsWorkItem -> az boards work-item create - Invoke-AzDevOpsParentLink -> Add-AzDevOpsWorkItemRelation -> az boards work-item relation add Diagram 9 (dependency map): - Add a "Data-plane wrappers (azdevops_db.ps1)" cluster with ClassList, NewWI, AddRel - Re-route InvokeDS / InvCls / InvCreate / InvLink edges so the wrapper layer is visible (everything funnels through AzJson -> Az) Get-AzDevOpsWorkItemTypeDefinition (the 4th wrapper added in this PR) is intentionally deferred along with the broader schema-management subsystem backlog (19 functions from PR #22 that are not yet in Diagram 9). Filing a follow-up issue for the backlog is cleaner than landing a single orphan node here. https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
After rebasing onto post-#26 main, two doc-staleness gaps surfaced: azdevops_db.ps1:16 Header comment said "alongside Connect-AzDevOps". Updated to az-Connect-AzDevOps to match the renamed user-facing function. docs/azure-devops-diagrams.md This doc was added by PR #24 before PR #26 was authored, so it referenced every user-facing AzDO function by the unprefixed name (64 references across TOC, section headings, mermaid node labels, and sequence-diagram participants). PR #26 didn't update it (out of scope for a rename PR). Bulk-renamed every occurrence to its az- counterpart and updated the matching anchor links so the TOC jumps still work. Two shortcut-notation cases (Get-/Open-AzDevOps, Register-/Unregister-AzDevOpsSyncSchedule) needed manual touch-up since the regex couldn't see the bare half of the pair. https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
9a74757 to
1c60faf
Compare
🔁 Rebased onto post-#26
|
| File | Lines |
|---|---|
.claude/commands/azdevops-diagrams-check.md |
+246 (new skill) |
.claude/commands/pr-flow.md |
+14 -0 |
CLAUDE.md |
+3 -0 |
docs/azure-devops-diagrams.md |
+82 -82 (rename propagation + wrapper-layer additions) |
powcuts_by_cli/azdevops_db.ps1 |
+166 (new wrapper layer) |
powcuts_by_cli/azdevops_workitems.ps1 |
+24 -64 (call sites routed through wrappers) |
powcuts_by_cli/pow_az_cli.ps1 |
+35 -35 (az-create-userstory → New-AzDevOpsWorkItem -Open) |
powcuts_home.ps1 |
+7 (dot-source azdevops_db.ps1) |
Verification
- ✅ Zero unprefixed user-facing AzDO names remain across
*.ps1,README.md,docs/azure-devops-diagrams.md - ✅ Zero
az-az-double-prefix instances - ✅ Brace + paren balance on
azdevops_db.ps1(22/22 / 40/40)
Commits on this branch (vs main)
1c60faf docs: propagate az- prefix into wrapper-layer doc-strings + diagrams
3982339 docs(azdevops-diagrams): reflect new data-plane wrapper layer
9dcd82d refactor: address clean-code HIGH + security minor on PR #27
52c864e refactor(#23): extract Azure DevOps data-plane calls into azdevops_db.ps1
4ae0792 feat: add /azdevops-diagrams-check skill and wire into /pr-flow
The earlier APPROVE verdicts from Phase 3 (bash, PowerShell, security) and the criteria-check PASS still hold — the rebase didn't change any code logic, only propagated the rename. Ready to merge.
https://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
Generated by Claude Code
70966ce— ViewSummary
Pulls every
az boards ...invocation inazdevops_workitems.ps1andpow_az_cli.ps1behind a thin wrapper layer in the newpowcuts_by_cli/azdevops_db.ps1. Higher-level orchestrators (sync, new-story, classification fallback, type-show) stop knowing aboutazargument shapes, JSON deserialization, and stderr handling — future caching, retry, or alt-transport changes are now a one-file edit.Also lands a supporting
/azdevops-diagrams-checkskill that keepsdocs/azure-devops-diagrams.mdin lockstep with the AzDO source going forward.Issue
Closes #23
Changes
Refactor (issue #23)
powcuts_by_cli/azdevops_db.ps1with 6 wrappers:Invoke-AzDevOpsAzJson,Invoke-AzDevOpsBoardsQuery(relocated; canonical{ Json, Error, ExitCode }envelope)Get-AzDevOpsClassificationList -Kind <Iteration|Area>(merged Iteration/Area twin pair per clean-code review)New-AzDevOpsWorkItem(with-Fieldsregex validation per security review; ordered-hashtable optional-flag loop)Add-AzDevOpsWorkItemRelationGet-AzDevOpsWorkItemTypeDefinitionazdevops_db.ps1intopowcuts_home.ps1(dot-sourced beforeazdevops_workitems.ps1)Invoke-AzDevOpsSmokeQuery→Invoke-AzDevOpsBoardsQueryGet-AzDevOpsClassificationList -KindInvoke-AzDevOpsClassificationLive→Get-AzDevOpsClassificationList -KindInvoke-AzDevOpsWorkItemCreate→New-AzDevOpsWorkItemInvoke-AzDevOpsParentLink→Add-AzDevOpsWorkItemRelationInvoke-AzDevOpsWorkItemTypeShow→Get-AzDevOpsWorkItemTypeDefinitionpow_az_cli.ps1'saz-create-userstory→New-AzDevOpsWorkItem -Openaz login,az account show,az extension *,az devops configure,az version) remain inazdevops_workitems.ps1per the issue's out-of-scope carve-out.Tooling (separate commit)
.claude/commands/azdevops-diagrams-check.mdskill — auto-detects AzDO source changes, diffs function inventory +azsubcommand coverage againstdocs/azure-devops-diagrams.md, proposes targeted mermaid edits per gap./pr-flowbetween Criteria Check and PR Body.CLAUDE.mdslash-commands table.Diagrams (commit
9a74757)docs/azure-devops-diagrams.mdDiagrams 4 / 7 / 9 to reflect the new wrapper layer.AC Gate (issue #23)
grep -nE '\baz boards' powcuts_by_cli/azdevops_workitems.ps1 powcuts_by_cli/pow_az_cli.ps1returns no active call sites — only header comments, doc-strings, andWrite-Hostuser hints.Test Plan
powcuts_home.ps1Get-Command Invoke-AzDevOpsAzJson, Invoke-AzDevOpsBoardsQuery, Get-AzDevOpsClassificationList, New-AzDevOpsWorkItem, Add-AzDevOpsWorkItemRelation, Get-AzDevOpsWorkItemTypeDefinition— all 6 resolveConnect-AzDevOps— 6-step orchestrator passes (smoke query now via wrapper)Sync-AzDevOpsCache— assigned/mentions/hierarchy viaInvoke-AzDevOpsBoardsQuery, iterations/areas viaGet-AzDevOpsClassificationList -KindGet-AzDevOpsAssignedandOpen-AzDevOpsAssigned— cache consumers unaffectedNew-AzDevOpsUserStory— interactive create viaNew-AzDevOpsWorkItem, parent-link viaAdd-AzDevOpsWorkItemRelationhttps://claude.ai/code/session_01P7znUKccAohZYEwYRBNVfo
Generated by Claude Code