Commit 87bc553
refactor: split dv-python-sdk into dv-data/dv-query, harden all skills through end-to-end validation (#32)
* Add safety and guardrails documentation
Public-facing doc covering the plugin's safety model: supported operations,
authentication, least-privilege enforcement, confirmation flows, data residency,
logging, telemetry policy, irreversible operations, and planned improvements.
Clearly distinguishes platform-enforced controls (Dataverse security roles,
MCP authorization layers) from agent-level guardrails (skill instructions).
Adds Safety & Security section to README with key trust signals and link
to the full doc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix telemetry claim in README to acknowledge AI host data flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address PR review feedback on safety doc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review feedback on skill instruction language
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Split dv-python-sdk into dv-data and dv-query
dv-python-sdk was a 545-line skill covering five distinct concerns.
As S05-S09 are built out it would grow to 900+ lines, loading unnecessary
context in every session.
dv-data (new):
- Record CRUD, CreateMultiple, UpdateMultiple, UpsertMultiple
- CSV import with type inference and lookup resolution
- Alternate key upserts, continue-on-error batch
- File column uploads (chunked)
- Table/column/relationship schema creation
- @odata.bind rules and error handling
Triggers: "create", "insert", "import", "bulk", "upsert", "write", "upload"
dv-query (new):
- OData filter/select/expand/orderby/top/paging
- GUID-free display (formatted values)
- Fuzzy record lookup and "my" scoping (WhoAmI)
- $apply aggregation (Web API path documented)
- N:N $expand (Web API path documented)
- Change tracking / delta queries ($deltatoken)
- Data quality patterns (null rate, duplicates, orphan detection)
- Jupyter/pandas notebook handoff
Triggers: "query", "filter", "find", "show me", "aggregate", "analyze",
"profiling", "notebook", "pandas", "GUID-free"
dv-overview updated:
- Tool capability table: Python SDK row split into dv-data and dv-query rows
- Available Skills index: dv-python-sdk replaced with dv-data and dv-query
- All dv-python-sdk references updated to dv-data or dv-query as appropriate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update skill cross-references from dv-python-sdk to dv-data and dv-query
* Bump plugin version to 1.1.0 and update descriptions
* Clean up dv-data and dv-query to validated content only, add skill boundary cross-references
* Update dv-data, dv-query, dv-connect for SDK b6-b8 changes
- Add context manager pattern to dv-data and dv-query setup
- Add client.dataframe.get/create/update() to dv-query pandas section
- Add QueryBuilder fluent API section to dv-query
- Update Jupyter notebook setup to use client.dataframe.get()
- Add pandas to pip install in dv-connect (required dep since b7)
- Soften @odata.bind casing warning (SDK preserves casing since b6)
* Fix conflicts and factual errors in dv-data and dv-query skills
- Fix order_by() syntax in QueryBuilder section (was string syntax, should be column+descending=True)
- Remove unnecessary QueryBuilder import (client.query.builder() needs no import)
- Fix composable filter method: .filter() -> .where() to match SDK API
- Simplify setup blocks: remove confusing dual-pattern with pass placeholder
- Remove @odata.bind from dv-query field casing table (read-only skill)
- Add guidance on QueryBuilder vs client.records.get() — QB preferred for multi-record, records.get() needed for single-by-GUID
* Align MCP vs SDK guidance across dv-overview and dv-query
- Add read-volume threshold to MCP/SDK rule: MCP for single-page reads, SDK for bulk/multi-page
- Split volume guidance into separate write and read paragraphs
- Update SDK checklist to reference QueryBuilder as preferred query API
- Remove stale dv-query capabilities from tool table (fuzzy lookup, my scoping, change tracking, data quality patterns)
- Update Available Skills table dv-query entry to match actual skill content
- Update dv-query frontmatter: proactive framing for bulk reads, not just defensive fallback
* Fix cross-skill conflicts and routing errors found in full audit
- Remove schema creation from dv-data tool table row (belongs in dv-metadata)
- Fix Hard Rule 1 bad redirect: was dv-data, now points to Hard Rule 1 above
- Clarify @odata.bind casing: SDK auto-handles record payloads (b6+), raw Web API still manual
- SDK checklist now routes each operation to the correct skill
- Fix dv-solution import path: was scripts.auth, now uses sys.path.insert + auth pattern
- Tool priority summary now consistent with detailed read-volume bullet rule
- Replace undefined "single page" with "no paging needed" in dv-query and dv-overview
* Fix agent-rail issues found in second full cross-skill audit
- Fix get_token() STOP warning: was prohibitive, now routes to Raw Web API list for context
- Fix @odata.bind casing: was imprecise (implied auto-correct), now explicit (SDK stops lowercasing, still need correct SchemaName)
- Complete PublishXml stub in dv-metadata: was a comment, now full runnable code
- Add solution-confirmation-first requirement to dv-metadata header (was only in dv-overview)
- Clarify MCP availability check: explicit vs implicit MCP requests are different branches, add explicit label
- Define multi-step logic in dv-data: sequential MCP calls are NOT multi-step; script only for bulk/transform/retry/CSV
- Fix missing os/sys.path.insert in dv-query N:N expand and apply sections (would fail with NameError)
* Fix code correctness and routing errors found in third cross-skill audit
- Add missing sys.path.insert to dv-metadata table creation and forms blocks
- Clarify PublishXml block requires env/token from form creation setup above
- Complete retrieve/modify form stub with full GET + PATCH runnable code
- Fix N:N $ref association routing in dv-data: was dv-query (wrong), now raw Web API with URL pattern
- Clarify dv-data supports list: remove standalone read, add note that reads within write workflows are acceptable
- Add HAVING qualifier to dv-query count table: simple count -> MCP, count+HAVING -> Web API
- Fix PAC CLI command in dv-metadata: was add-reference (solution deps), now add-solution-component (components)
- Standardize volume guidance phrasing: single page -> no paging needed (matches Hard Rule 2)
* fix: audit 4 corrections across dv-connect, dv-data, dv-query, dv-solution
- dv-solution: add client setup before publisher discovery in Step 1
- dv-solution: add sys.path.insert and define env in user role check block
- dv-connect: fix MCP capabilities table to reflect update_table supports column add
- dv-connect: add missing import os to gitignore block
- dv-query: note that DataFrame write-back is a write operation, cross-ref dv-data
- dv-query: document Jupyter auth exception (no scripts/auth.py in notebooks)
- dv-data: add DataFrame write-back section cross-referencing dv-query
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: pre-push static eval corrections in dv-data and dv-solution
- dv-data: change illustrative import fragments from python to plain fences
to avoid false-positive sys.path.insert eval failures; add clarifying note
- dv-solution: add Skill Boundaries table (was missing, only skill without one)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* add: static eval suite for skill file quality checks
Implements the P0 evals from the offline evals proposal as a runnable
script. Checks all SKILL.md files for:
- EVAL-AUTH-01: no 'from scripts.auth import' pattern
- EVAL-PY-01: sys.path.insert present and ordered before 'from auth import'
- EVAL-PY-04: no all-comment stub blocks presented as runnable code
- EVAL-PY-05: get_token() not used in DataverseClient blocks
- EVAL-PY-06: load_env() called before os.environ access
- EVAL-PAC-02: no 'pac --version' invocations
- EVAL-COMPLETE-01: Skill Boundaries table present in every skill
Usage: python .github/evals/static_checks.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: consolidate pip install to dv-connect, add pandas to tools-setup
dv-data had a redundant pip install line -- dv-connect owns installation
via the mandatory connect flow. Removed from dv-data. Also added pandas
to tools-setup.md to match dv-connect/SKILL.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* add: CLAUDE.md for contributor-facing AI assistant guidance
Documents plugin structure, skill authoring rules (auth pattern, no stubs,
skill boundaries, MCP/SDK/Web API priority), and the eval check to run
before every commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* add: GitHub Actions workflow for skill static evals
Runs on every PR that touches skill files or the eval suite itself.
No external dependencies -- stdlib only, completes in seconds.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* add: CodeQL security analysis workflow
Runs on push/PR to main and weekly on a schedule.
Standard requirement for Microsoft public OSS repos.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: skip CodeQL SARIF upload for fork PRs
External contributors run with read-only tokens -- uploading security
results requires write access only available to internal PRs. Analysis
still runs and the check still passes/fails correctly for all PRs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: categorize evals and add structure/discoverability checks
Reorganizes static_checks.py into 5 named categories (CAT-1 through
CAT-5) with one function per category -- easier to extend without
touching unrelated checks.
New checks added (CAT-4 and CAT-5):
- EVAL-STRUCT-01: frontmatter has required name and description fields
- EVAL-STRUCT-02: frontmatter name matches directory name
- EVAL-STRUCT-03: description contains 'Use when:' routing trigger
- EVAL-STRUCT-04: description contains 'Do not use when:' routing trigger
- EVAL-COMPLETE-02: Skill Boundaries cross-references point to real skills
- EVAL-COMPLETE-03: dv-overview Available Skills table lists every skill
- EVAL-COMPLETE-04: no references to removed dv-python-sdk skill
Output now groups failures by category prefix for readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* skills: add multi-table import patterns and metadata guardrails from BulkImport experiment
- dv-data: multi-table FK-ordered import with in-memory lookup maps, chunk helper,
idempotent re-run via UpsertItem with alternate keys
- dv-metadata: EntityDefinitions startswith() filter limitation, idempotent table
creation pattern (catch 0x80040237)
- dv-overview: Windows background job monitoring caveat (silent empty output)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: cross-skill consistency audit — correct batching claims, stale counts, routing gaps
- dv-data: fix false "built-in batching" claims (SDK sends all records in
one POST, does NOT chunk); add ThreadPoolExecutor pattern for large imports;
add frontmatter triggers for multi-table/large dataset sections
- dv-overview: fix volume guidance batching claim; fix index descriptions
(dv-metadata uses SDK not just Web API; remove phantom continue-on-error)
- README: update skill count from 5 to 6 after dv-python-sdk split
- evals: add EVAL-COMPLETE-05 — README skill count must match actual directories
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: end-to-end validation fixes from 5 BulkImport runs (297K records, 21 tables)
Findings from running the same dataset against 5 fresh environments with
and without skills — reduced import time from 44 hours to ~1 hour.
dv-data:
- Default to UpsertItem with alternate keys (not create) for idempotent imports
- Adaptive chunking: start at 1K, ramp to 4K on success, back off on
payload/timeout failure, cap at last successful size
- Cross-table parallelism with ThreadPoolExecutor, sequential within tables
(concurrent writes to same table cause SQL deadlocks)
- UpsertMultiple fails when key columns appear in record body (bulk-only bug)
- Per-table error isolation in ThreadPoolExecutor (one failure must not kill others)
- EntitySetName must be queried, not guessed (English pluralization is irregular)
- Post-import verification pattern
- flush=True on all long-running print statements
dv-metadata:
- Phased schema creation (tables -> keys -> lookups) to avoid lock contention
- Column naming: Src prefix for source IDs to avoid nav property collisions
- Alternate key creation via SDK (client.tables.create_alternate_key)
- Idempotent creation with retry for deadlocks and lock contention
- Publisher/solution creation SDK snippet in preamble (agent skips dv-solution)
- Mandatory user confirmation for solution name and publisher prefix
dv-query:
- Intent-driven routing table (user question -> right tool)
- SDK-First Rule for reads (no raw HTTP for queries)
- $apply as primary for single-table aggregation
- pandas merge with $select for cross-table (always pass select=)
- client.query.sql() documented with actual limitations (no JOINs, 5K cap)
- QueryBuilder gated behind SDK b8+ (doesn't exist in b7)
- $expand must use nested $select to avoid fetching all columns
- include_annotations required for formatted values (was silently returning None)
dv-overview:
- Anti-introspection rule (no dir/inspect — follow skill patterns)
- Explicit NEVER list for raw Web API
- Inline SDK snippet for publisher/solution creation
- Aggregation routing in SDK checklist
evals:
- EVAL-AUTH-02: every get_token/urllib block must justify why SDK not used
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: correct false auto-batching claim in safety doc, add publisher prefix to irreversible ops
- SDK does NOT auto-batch — agent chunks with adaptive sizing (skill-enforced)
- Upsert with alternate keys is the idempotent import pattern, not CreateMultiple
- Publisher prefix is permanent and listed in irreversible operations table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add version bumping instructions to CLAUDE.md
Lists all four files that must stay in sync and semver guidance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address review nits — pandas skip condition, urlopen timeout, regex info-string
* fix: increase urlopen timeout to 120s to match Dataverse server-side limit
* fix: increase urlopen timeout to 150s
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Saurabh Badenkal <sbadenkal@microsoft.com>
Co-authored-by: Saurabh Ravindra Badenkal <32964911+saurabhrb@users.noreply.github.com>1 parent a6dda75 commit 87bc553
18 files changed
Lines changed: 1922 additions & 622 deletions
File tree
- .claude-plugin
- .github
- evals
- plugins/dataverse
- .claude-plugin
- .github/plugin
- skills
- dv-connect
- references
- dv-data
- dv-metadata
- dv-overview
- dv-python-sdk
- dv-query
- dv-solution
- plugin
- workflows
- docs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
0 commit comments