Skip to content

Changelog

All notable changes to crm are documented in this file.

The format is loosely based on Keep a Changelog, and this project adheres to Semantic Versioning. Releases from 0.12.0 on are cut automatically by python-semantic-release from Conventional Commit messages; new version sections are inserted below this line.

v1.71.0 (2026-07-19)

Documentation

  • Address review round 1 — ADR front matter, wording, harden admin-merge guidance (#902, b22ed41)

  • ADR for the crm skill evaluation system (#874, c40fb28)

  • Promote agent-memory knowledge into ADRs and contributor docs (#902, b22ed41)

  • Promote memory knowledge into repo — ADRs 0029/0030, CI traps, D365 Web API quirks (#902, b22ed41)

  • Record on-prem-priority stance in CLAUDE.md (f33bcbf)

  • research: Pilot task harvest — community sourcing validates demand-weighted corpus (#885, 9bf00b5)

  • skill sync: Sync matt pocock skills (450092b)

Features

  • Add static skill-lint gate (Machine A) (#901, f54dfb6)

Refactoring

  • Give check_self_containment an explicit (router, references) signature (#901, f54dfb6)

  • Harden skill-lint self-review findings (#901, f54dfb6)

  • Retire skill-bundle checks now owned by the lint gate (#901, f54dfb6)

Testing

  • Add static skill-lint gate (Machine A) (#901, f54dfb6)

v1.70.0 (2026-07-17)

Bug Fixes

  • Harden self-update per review (#873, 7384b70)

  • Preserve payload on failed upgrade; surface refresh failures (#873, 7384b70)

Documentation

  • Document method-aware self-update; type + guard fixes (#873, 7384b70)

  • Qualify self-update skill-refresh guarantee (failed upgrade leaves skills untouched) (#873, 7384b70)

Features

  • Make self-update install-method-aware (#873, 7384b70)

  • Make self-update install-method-aware (uv/pipx upgrade, fix broken pip hint) (#873, 7384b70)

v1.69.5 (2026-07-17)

Bug Fixes

  • skill: Hide internal dev skills from bare npx skills add (#869, 8bbb323)

  • skill: Make npx skills install banner actually install the crm skill (#869, 8bbb323)

  • skill: Make the npx skills install banner actually install the crm skill (#869, 8bbb323)

  • skill: Normalize any existing metadata.internal instead of duplicating (#869, 8bbb323)

Chores

  • Share Claude skills with Codex (0ce22f7)

Code Style

  • Ruff format the plugin-manifest regression test (#869, 8bbb323)

Documentation

  • triage: Record MCP server as out-of-scope (#610, 26dea69)

Testing

  • skill: Assert manifest points at canonical ./crm/skills (#869, 8bbb323)

v1.69.4 (2026-07-13)

Bug Fixes

  • plugin: Default plugintypes.name to type name so workflow activities get a designer label (#867, ee80d50)

Chores

  • Adopt zizmor Actions security auditor + fix workflow hygiene (#865, ba028f2)

v1.69.3 (2026-07-11)

Bug Fixes

  • ribbon: Wrap invalid base64 from b64decode at org boundaries (#862, c814021)

Chores

  • Adopt semgrep CE with dry-run-contract house rule (#856, 58532fc)

  • Adopt tooling-sweep quick wins — actionlint, codespell, coverage artifact (#859, 1370d28)

  • review: Wire post-trial review lanes — Copilot round 1 only, CodeRabbit owns iteration (#863, b22bbae)

v1.69.2 (2026-07-11)

Bug Fixes

  • Bump ruff target-version to py313 + mechanical UP/B905 sweep (#857, 112dbb7)

Chores

  • Exclude D205 permanently instead of as adoption debt (#855, e253872)

v1.69.1 (2026-07-11)

Bug Fixes

  • security: Resolve initial CodeQL alerts (perms, action pinning, 0o644) (#853, c719f28)

Chores

  • Address CodeRabbit review on cloud-ship gh->MCP override (#850, c110f1b)

  • Defer fire polling bound to ship's copilot-loop spec (#850, c110f1b)

  • Extend cloud-ship gh->MCP override to repo docs (phase-1 claim) (#850, c110f1b)

  • Make fire poll-ceiling defer to step-4/5, not "proceed" (#850, c110f1b)

  • Reword blocked-hand-off label transform as plain prose (#850, c110f1b)

  • Route cloud-ship GitHub ops through MCP connector (#850, c110f1b)

v1.69.0 (2026-07-11)

Chores

  • review: Address round-1 review comments (#845, aef8fec)

  • review: Wire CodeRabbit engagement into agent PR workflows (#845, aef8fec)

Documentation

  • review: Pin --pythonversion in CLAUDE.md lint cmd; note click 8.4.1 raise in ADR 0026 (#852, 653ee85)

Features

  • Raise Python floor to 3.13 with a single-version policy (#852, 653ee85)

v1.68.3 (2026-07-11)

Bug Fixes

  • Re-anchor type-ignore comments detached by the reformat (#844, 31a9aa3)

  • Repair autofix-removed re-exports, apply UP unsafe fixes, scoped ignores (#844, 31a9aa3)

  • Resolve remaining ruff findings (E501, B904, B023, F841, D41x, E741, F402) (#844, 31a9aa3)

Chores

  • Add ruff config, dev deps, pre-commit hook, CI lint steps (#844, 31a9aa3)

  • Adopt ruff lint + format with pre-commit hook and CI gate (#844, 31a9aa3)

  • review: Derive all reviewers from the canonical coding-standards doc (#842, d45a0e7)

Code Style

  • One-time whole-tree ruff format + autofixes (#844, 31a9aa3)

Documentation

  • Record D205 adoption debt in the canonical standards doc (#844, 31a9aa3)

  • contributing: Add canonical coding-standards.md (#836, fa821c4)

v1.68.2 (2026-07-11)

Bug Fixes

  • Address Copilot review on safe_xml messaging (#841, 7f9def7)

  • Harden org-supplied XML parsing against entity-expansion bombs (#841, 7f9def7)

  • Wrap corrupt-export zip errors in _rewrite_customizations (#841, 7f9def7)

Chores

Documentation

  • research: Code-quality tooling sweep — 7-item shortlist beyond CodeRabbit/CodeQL (#823, 5118dcd)

v1.68.1 (2026-07-11)

Bug Fixes

  • metadata: Add --behavior to update-attribute for the one-time datetime behavior change (#834, 4bd894c)

v1.68.0 (2026-07-11)

Bug Fixes

  • cli: Honor trailing --json/--jq in the passive update-check guard (#835, f654de9)

  • cli: Skip --fields/--jq values in the JSON-mode argv scan (#835, f654de9)

Chores

  • skills: Strip model-invocation flag in user-dir source too (1096124)

Documentation

  • Add customizations-as-code golden-workflow guide + skill spine (#815, 95ca069)

  • Address Copilot round 2 — crm prefix in spine, unsplit code spans (#815, 95ca069)

  • Document dual-position global options (#835, f654de9)

  • Fix stale-plan anchor + clarify plug-in projection in customizations-as-code guide (#815, 95ca069)

  • Make convergence-verify steps read-only (--dry-run) (#815, 95ca069)

  • Pass doc-example + skill-bundle gates; tighten skill spine (#815, 95ca069)

  • research: CodeQL evaluation — go on default setup + security-extended (#822, 1eca9d3)

  • research: CodeRabbit OSS plan eligibility + PR-level surface (#820, 19f94ff)

  • skills: Address Copilot round 1 on audit-crm-skill snippets (#816, 94a1f4f)

  • skills: Fix stale claims, add D365 best-practice gotchas, trim --help restatements (#816, 94a1f4f)

  • skills: Register-image --step matches name or GUID, not name only (#816, 94a1f4f)

Features

  • cli: Accept --json/--fields/--jq/--profile/--dry-run after the subcommand (#835, f654de9)

v1.67.0 (2026-07-10)

Documentation

  • apply: Document UI kinds in the approval-gated plan loop (#813, 20f60ef)

Features

  • apply: Plan artifact + prune cover UI kinds (closes PRD #791) (#813, 20f60ef)

  • apply: Record UI-kind changed-field set in the plan artifact (#813, 20f60ef)

v1.66.1 (2026-07-10)

Bug Fixes

  • apply: App-publish and read back model-driven apps so apply-created apps are visible (#812, fba9953)

v1.66.0 (2026-07-10)

Bug Fixes

  • export-spec: Warn on failed app-bindings read; fail-fast e2e poll (#811, 02f3bf9)

Features

  • export-spec: Project model-driven apps + sitemap (#811, 02f3bf9)

  • export-spec: Project model-driven apps + sitemap (#797) (#811, 02f3bf9)

Performance Improvements

  • export-spec: Filter app bindings-count query server-side (#811, 02f3bf9)

v1.65.0 (2026-07-10)

Features

  • apply: Converge app component set + sitemap on re-apply (#810, 5febbd1)

v1.64.0 (2026-07-10)

Bug Fixes

  • apply: Fail hard when a created app's id can't be resolved (#808, 97ec6a1)

Documentation

  • apply: Document apps: spec create path (model-driven app + sitemap) (#808, 97ec6a1)

Features

  • apply: Apps: spec create path — model-driven app + sitemap (#808, 97ec6a1)

  • apply: Model-driven app + sitemap spec create path (#808, 97ec6a1)

Refactoring

  • apply: Drop redundant _require in app-block validation (#808, 97ec6a1)

Testing

  • apply: Live e2e for apps: create path; surface sitemapid (#808, 97ec6a1)

v1.63.0 (2026-07-10)

Bug Fixes

  • apply: Qualify forms dry-run diff keys by scope (Copilot round 2) (#807, b83e9aa)

Features

  • apply: Converge or refuse drift in spec forms: blocks (#807, b83e9aa)

  • apply: Converge or refuse drift in spec forms: blocks (#793) (#807, b83e9aa)

Refactoring

  • apply: Harden handler-prop validation + reorder perf (Copilot round 1) (#807, b83e9aa)

Testing

  • apply: Fix pyright reportOptionalMemberAccess in forms reconcile test (#807, b83e9aa)

  • apply: Live e2e + section-order coverage; docs-sync for forms reconcile (#807, b83e9aa)

  • apply: Make forms-refuse e2e spec validation-valid (#807, b83e9aa)

v1.62.0 (2026-07-10)

Documentation

  • export-spec: Clarify skipped-bucket channel and envelope meta (#806, 65ba217)

Features

  • export-spec: Project entity forms (seedable round-trip) (#806, 65ba217)

v1.61.0 (2026-07-10)

Bug Fixes

  • apply: Reject non-mapping form sub-blocks with a clean usage error (#805, 562ce27)

  • apply: Report resolved form name/formid, not the placeholder (#805, 562ce27)

  • apply: Tighten forms: spec validation up front (#805, 562ce27)

  • apply: Treat columns: null as absent in form convergence (#805, 562ce27)

  • tests: Type-clean forms-phase test helper for CI pyright (#805, 562ce27)

Documentation

  • research: Verify customizations-as-code market claims (#801, 3fc6299)

Features

  • apply: Converge entity forms from a spec forms: block (#805, 562ce27)

Testing

  • apply: Cover forms --stage-only + clarify apply_form_spec docstring (#805, 562ce27)

v1.60.0 (2026-07-10)

Bug Fixes

  • Honest totals + None-safe human rendering in org brief (#804, 553f706)

  • Make org brief request budget truly constant + generic docs placeholders (#804, 553f706)

Documentation

  • Clarify export-gap docstrings + sharpen completeness diagnostic (#789, 09cc3ed)

  • Tighten export-gap contract comments to modelled surface (#789, 09cc3ed)

Features

  • Add crm org brief — one-call agent-first org inventory (#804, 553f706)

Performance Improvements

  • Narrow org brief $count reads with $select; clarify workflow page bound (#804, 553f706)

Testing

  • Prove EXPORTED_KEYS emit via maximal-payload roundtrip (#799, f3dc245)

  • Prove EXPORTED_KEYS emit via maximal-payload roundtrip (#788) (#799, f3dc245)

  • Reify export-spec adapter gaps as data + contract test (#789, 09cc3ed)

  • Reify export-spec adapter gaps as data + contract test (#787) (#789, 09cc3ed)

  • Report both directions in roundtrip-coverage guard message (#799, f3dc245)

v1.59.1 (2026-07-10)

Bug Fixes

  • forms: Address review — placeholder GUIDs, case-insensitive preserve (#786, 1bd2a65)

  • forms: Clone forms with a custom-control subgrid descriptor (#786, 1bd2a65)

Documentation

  • xml_edit: Note regenerate_guids preserve is value-scoped (#786, 1bd2a65)

v1.59.0 (2026-07-09)

Documentation

  • Fix --to → --output in ribbon apply flow references (#784 review) (#784, 5b0c272)

Features

  • Ribbon working-copy flow — offline --diff-file edits + ribbon apply (#784, 5b0c272)

Testing

  • Fix pyright errors in ribbon working-copy tests (#784, 5b0c272)

v1.58.1 (2026-07-09)

Bug Fixes

  • Exit 2 for a half-supplied disassociate flag pair (#783, 66d1e90)

  • Reject invalid input and harden edge cases across nine commands (#783, 66d1e90)

Documentation

  • Explain the true/false-label compare-to-default guard rationale (#783, 66d1e90)

  • Sync plugin image-type and auto-number-format guards with #693 (#783, 66d1e90)

v1.58.0 (2026-07-09)

Bug Fixes

  • Validate plan shape at load so a malformed plan is a clean error (#781, e9940fb)

Features

  • Execute an approved plan with apply --from-plan (ADR 0022 slice 2) (#781, e9940fb)

Testing

  • Add # pyright: basic header to marker guard test (#782, 55e80fd)

  • Mark shared e2e fixtures with a greppable E2E_MARKER (#782, 55e80fd)

  • Mark shared e2e fixtures with a greppable E2E_MARKER (#769) (#782, 55e80fd)

  • Satisfy pyright strict in the marker guard test (#782, 55e80fd)

  • Self-seed contacts in odata cursor e2e test (#780, a6b4841)

  • Self-seed contacts in odata cursor e2e test so it runs on a bare org (#768) (#780, a6b4841)

  • Sharpen _marker_prefix wording (self-review) (#782, 55e80fd)

v1.57.0 (2026-07-09)

Bug Fixes

  • Pin an unreadable payload as null instead of aborting the plan write (#778, 611d25d)

Features

  • Serialize the dry-run apply drift report as a plan artifact (-o/--plan-out) (#778, 611d25d)

Refactoring

  • Stream payload hashing and include resolved path in read error (#778, 611d25d)

v1.56.1 (2026-07-09)

Bug Fixes

  • test: Remove leaked ribbon button before deleting its icon web resource (#779, f9e6e0e)

Chores

  • build: Bundle jq engine in PyInstaller binaries (#777, 21947d7)

v1.56.0 (2026-07-09)

Bug Fixes

  • Route --jq eval-error through the canonical error envelope (#776, fc0f56a)

Chores

  • Color-code e2e_all.py live output by test outcome (89f2279)

Documentation

Features

  • Add global --jq output shaper for --json payloads (#776, fc0f56a)

v1.55.0 (2026-07-09)

Features

  • Global --fields output projection at the emit seam (#772, 377c0be)

v1.54.0 (2026-07-09)

Features

  • Add ribbon button icon support (add-button flags + set-icon verb) (97b791c)

v1.53.3 (2026-07-09)

Bug Fixes

  • Retry CustomizationLockException on all customization writes (#774, a8064eb)

  • Ribbon set-label/set-rules guide to publish when staged diff is empty (#766, 05926ea)

v1.53.2 (2026-07-09)

Bug Fixes

  • Convert validation-before-backend bad-argument sites to usage errors (exit 2) (#770, 8a502d8)

  • Keep empty --file/--xml-file as exit-1 content error, not a usage error (#770, 8a502d8)

Documentation

  • Align exit-code contract notes with the usage-error reclassification (#770, 8a502d8)

  • adr: Reopen ADR 0012 — free durable cloud e2e org (#760, dca1c4f)

v1.53.1 (2026-07-09)

Bug Fixes

  • Add canonical --user-owned/--user-id/--display flags with hidden aliases (#765, b3b2848)

v1.53.0 (2026-07-09)

Bug Fixes

  • Bulk-detect reports flagged records, not pairs (#761, f823283)

  • Fail fast on malformed FetchXML; narrow dry-run test comment (#761, f823283)

  • Follow paging in bulk-detect result read; placeholder test GUID (#761, f823283)

  • Wrap app set-sitemap --xml-file read in clean error envelope (#755, f8f92ff)

Documentation

  • Include job_name in bulk-detect examples; assert full pair in e2e (#761, f823283)

Features

  • Dup bulk-detect verb wrapping BulkDetectDuplicates job (#761, f823283)

v1.52.1 (2026-07-09)

Bug Fixes

  • Address Copilot review — presence-based alias precedence, docstring (#763, 69e7d08)

  • Canonical --order-by for OData ordering with hidden aliases (#763, 69e7d08)

Chores

  • skills: Sync-skills tool + vendor personal skills (#762, 88c328d)

Documentation

  • merge-gate: Harden review-skill invocation, e2e completion, issues section (99989bf)

v1.52.0 (2026-07-09)

Features

  • Audit enablement via --audit/--no-audit on metadata create/update verbs (#759, f5925a9)

v1.51.24 (2026-07-09)

Bug Fixes

  • Add --secure-configuration to plugin register-step (#758, 3fdbfc4)

Documentation

  • Clarify --secure-configuration dry-run echo (Copilot review) (#758, 3fdbfc4)

v1.51.23 (2026-07-09)

Bug Fixes

  • profile: Add SSL-verification toggle to profile edit (#757, 1b82dfe)

v1.51.22 (2026-07-08)

Bug Fixes

  • --publish parity on ribbon add-button/remove and app set-sitemap (#754, edda0a3)

  • Add --skip-dependency-check and --formatted to solution stage-and-upgrade (#756, de5e239)

v1.51.21 (2026-07-08)

Performance Improvements

  • Collapse redundant reads and cap batch Retry-After (#753, 0dd27e0)

Testing

  • Make validate request-count assertions explicitly case-insensitive (#753, 0dd27e0)

v1.51.20 (2026-07-08)

Bug Fixes

  • Address Copilot review on #703 batching (#752, c67cf23)

Documentation

  • Note run_batched $batch retry semantics (#703 review) (#752, c67cf23)

Performance Improvements

  • Route four N+1 HTTP loops through $batch (#752, c67cf23)

v1.51.19 (2026-07-08)

Bug Fixes

  • Harden apply spec-read + alt-key name-map seam + orphaned temp-file reap (#744 #743 #740 #739) (43ee3ae)

v1.51.18 (2026-07-08)

Performance Improvements

  • Lazy the appmodule saxutils and completion-registry subprocess/tempfile imports (#749, 1c9f6ef)

v1.51.17 (2026-07-08)

Bug Fixes

  • Allowlist ADR-0022 rejected/unshipped command examples (#748, e3f9ed2)

v1.51.16 (2026-07-08)

Bug Fixes

  • Preserve lookup schema-name casing and export entity/optionset/view descriptions (#745, dba87c8)

Documentation

  • ADR 0022 plan artifact (approval-gated apply) + glossary terms (5aadace)

  • Clarify optionset description is applied on create, not update-reconciled (#745, dba87c8)

v1.51.15 (2026-07-08)

Bug Fixes

  • Export Customer columns as the customer kind (#742, 5a7fa62)

  • Truth-pass discoverability surfaces (catalogue, error text, docstring, skill) (#738, b6bd23a)

Documentation

  • Note workflow-run + >5000-row seeds and the count-cache caveat (8ec3754)

v1.51.14 (2026-07-08)

Bug Fixes

  • Create profile secret files 0600 and make atomic writes collision-safe (#733, 4688d5f)

  • Make atomic-write temp name independent of target name length (#733, 4688d5f)

Documentation

  • Drop dead guides/session-locking.md reference from atomic-write docstring (#733, 4688d5f)

Testing

  • Scope concurrent-writer stress test to POSIX (#733, 4688d5f)

v1.51.13 (2026-07-08)

Bug Fixes

  • Degrade alt-key column-type lookup gracefully; use explicit BOM escapes in tests (#731, a8f1a9a)

  • Make file-boundary reads UTF-8/BOM-tolerant and alt-key CSV values type-correct (#731, a8f1a9a)

  • Resolve alt-key column types through the cached name-map seam (#731, a8f1a9a)

Documentation

  • Note durable on-demand workflow seed unblocking activate/clone e2e (d25dc4c)

v1.51.12 (2026-07-08)

Bug Fixes

  • Honor dry-run contract in workflow/plugin state changers (#730, 439daec)

Documentation

  • Document dry-run preview shapes for workflow/plugin state changers (#730, 439daec)

v1.51.11 (2026-07-08)

Bug Fixes

  • Require confirmation for unguarded destructive commands (#728, 996be24)

Chores

  • agents: Add blindspot + quiz-before-merge skills, .agent symlink tree (#729, df85958)

  • agents: Restructure CLAUDE.md, add blindspot + quiz-before-merge skills, .agent symlinks (#729, df85958)

Documentation

  • Add assert-under-optimize house rule to copilot instructions (#729, df85958)

  • Add assert-under-optimize house rule to copilot instructions (d678151)

  • Restructure CLAUDE.md walls into scannable lists (#729, df85958)

v1.51.10 (2026-07-08)

Bug Fixes

  • Keep read commands successful when session state can't be persisted (#719, 7c2ba3f)

v1.51.9 (2026-07-08)

Bug Fixes

  • Follow @odata.nextLink in workflow/solution list paths (#727, 71e2f44)

v1.51.8 (2026-07-08)

Bug Fixes

  • Prevent repl completion backend construction (4480495)

Documentation

  • Restructure CLAUDE.md walls into scannable lists (dd8e19b)

v1.51.7 (2026-07-08)

Bug Fixes

  • Guard solution publish --xml-file read; parametrize the IO sweep (#722, 5d5d7a9)

  • Wrap unguarded file IO in the OSError→domain-error idiom (#722, 5d5d7a9)

  • Wrap unguarded file IO in the OSError→domain-error idiom (#699) (#722, 5d5d7a9)

Chores

  • agents: Add merge-gate skill; ship deviation log; brief known-unknowns (#724, 9706f77)

Testing

  • Assert the operation (read/write), not just the path, in the IO sweep (#722, 5d5d7a9)

  • Isolate CRM_HOME in the file-IO sweep so CLI runs stay hermetic (#722, 5d5d7a9)

v1.51.6 (2026-07-08)

Bug Fixes

  • Render click exceptions as json envelopes (#721, e4e54cc)

Chores

  • agents: Align workflow skills, complete cloud-env skill closure (5e29c43)

v1.51.5 (2026-07-07)

Bug Fixes

  • Harden destructive confirmation helper (#717, d0f1a1d)

v1.51.4 (2026-07-07)

Bug Fixes

  • Recognise entity FormXml system forms in reverse root-parity check (#716, 5bf86e9)

v1.51.3 (2026-07-07)

Bug Fixes

  • Guard backend construction against bad-profile tracebacks (#698, 9468bb5)

Chores

  • Inject crm memory index into codex sessions (eedd279)

v1.51.2 (2026-07-06)

Bug Fixes

  • Normalize update write payloads (12f4793)

Documentation

  • Clarify integer bounds helper contract (12f4793)

  • Clarify read-shape update round trips (12f4793)

v1.51.1 (2026-07-05)

Bug Fixes

  • Strip whitespace from profile URLs and test add before saving (#677, d86cdd5)

Chores

  • Add codex migration adapters (5a021ed)

  • agents: Extract live-e2e skill; gate commits for secrets + git discipline (#674, 8751873)

Refactoring

  • apply: Migrate plugin assembly + step onto the component-kind adapter (#673, cd16bf4)

v1.51.0 (2026-07-03)

Bug Fixes

  • profile: Address Copilot review — report enforced flag, clearer clear-refusal (#672, 2f6b43f)

  • profile: Scope read-safe exemption to POST (Copilot round 2) (#672, 2f6b43f)

Documentation

  • Add crm prefix to read-only example comment (Copilot round 3) (#672, 2f6b43f)

Features

  • profile: Add read-only guardrail flag blocking org mutations (#672, 2f6b43f)

Testing

  • profile: Fix pyright types in read-only seam tests (#672, 2f6b43f)

v1.50.2 (2026-07-03)

Bug Fixes

  • examples: Metadata write examples need --solution + colon option syntax (#671, fd58531)

Testing

  • examples: Guards accept Click's --flag=value form too (#671, fd58531)

  • examples: Robust metadata-write guard + accurate failure-mode comment (#671, fd58531)

v1.50.1 (2026-07-03)

Bug Fixes

  • completion: Also complete target_set/related_set positionals (#670, 87bc38e)

  • completion: Honor --session in shell entity-set profile resolution (#670, 87bc38e)

  • completion: REPL attribute-name + OS-shell entity-set completion (#670, 87bc38e)

Documentation

  • completion: Use entity-set name in REPL --select example (#670, 87bc38e)

v1.50.0 (2026-07-03)

Bug Fixes

  • Gate examples picker on stdout TTY and relax gallery floor (#669, d8b180b)

  • Generic placeholders in examples + resolver handles leading globals (#669, d8b180b)

Features

  • Add crm examples curated gallery with anti-drift parse gate (#669, d8b180b)

v1.49.5 (2026-07-03)

Bug Fixes

  • Harden next-step hints per review — presence-based CRM_NO_HINTS, non-fatal store write (#668, 4d46e97)

  • Show-once next-step hints for new human users (#668, 4d46e97)

Documentation

  • Use full crm-prefixed command names in next-step-hints section (#668, 4d46e97)

v1.49.4 (2026-07-03)

Bug Fixes

  • Address Copilot review — clearer picker-disabled message, test cleanups (#667, 0bc09a6)

  • Interactive solution picker for no-arg solution export (#667, 0bc09a6)

Refactoring

  • apply: Migrate optionset, web resource, security role onto the component-kind adapter (#666, 6fb59cc)

  • apply: Reconcile on a security-role create race instead of replacing (#666, 6fb59cc)

v1.49.3 (2026-07-02)

Bug Fixes

  • Mask secret prompts with asterisks and add no-arg profile pickers (cd5a717)

Documentation

  • apply: Correct extra_validate ordering comment (#664, 1d62c51)

Refactoring

  • apply: Component-kind adapter interface + entity subtree migration (#664, 1d62c51)

v1.49.2 (2026-07-02)

Bug Fixes

  • ribbon: Route core ribbon errors through the D365Error contract (#662, b1fedb3)

  • ribbon: Translate malformed-response errors in the two core helpers (#662, b1fedb3)

v1.49.1 (2026-07-02)

Bug Fixes

  • repl: Reset entity-name completion cache on profile switch (#661, a696f57)

v1.49.0 (2026-07-02)

Bug Fixes

  • repl: Extend tab-completion to commands, flags, choices, profiles (e7a85e3)

  • repl: Harden completion against internal errors, tighten docs (e7a85e3)

  • session: Make list_profiles() read-only, no mkdir side effect (e7a85e3)

Chores

  • cloud-ship: Fix invalid --publisher-prefix in bootstrap script (#653, 906a924)

Documentation

  • Address Copilot round-1 nits (link style, key-1 grammar) (#648, 5173bbe)

  • Align frontmatter with step 2 (oldest open issue) (#649, 632c2c5)

  • Dedup crm skill invariants, split customizations ref, docs-sync anti-drift gates (#650, 2d7877c)

  • Tighten cloud-ship skill; document sandbox subagent absence (#649, 632c2c5)

  • Tighten ship skill lanes + delegation rule (#648, 5173bbe)

  • completion: Document REPL's built-in tab-completion (e7a85e3)

  • completion: Mention OS-shell --profile dynamic completion (e7a85e3)

  • skill: Split metadata reference into read + write-gotchas files (#651, 5b93abb)

Features

  • repl: Extend tab-completion to commands, flags, choices, profiles (e7a85e3)

v1.48.2 (2026-07-02)

Bug Fixes

  • Retry PublishAllXml on org-wide publish lock (0x80071151) (#642, e2431ef)

v1.48.1 (2026-07-02)

Bug Fixes

  • Stage metadata writes by default; --publish becomes opt-in (#641, 7c7654c)

Chores

  • Drop stale --default-solution flag from cloud-ship bootstrap (#640, f2f43f8)

  • Pin pyright to exact 1.1.411 so local lint matches CI (#640, f2f43f8)

v1.48.0 (2026-07-02)

Features

  • profile: Add rename command; validate publisher-prefix on add/edit (#639, 0fd2b53)

v1.47.0 (2026-07-01)

Features

  • Require explicit --solution for all customization writes (#636, 34967f9)

v1.46.1 (2026-07-01)

Bug Fixes

  • Flag clamped --count and signal more rows on a capped query page (#635, 06746c7)

v1.46.0 (2026-07-01)

Features

  • Identify serving profile/org/URL in JSON envelopes and whoami (#630, a37bc74)

v1.45.2 (2026-07-01)

Bug Fixes

  • Surface componenttypename in solution components JSON output (#629, 7de5e4d)

Chores

  • Add multi-target e2e runner (scripts/e2e_all.py) (#622, 4b4760d)

  • Multi-target e2e runner (scripts/e2e_all.py) (#622, 4b4760d)

Documentation

  • Document componenttypename and component-type code list (#629, 7de5e4d)

Testing

  • Update solution components JSON-mode test for componenttypename (#629, 7de5e4d)

  • e2e: Self-seed query saved/user skips; document workflow-activate fallback (#621, 767d2e5)

v1.45.1 (2026-06-28)

Bug Fixes

  • plugin: Create-message images key on "Id", not "Target" (#620, 6e0f204)

v1.45.0 (2026-06-28)

Documentation

  • Address self-review — README/ADR-0019 surface, BU/dispatch doc notes (#614, 4858f24)

  • Export-spec projects roles + web resources; apply accepts inline content (#614, 4858f24)

Features

  • solution: Export-spec — project security roles + web resources (#614, 4858f24)

Testing

  • Isolate CRM_HOME in scaffold tests (fixes #616) (b5a3bac)

v1.44.0 (2026-06-28)

Documentation

  • skill: On-prem CI deploy spine recipe (c58f251)

Features

  • solution: Export-spec — project a solution into a merged apply-spec (#615, 95f0633)

Refactoring

  • backend: Thread solution= through write verbs; delete solution_headers() (#609, f50507e)

v1.43.0 (2026-06-28)

Features

  • apply: Entity enable-only/identity + view record-PATCH reconcile (#608, beb48fb)

v1.42.0 (2026-06-28)

Documentation

  • adr: Classify the wider apply-reconcile spec surface (#598, 044dd3e)

Features

  • apply: Reconcile existing 1:N relationships + lookup attribute (#607, 67f40c2)

v1.41.0 (2026-06-28)

Bug Fixes

  • apply: Export-spec emits relationship lookup_description too (#604, 7d22dd0)

  • apply: Keep export-spec menu output apply-valid; correct datetime caveat (#604, 7d22dd0)

  • apply: Scope view fetchxml parse to the root entity; complete docs (#604, 7d22dd0)

Documentation

  • metadata: Correct remaining stale export-spec round-trip claims (#604, 7d22dd0)

  • metadata: Document the wider export-spec round-trip surface (#604, 7d22dd0)

Features

  • apply: Export-spec emits the wider spec surface (lossless round-trip) (#604, 7d22dd0)

Testing

  • e2e: Cover the wider export-spec round-trip live (#604, 7d22dd0)

v1.40.0 (2026-06-28)

Chores

  • evals: Address Copilot round 1 — active-leg progress key + env restore (#587, dcc6cc3)

  • evals: Address Copilot round 2 — robust host parse, graceful leg, target guard (#587, dcc6cc3)

  • evals: Default result.json + run.log into the run dir (51289b2)

  • evals: Live stderr progress + python -m evals.skill run front door (#587, dcc6cc3)

  • evals: Record 2026-06-26 baseline — cloud 100%, onprem 100% (4cbf83e)

  • evals: Reject --update-baseline for a single target; drop dead return (#587, dcc6cc3)

  • evals: Skill-efficacy review — persist traces, judge skill help/efficiency post-hoc (#589, b609486)

  • evals: Use host_of() in the prod-host guard for parser consistency (#587, dcc6cc3)

Documentation

  • context: Sharpen usage-error term + add validation-predicate (#600, 5a33012)

Features

  • apply: Let specs reach the full builder surface (attribute/relationship/entity/view) (#603, a023108)

Refactoring

  • metadata: Explicit quoting in reorder ParamType error (Copilot) (#600, 5a33012)

  • metadata: Sink command-body rules to core predicates + usage_guard (#600, 5a33012)

Testing

  • Audit & prune offline suite — remove 54 low-value tests, fix 6 drifted (#591, d89b10b)

  • Backfill offline coverage for testable blind spots (#592) (#594, 973dbc1)

  • Cover repl_skin pure layout helpers (_strip_ansi, _visible_len, _display_home_path) (1290908)

  • Prune round-2 audit findings — collapse redundancy, delete low-value tests (#590) (#593, 4380d87)

  • repl_skin: Make outside-home path test platform-independent (#600, 5a33012)

v1.39.2 (2026-06-26)

Bug Fixes

  • evals: Harden row_suffix against absent keys and non-mapping rows (#586, 8f5d199)

  • evals: Make skill-eval predicates org-agnostic to stop false on-prem fails (#586, 8f5d199)

Chores

  • evals: Add Machine B tracer — isolated single-task skill eval harness (#576, b116fde)

  • evals: Add opt-in Claude --analyze pass to Machine B harness (#579, 6c96209)

  • evals: Add opt-in Claude --analyze pass to the Machine B eval harness (#579, 6c96209)

  • evals: Add workflow-per-domain skill-eval task set + set runner (#577, b96eb52)

  • evals: Address Copilot round-1 — generic prefixes, stronger predicates (#577, b96eb52)

  • evals: Address Copilot round-2 — accurate scored-vs-demonstrated comments (#577, b96eb52)

  • evals: Address Copilot round-3 — test name + accurate prefix note (#577, b96eb52)

  • evals: Address review round 2 — fix probe docstring + README wording (#582, a65a4ee)

  • evals: Address review — prod-host guard on probe, utf-8 I/O, validate repeat (#582, a65a4ee)

  • evals: Address review — reject empty end_state.query, assert task shape (#579, 6c96209)

  • evals: Both-targets union runner + periodic baseline trend (#582, a65a4ee)

  • evals: Both-targets union runner + periodic baseline trend (#573) (#582, a65a4ee)

  • evals: Document --dangerously-skip-permissions for headless agent cmd (#581, c2ae98f)

  • evals: Enforce task target gate, capture agent exit, review polish (#576, b116fde)

  • evals: Harden isolation env, task parsing, and cleanup (review round 1) (#576, b116fde)

  • evals: Pass Claude subscription auth into skill-eval sandbox (#580, 0e3762d)

  • evals: Record 2026-06-26 baseline — cloud 100%, onprem 67% (2 harness fails, #584) (50a9a2a)

  • evals: Run subprocesses from sandbox, validate expect shape (review round 2) (#576, b116fde)

  • evals: Score diagnostic tasks from the analyzer verdict (#579, 6c96209)

  • evals: Self-review polish — typing idiom, cleanup-limitation honesty (#577, b96eb52)

  • evals: Skip diagnostic tasks in the set runner (#579, 6c96209)

  • evals: Strip only delimiter newlines so prompt body stays verbatim (review round 3) (#576, b116fde)

Documentation

  • evals: Document the --analyze pass and diagnostic tasks (#579, 6c96209)

  • evals: Note diagnostic VERDICT scoring in the analyze section (#579, 6c96209)

v1.39.1 (2026-06-25)

Bug Fixes

  • Allowlist ADR 0015 prose shorthand in doc-example gate (#575, 399f914)

Documentation

  • adr: Record skill-evaluation design (static gate + behavioral eval) (53d1484)

  • skill: Add workflow-xaml composition reference (#567, 57cc4d2)

Performance Improvements

  • Cache catalogue() so the skill-coverage gate walks the CLI once (#575, 399f914)

Testing

  • Add offline skill-coverage gate reconciling skill tree vs CLI catalogue (#575, 399f914)

  • Assert top-level names strictly superset groups in coverage gate (#575, 399f914)

v1.39.0 (2026-06-25)

Features

  • webresource: Push bulk-upserts a directory tree, publishes once (#566, a7d05fb)

v1.38.0 (2026-06-25)

Bug Fixes

  • Reject --strict/--no-rollback without --xaml-file (#565, ccd0406)

  • Route workflow XAML warnings via emit channel, guard file read (#565, ccd0406)

Documentation

  • Workflow update --xaml-file logic path (README, how-to, skill) (#565, ccd0406)

  • test: Note workflow update --xaml-file target-divergent e2e pair (#565, ccd0406)

Features

  • Workflow update --xaml-file on-prem XAML step-editing (#565, ccd0406)

Refactoring

  • Tighten workflow XAML rollback + provenance tests (#565, ccd0406)

Testing

  • e2e: Workflow update --xaml-file onprem-accept + cloud-refuse (#565, ccd0406)

v1.37.0 (2026-06-25)

Bug Fixes

  • apply: Reject null source_type and present formula on simple columns up front (#564, ab1f22d)

Documentation

  • export-spec: Document calculated/rollup source_type + formula_definition (#564, ab1f22d)

Features

  • export-spec: Capture calculated/rollup FormulaDefinition for round-trip (#564, ab1f22d)

Testing

  • apply: Reject formula on simple column + chained export→apply round-trip (#564, ab1f22d)

v1.36.0 (2026-06-25)

Bug Fixes

  • Address review — NCName-safe prefix scan; drop dead mxswa URI loop (#557, 83a1991)

  • Address review — skip mxswa property-elements; drop dead constants; tidy prefix scan (#557, 83a1991)

  • Report the real undeclared XAML namespace prefix, not 'xmlns' (#557, 83a1991)

Documentation

  • Cross-link CONTEXT.md terms to their ADRs and code seams (#563, f709768)

Features

  • Add validate_workflow_xaml pure reference-validator (#557, 83a1991)

  • Validate_workflow_xaml - pure XAML reference-validator (#557, 83a1991)

Testing

  • Add validate_workflow_xaml fixtures (red) (#557, 83a1991)

v1.35.0 (2026-06-25)

Features

  • apply: Opt-in --prune to delete solution components absent from the spec (#553, 5c8670b)

v1.34.0 (2026-06-25)

Documentation

  • Rebrand docs site and README to the gradient CRM logo (#560, cf6cb75)

Features

  • apply: Declare plug-ins (assembly/types/steps/images) in the spec (#561, 743d7de)

v1.33.0 (2026-06-25)

Bug Fixes

  • apply: Fail-fast spec validation + accurate publish/convergence docs (review round 2) (#559, 8025d9e)

  • apply: Harden spec validation and dry-run contract (review round 1) (#559, 8025d9e)

Documentation

  • apply: Document web resource & security role kinds; review fixes (#559, 8025d9e)

Features

  • apply: Declare web resources and security roles in the spec (#559, 8025d9e)

v1.32.0 (2026-06-25)

Features

  • apply: --dry-run emits a full drift report (b1fe805)

v1.31.0 (2026-06-25)

Chores

  • Relicense under PolyForm Noncommercial 1.0.0 (5963212)

Documentation

  • adr: Record 0004 reconsideration — codegen stays external (e2c17f8)

Features

  • apply: Convergent desired-state reconciliation for entity/attribute/optionset (#556, 0f74931)

v1.30.1 (2026-06-24)

Bug Fixes

  • Note .NET SDK prereq in pac install hint (1de97dd)

Refactoring

  • De-duplicate four byte-identical helpers (#543) (#546, 4f1a1a3)

  • Remove dead TypedDicts, REPL skin cruft, metadata leftovers (#545, f88f0be)

v1.30.0 (2026-06-24)

Documentation

  • Add ADR 0013 + Provenance wall term for on-prem workflow xaml editing (73189b5)

  • Add workflow create/update feasibility spec (bb63a73)

  • Enrich workflow feasibility spec with deep-research findings (0153878)

  • context: Add Failure enrichment term to the CLI contract (218a5fd)

Features

  • Add workflow update for editing workflow metadata on both targets (#542, ca9f371)

Refactoring

  • Deepen d365_errors with an additive enrich(exc) callback (#533, 6d575b3)

Testing

  • Add live e2e coverage for workflow clone/import/delete (#534, 39c16e9)

  • Add offline gate validating doc CLI examples against describe (#532, b87f2d7)

v1.29.0 (2026-06-23)

Documentation

  • Fix e2e worktree-CLI note — cwd beats PYTHONPATH, not the editable finder (329ae1e)

Features

  • plugin: Add register-type verb; correct auto-created-plugintype docstrings (e94956e)

Testing

  • e2e: Provision pac in CI + live offline solution pack/extract roundtrip (#530, 2867320)

v1.28.0 (2026-06-23)

Documentation

  • Fix non-working CLI examples in quickstart/index/agent (41ba0ba)

Features

  • solution: Migrate extract/pack to cross-platform pac solution (#527, 1c6c508)

v1.27.6 (2026-06-23)

Bug Fixes

  • Bump-guard messages reflect title-or-body breaking change (#526, fc3ec66)

  • Stop bump-guard stalling agent feat PRs; only major is label-gated (#526, fc3ec66)

Testing

  • e2e: Cover solution stage-and-upgrade + apply-upgrade (single-org managed recipe) (#525, 8cc88db)

v1.27.5 (2026-06-22)

Bug Fixes

  • Sla create writes numeric ObjectTypeCode to objecttypecode (#524, 0605420)

Testing

  • e2e: Assert persisted cancreate is not allowed (#522, bb6a42f)

  • e2e: Cover fieldsec add-permission on a secured custom column (#522, bb6a42f)

  • e2e: Cover workflow run dispatch-only vs seeded on-demand workflow (1cde24b)

v1.27.4 (2026-06-22)

Bug Fixes

  • Workflow run surfaces async_operation_id from asyncoperation entity (#521, 2383f1c)

Continuous Integration

  • e2e: Add .NET SDK step + document dedicated CS trial as cloud e2e target (ADR 0012) (#515, 26e4505)

Documentation

  • Clarify CS-trial profile wording + workflow provisioning step (#515, 26e4505)

  • Record 2026-06-22 e2e live run (on-prem + cloud) in TEST.md (d08407f)

  • adr: Dedicated CS Dataverse sandbox as the cloud e2e target (4b42fae)

Testing

  • e2e: Address review on theme publish test (#520, 6433923)

  • e2e: Assert seed filter matches zero rows before submit (#513, 791f30e)

  • e2e: Correct the 5 keep-skipped E2E_SKIP reasons + refresh TEST.md (#516, e58728f)

  • e2e: Cover async cancel + solution job-cancel via future-dated BulkDelete (#513, 791f30e)

  • e2e: Cover audit detail by generating an audit row inline (#519, e99ed3a)

  • e2e: Cover plugin assembly register/unregister lifecycle (build signed .dll from C# source) (41b4349)

  • e2e: Cover theme publish (capture -> publish throwaway -> restore) (#520, 6433923)

  • e2e: Poll audits table before skipping audit-detail test (#519, e99ed3a)

v1.27.3 (2026-06-22)

Bug Fixes

  • Skip uncreatable lookup companion columns in clone-entity spec (#501, c4fa0c2)

v1.27.2 (2026-06-22)

Bug Fixes

  • App create --if-exists skip swallows on-prem duplicate fault (0x80040216/500) (#499, 50b9b3a)

v1.27.1 (2026-06-22)

Bug Fixes

  • Sitemap add-subarea --pass-params + validate --dashboard exists (a38f1c0)

v1.27.0 (2026-06-22)

Features

  • Sitemap set-title / set-description (localized titles/descriptions) (#492, 563dcce)

v1.26.0 (2026-06-22)

Bug Fixes

  • view: Cascade-prune emptied parent filters on remove-filter (#493, c9a1295)

  • view: Harden remove-filter multi-condition path (#493, c9a1295)

Features

  • view: Add add-filter / remove-filter for FetchXML conditions (#493, c9a1295)

v1.25.0 (2026-06-21)

Documentation

Features

  • Add sitemap move-node to reorder a navigation node (#491, 408d7ab)

  • Add sitemap move-node to reorder a navigation node (B12) (#491, 408d7ab)

Refactoring

  • Tighten move-node same-type sibling semantics and test typing (#491, 408d7ab)

v1.24.0 (2026-06-21)

Bug Fixes

  • Harden dashboard web-resource warning and correct skill JSON contract (#490, f5b6d66)

  • Reject blank --url / --webresource in the command layer (#490, f5b6d66)

  • Validate remove-component selectors in the command layer (#490, f5b6d66)

Documentation

  • Document dashboard add-iframe / add-webresource / remove-component (#490, f5b6d66)

Features

  • Add dashboard add-iframe / add-webresource / remove-component (#490, f5b6d66)

Testing

  • Use _rowspan helper to satisfy pyright on rowspan assertion (#490, f5b6d66)

v1.23.0 (2026-06-21)

Bug Fixes

  • Address Copilot review on view editors (#489, c65e815)

  • Insert new view column attribute before order/filter in fetchxml (#489, c65e815)

Documentation

  • Document view edit-columns / set-order (B7) (#489, c65e815)

Features

  • View edit-columns / set-order editors (B7) (#489, c65e815)

Refactoring

  • Hoist attribute_info_or_raise to metadata, reuse in views (#489, c65e815)

Testing

  • Fix pyright return-type on e2e _cells helper (#489, c65e815)

v1.22.0 (2026-06-21)

Bug Fixes

  • Address review — usage-error for exclusive flags, cleaner JSON keys (#486, f6d6fe2)

  • Uniform global node-id uniqueness + parent-aware sitemap T3 (#486, f6d6fe2)

  • Warn against chaining --no-publish sitemap edits; strip subarea flags (#486, f6d6fe2)

Documentation

  • Sync sitemap live-edit verbs (README, how-to, skill) + bundle spec (#486, f6d6fe2)

Features

  • Add sitemap live-edit verbs (add-area/group/subarea, remove-node) (#486, f6d6fe2)

  • Sitemap live-edit verbs (add-area/group/subarea, remove-node) (B6) (#486, f6d6fe2)

Refactoring

  • Unquote annotations + cover comment-out T3 in sitemap e2e (#486, f6d6fe2)

v1.21.0 (2026-06-21)

Documentation

  • Clarify set-label solution requirement; e2e selects provisioned LCID (#484, 267129c)

  • Sync set-label docs + strengthen e2e LocLabel/tooltip assertions (#484, 267129c)

Features

  • Add ribbon set-label to set custom-button labels/tooltips (#484, 267129c)

  • Add ribbon set-label to set custom-button labels/tooltips (B11) (#484, 267129c)

Testing

  • Tighten set-label --lcid e2e assertion to resolved label or directive (#484, 267129c)

v1.20.1 (2026-06-21)

Bug Fixes

  • Add --solution to security create-role (#485, fe292b1)

  • Document if-exists-skip rationale; pyright-clean entity test (#485, fe292b1)

Documentation

  • Document create-role --solution and if-exists-skip nuance (#485, fe292b1)

Testing

  • E2e for create-role --solution membership (cloud-verified) (#485, fe292b1)

v1.20.0 (2026-06-21)

Features

  • Author custom security roles — security create-role + security set-role-privileges (3018c65)

v1.19.0 (2026-06-21)

Bug Fixes

  • Harden ribbon validate_rule_ids against invalid kind; clarify docs (#481, 6934f34)

  • Reject mis-cased Mscrm.* ribbon rule ids (case-insensitive prefix guard) (#481, 6934f34)

Documentation

  • Ribbon set-rules / add-custom-rule (how-to, skill, README) (#481, 6934f34)

Features

  • Ribbon set-rules / add-custom-rule (enable/display rules) (#481, 6934f34)

Testing

  • Assert ElementTree.find results non-None in ribbon rule tests (pyright) (#481, 6934f34)

  • Assert ribbon rule set by parsed value in e2e T3 (no drop/reorder) (#481, 6934f34)

v1.18.0 (2026-06-21)

Documentation

  • Clarify hide-action confirmation UX (interactive prompt, --yes skips) (#479, 74aab32)

Features

  • Add ribbon hide-button to hide OOB command-bar buttons (#479, 74aab32)

Testing

  • Satisfy pyright basic-mode optional/attribute checks in ribbon tests (#479, 74aab32)

v1.17.0 (2026-06-21)

Features

  • Chart editors — update/set-fetch/add-series/remove-series/set-groupby (B5) (#478, 16739c4)

v1.16.0 (2026-06-21)

Bug Fixes

  • Harden dashboard tile add per review (guard, occupied section, input ranges) (#477, ba23f43)

  • Top-align dashboard tile placement; clarify --section is empty-only (#477, ba23f43)

Documentation

  • Explain why dashboard tile commit skips the in-process T3 read-back (#477, ba23f43)

  • Sync dashboard add-chart / add-view tile editors (#477, ba23f43)

Features

  • Add dashboard add-chart / add-view (ChartGrid tiles) (#477, ba23f43)

Testing

  • Satisfy pyright on dashboard tile tests (assert Element non-None) (#477, ba23f43)

  • Use valid placeholder GUIDs for existing dashboard cells (#477, ba23f43)

v1.15.0 (2026-06-21)

Features

  • Add form set-field-props editor for field presentation properties (#476, 30bc45f)

v1.14.0 (2026-06-21)

Documentation

  • Clarify sibling-guard message (PATCH not POST) and dry-run comment (#475, 64a1ae4)

Features

  • Add form tab & section structure editors (B8) (#475, 64a1ae4)

Testing

  • Add offline command-layer + dry-run coverage for form tab/section verbs (#475, 64a1ae4)

v1.13.0 (2026-06-21)

Documentation

  • Clarify assert_external_guids_intact compares a multiset (#473, 6c1357e)

  • research: Safe customization-XML editors feasibility study (e46f05e)

  • triage: Document kind label axis (refactor, chore) (f05c757)

  • triage: Record SOAP-only messages as out-of-scope (#453, f5e3928)

Features

  • Add form JS event/handler & library wiring verbs (#474, a3cfdef)

Refactoring

  • Enforce publish-before-read-back in commit_xml_patch (#473, 6c1357e)

  • Extract shared xml_edit primitives + generalized direct-PATCH commit (#473, 6c1357e)

  • Extract shared xml_edit primitives and generalize the direct-PATCH commit (#473, 6c1357e)

v1.12.0 (2026-06-20)

Features

  • metadata: Hierarchical relationships, virtual entities, can-relate (#454, 99e2815)

v1.11.0 (2026-06-20)

Bug Fixes

  • Use UsageError + guard file I/O in discovery read commands (#456, 66380fb)

Chores

  • Re-trigger CI (missed synchronize dispatch) (#456, 66380fb)

Documentation

  • Sync README, how-to, skill, e2e for discovery reads (#456, 66380fb)

Features

  • Add solution/metadata/ribbon discovery reads (#456, 66380fb)

Refactoring

  • Address self-review for discovery reads (#456, 66380fb)

v1.10.0 (2026-06-20)

Features

  • metadata: Add 'metadata changes --since' for incremental metadata sync (#455, cadc910)

v1.8.1 (2026-06-20)

Bug Fixes

  • solution: Add help string to 'solution info' command (468de62)

Documentation

  • Audit crm skill CLI coverage + slim router; add skill-authoring tooling (b7fe5ec)

v1.3.1 (2026-06-20)

Bug Fixes

  • Add --type rollup|calculated and --formula-file to metadata add-attribute (#443, edcbd0d)

v1.0.0 (2026-06-18)

  • Initial Release

Pre-1.0 development history

The sections below (v0.12.0v4.31.1) record crm's pre-launch development. Before its first public release the project reset its version to 1.0.0 to shed the misleading inflated number (see ADR 0011); this history is kept for the record. Releases from 1.0.0 on are inserted above this line by python-semantic-release.

v4.31.1 (2026-06-18)

Bug Fixes

  • cli: Align solution import publish/overwrite flags to opt-in pairs (#378) (#414, c4a3969)

v4.31.0 (2026-06-18)

Bug Fixes

  • translation: Add --publish to translation import (#377) (#413, 6f2af59)

  • translation: Restore publish-all warning when --publish skipped by dry-run (#413, 6f2af59)

Documentation

  • translation: Document --publish flag on translation import (#413, 6f2af59)

Features

  • translation: Add --publish flag to translation import (#413, 6f2af59)

v4.30.1 (2026-06-18)

Bug Fixes

  • solution: Add solution import --skip-dependency-check (#412, 8ceb564)

v4.30.0 (2026-06-18)

Features

  • async: Async list --order-by/--filter (153f279)

v4.29.0 (2026-06-18)

Features

  • data: First-class server-side BulkDelete verb (#410, b9c7029)

v4.28.0 (2026-06-18)

Documentation

  • ship: Address copilot review nits (#407, 2631c23)

  • ship: Address copilot round-2 nit (#407, 2631c23)

  • ship: Instruct phase 6 to fill the repo PR template (#407, 2631c23)

  • ship: Trim SKILL.md, add Claude+Gemini model tiers (#407, 2631c23)

  • skill: Note plugin set-step-state in automation reference (#409, 250f7cc)

Features

  • Add plugin step and image registration options (#409, 250f7cc)

v4.27.1 (2026-06-18)

Bug Fixes

  • metadata: Add --auto-number-format and entities --filter/--managed-only (#373, bc5007d)

Chores

  • Expand PR template to cover all merge gates (#408, b8011b0)

v4.27.0 (2026-06-18)

Features

  • plugin: Register webhook service endpoints and bind steps to them (#406, ef2ec37)

v4.26.1 (2026-06-18)

Bug Fixes

  • docs: Correct phrasing in documentation regarding docs-sync subagent usage (fd653fd)

v4.26.0 (2026-06-18)

Features

  • security: Add --name-contains filter to list-roles, document direct-only scope on list-user-roles (75a4235)

v4.25.1 (2026-06-18)

Bug Fixes

  • view: Add --query-type and --description to view create (#403, e1fa769)

Documentation

  • ship: Fold issue claim + PR-status reflection into the ship skill (#401, 1903d4d)

v4.25.0 (2026-06-18)

Documentation

  • Add AGENTS.md pointing agents to CLAUDE.md (db08c31)

Features

  • records: Round out data-plane flags (--apply, --if-none-match, --mode delete) (#402, ddcd419)

v4.24.0 (2026-06-18)

Documentation

  • Clarify version bump discipline (feat=minor for real features only) (95a829d)

Features

  • metadata: Add --kind customer composite lookup (#400, 124fab3)

Refactoring

  • cli: Unify the output-file flag to --output/-o (#362, 7482496)

v4.23.0 (2026-06-18)

Features

  • security: Add record sharing verbs (grant/revoke/list-access) (#397, 94e5a56)

v4.22.0 (2026-06-18)

Features

  • action: Parameter aliases and @odata.id record-reference params for action function (#396, acece55)

v4.21.0 (2026-06-18)

Features

  • query: Add change tracking via --track-changes/--delta-token (#395, 052ea60)

v4.20.0 (2026-06-18)

Features

  • audit: Retrieve server-side audit change history (#394, 82c063c)

v4.19.0 (2026-06-18)

Features

  • plugin: Consistent --solution across register-assembly/step/image (#393, 14a734b)

v4.18.0 (2026-06-18)

Features

  • form: Add --type/--all to form list to expose form types (#392, 5cf7c4a)

Testing

  • form: Make form list tests encoder- and org-independent (#392, 5cf7c4a)

v4.17.0 (2026-06-18)

Documentation

  • metadata: Clarify datetime default — CLI sends Format, server defaults behavior (#391, a46a059)

Features

  • metadata: Add-attribute --behavior to set DateTimeBehavior (#391, a46a059)

v4.16.1 (2026-06-18)

Bug Fixes

  • query: Default --annotations to True on query odata/fetchxml (#390, 0af693c)

v4.16.0 (2026-06-18)

Features

  • app: Add app remove-components (RemoveAppComponents) (#389, e81967d)

v4.15.0 (2026-06-17)

Features

  • security: Add security user-privileges (RetrieveUserPrivileges) (#388, 17bac1b)

v4.14.0 (2026-06-17)

Features

  • solution: Add standalone apply-upgrade (DeleteAndPromote) (#387, 7648343)

v4.13.0 (2026-06-17)

Features

v4.12.0 (2026-06-17)

Documentation

  • metadata: Document is_bound/return_type/is_composable on list-actions/list-functions (#385, a2ff49c)

Features

  • metadata: Surface is_bound/return_type/is_composable in list-actions/list-functions (#385, a2ff49c)

v4.11.0 (2026-06-17)

Features

  • action: Let action function invoke bound functions (#384, 7949fb8)

v4.10.0 (2026-06-17)

Features

  • query: Add --all/--max-records to follow @odata.nextLink (#383, 5b297e5)

v4.9.0 (2026-06-17)

Features

  • metadata: Create and delete alternate keys (EntityKeyMetadata) (#382, 99da3a6)

v4.8.2 (2026-06-17)

Bug Fixes

  • cli: Emit JSON envelope for form/ribbon export to stdout (#349) (#381, 2832b24)

v4.8.1 (2026-06-17)

Bug Fixes

  • cli: Guard entity disassociate/clear-lookup and workflow deactivate behind the destructive confirm (#380, d9f3c72)

v4.8.0 (2026-06-17)

Bug Fixes

  • import: Gate bulk alt-key enrichment on json mode (when-to-pay) (#379, 1847649)

Continuous Integration

  • Build binaries once, trust PR as the gate (#344, 9d94f10)

Documentation

  • Add section on subagents for code exploration using the Explore agent (ed5b4df)

  • adr: Correct 0010 required-checks list + record PSR admin bypass (d8f9de8)

  • adr: Record build-once, trust-PR CI/CD pipeline decision (0010) (c097b14)

Features

  • import: Surface alternate-key collision hints on bulk data import failures (#379, 1847649)

v4.7.0 (2026-06-16)

Documentation

  • Clarify RequiredLevel string vs null in skill contract (#343, a4bed9d)

Features

  • Expose write/read validity + required level in metadata attributes (#343, a4bed9d)

v4.6.1 (2026-06-16)

Bug Fixes

  • entity: Point --data @file users to --data-file (#342, 79812af)

v4.6.0 (2026-06-16)

Bug Fixes

  • Reject empty --key and stray alt_key; clarify docs (review) (#341, 0101e30)

Documentation

  • Document upsert by alternate key (--key) (#341, 0101e30)

Features

  • Upsert by alternate key (entity upsert --key / data import --key) (#341, 0101e30)

v4.5.2 (2026-06-16)

Bug Fixes

  • Import-side rebind of exported __value lookups (#333, ce27287)

v4.5.1 (2026-06-16)

Bug Fixes

  • Data import reports per-record failures, not just counts (#339, 54712d1)

  • Human-mode import failure line always leads with row index (#339, 54712d1)

v4.5.0 (2026-06-16)

Features

  • form: Add field-editing verbs (add-field/remove-field/set-field) (#338, be7353c)

v4.4.0 (2026-06-15)

Features

  • Add app delete verb that sweeps FK-blocking dependents (#324) (#331, f48e183)

v4.3.0 (2026-06-15)

Documentation

  • Reword package-version note in module docstring (#330, 6bc55db)

Features

  • solution: Check package version ceiling in validate --against-org (#330, 6bc55db)

v4.2.0 (2026-06-15)

Features

v4.1.1 (2026-06-15)

Bug Fixes

  • App create --if-exists skip survives publish-before-read duplicate fault (#328, 6aa21c6)

  • Default max_length for string/memo attributes when omitted (#327, fcf4bc1)

Documentation

  • skill: Add flag-free customization spines + ADR 0009 (#320, 284725f)

v4.1.0 (2026-06-15)

Chores

  • claude: Add docs-sync subagent + edit-guard hooks, wire ship gate (#318, 7b63126)

Documentation

  • docs-sync: Update model from opus to sonnet (f8ca3fd)

  • ship-skill: Add context-discipline guidance for long runs (f4fc41e)

Features

  • output: Concise human single-record render with --full escape hatch (#319, 3894a1c)

v4.0.0 (2026-06-15)

Features

  • output: Curate the --json data contract (ADR 0008) (#317, ae75bb3)

Breaking Changes

  • output: query odata --json rows move from data.value to a bare array in data, and @odata.context/@odata.nextLink leave data for meta. entity delete --json renames id to _entity_id. Single-record --json data has @odata.* stripped. Pre-1.0, permitted.

v3.12.9 (2026-06-15)

Bug Fixes

  • Don't crash profile add when keyring backend panics at the Rust layer (#316, 8fc8281)

v3.12.8 (2026-06-15)

Bug Fixes

  • output: Render solution-component and relationship lists as tables (#315, d7a51fc)

v3.12.7 (2026-06-15)

Bug Fixes

  • query: Count accepts the entity-set name and is case-insensitive (#314, 48a323e)

Documentation

  • cloud-ship-routine: Force Skill-tool invocation + task-tool fallback (6fca0fe)

v3.12.6 (2026-06-15)

Bug Fixes

  • skill: Echo destination directory on uninstall no-op (#313, d2cf6eb)

Testing

  • skill: Assert uninstall exit codes before parsing JSON (#313, d2cf6eb)

v3.12.5 (2026-06-15)

Bug Fixes

  • repl: Ignore an optional leading 'crm' prefix (#312, 620dba2)

v3.12.4 (2026-06-14)

Bug Fixes

  • cli: Clarify --profile help points at profiles/ under CRM_HOME (#311, 6fc5d83)

  • cli: Root help reflects cloud + on-prem; drop hardcoded --profile path (#311, 6fc5d83)

v3.12.3 (2026-06-14)

Bug Fixes

  • cli: Suggest near-miss top-level command names (#310, 3fb1f99)

v3.12.2 (2026-06-14)

Bug Fixes

  • cli: Hint that global flags go before the subcommand (#309, ece8923)

Chores

  • agents: Cloud ship routine (vendored skills + bootstrap + runbook) (8f1ce23)

  • agents: Install gh in cloud routine + branch/merge-gate fixes (b1c3ff7)

  • metadata: Drop unused _admin_header_options/_admin_kwargs imports (20b0668)

Documentation

  • adr: Record CLI output contract + glossary terms (ADR 0008) (e0d366d)

  • claude: Note zsh output-capture traps when driving the CLI (3fcb54e)

v3.12.1 (2026-06-14)

Bug Fixes

  • commands: Clean exit-2 on non-int value:label option input (#296, 2eb4102)

Documentation

  • Purge real test-org GUIDs from published docs (#259, 5ab576a)

  • claude: Note single-test command and pytest e2e default (34c008e)

  • metadata: Align --update-option help with parser error wording (#296, 2eb4102)

Refactoring

  • commands: Composable seams for per-verb boilerplate (#264, aeee16f)

  • commands: Split _helpers grab-bag into a package (#271, 8cdacce)

  • core: Single seam for metadata constraint vocabularies (#295, 3cca964)

  • core: Split solution.py into lifecycle / components / transfer (#265, 8e88986)

v3.12.0 (2026-06-14)

Bug Fixes

  • completion: Quote PS dot-source path; positive-guard the completer loop (#288, a533710)

Features

  • completion: Add PowerShell tab-completion (#288, a533710)

v3.11.0 (2026-06-14)

Documentation

  • skill: Add customization-lifecycle reference + query count example (ac87ece)

Features

  • dry-run: Resolve & report referenced objects on name-taking writes (#286, d2e54aa)

Refactoring

  • Consolidate entity-name resolution into one cache-backed seam (#261, 95e1d84)

  • Deepen the OData response surface on D365Backend (#280, 509f564)

Testing

  • Finish shared-conftest dedup — drop remaining fixture shadows (#285, 4930753)

  • Shared unit-suite conftest + canonical FakeBackend adapter (#283, 2714a06)

v3.10.0 (2026-06-13)

Features

  • Add crm completion command + docs, auto-refresh on self-update (#279, e666861)

v3.9.4 (2026-06-13)

Bug Fixes

  • metadata: Update-relationship writes to un-cast RelationshipDefinitions path (#278, dac4ecc)

v3.9.3 (2026-06-13)

Bug Fixes

  • metadata: Request $metadata CSDL with XML Accept header (#266) (#277, 5974c7c)

Documentation

  • Name agent-on-prem/agent-cloud as the live profiles, document Copilot auto-review (6e67adc)

v3.9.2 (2026-06-13)

Bug Fixes

  • ribbon: Unblock add-button/remove on round-trip solution import (#276, cd9ea28)

Documentation

  • Add worktree-e2e, host-guard, and test-target rules to CLAUDE.md (25a77e1)

v3.9.1 (2026-06-13)

Bug Fixes

  • form: Regenerate internal ids on clone to avoid on-prem collisions (#275, 7477088)

Documentation

  • agents: Document priority + effort label axes (#272, 30f6384)

  • plans: E2e test completeness implementation plan (44a69b4)

  • specs: E2e test completeness design (143aa5d)

  • specs: Harden e2e design after review (3b440fa)

Testing

  • e2e: Complete live coverage + offline enforcement gate (#270, 75f4ae7)

  • e2e: Profile-based target + credential selection for live suite (#274, df08be8)

  • form: Use obvious placeholder GUIDs for external-ref constants (#275, 7477088)

v3.9.0 (2026-06-12)

Features

  • entity: Add entity clone --with-children (#256) (#258, a053a89)

v3.8.0 (2026-06-11)

Features

  • entity: Add entity clone — single-record clone with overrides (#255, dc757d2)

v3.7.0 (2026-06-11)

Bug Fixes

  • entity: Narrow children $select; validate --filter-entities at CLI boundary (#254, 9a9cdd0)

  • entity: Run children counts under --dry-run; validate regex + chunk size (#254, 9a9cdd0)

Documentation

  • entity: Say children uses chunked $batch, not "one batched call" (#254, 9a9cdd0)

Features

  • entity: Add children — per-1:N related-record counts via $batch (#254, 9a9cdd0)

  • entity: Add children — per-1:N related-record counts via $batch (#234) (#254, 9a9cdd0)

Testing

  • e2e: Seed custom solution + xfail bigint kind (#252, d04f32e)

v3.6.1 (2026-06-11)

Performance Improvements

  • Lazy-load the HTTP-transport stack so local commands skip the import tax (d88c754)

v3.6.0 (2026-06-11)

Chores

  • test: Remove unused imports (call, D365Backend) in fetchxml test (#250, 0d70d73)

Features

  • query: Make ENTITY_SET optional on fetchxml — derive from XML (#250, 0d70d73)

  • query: Make ENTITY_SET optional on fetchxml — derive from XML (#202) (#250, 0d70d73)

Refactoring

  • query: Guard empty logical_name in resolver; drop type: ignore in fetchxml cmd (#250, 0d70d73)

Testing

  • query: Strengthen fetchxml tests per round-3 review (#250, 0d70d73)

v3.5.0 (2026-06-11)

Features

  • metadata: Hint did_you_mean on describe 404 for set-name mistakes (#249, ad7ca98)

v3.4.0 (2026-06-11)

Bug Fixes

  • entity: Address Copilot review comments on duplicate-key enrichment (#248, 8aadbe7)

Features

  • metadata: Add metadata keys command + enrich duplicate-key errors (#248, 8aadbe7)

v3.3.0 (2026-06-11)

Features

  • workflow: Accept friendly names for --category on workflow list (#246, d51e87f)

v3.2.0 (2026-06-11)

Bug Fixes

  • emit: Print warnings before error in human mode (#244, f093b4f)

  • entity: Thread validate warnings through create D365Error path; simplify payload check (#244, f093b4f)

  • entity: Tighten human-mode warning test; annotate payload type (#244, f093b4f)

Documentation

  • entity: Clarify validate_payload docstring — 1-3 GETs not always 3 (#244, f093b4f)

Features

  • entity: Warn when create payload contains the primary id (#244, f093b4f)

  • entity: Warn when create payload contains the primary id (#233) (#244, f093b4f)

v3.1.5 (2026-06-11)

Bug Fixes

  • self-update: Fall back to current_version() when frozen install is up-to-date (#245, b809468)

  • self-update: Progress messages, clear version labels, expanded skills output (#245, b809468)

  • self-update: Show progress, fix misleading version label, expand skills output (#245, b809468)

v3.1.4 (2026-06-11)

Bug Fixes

  • query: Widen error message and test matrix to cover all three path forms (#243, 51a1687)

Code Style

  • tests,docs: Replace real-looking GUID with obvious placeholder (#243, 51a1687)

Documentation

  • query: Promote odata path-arg contract to documented, tested behavior (#243, 51a1687)

v3.1.3 (2026-06-11)

Bug Fixes

  • metadata: Picklist command returns options for State/Status attributes (#229) (#242, c82909c)

v3.1.2 (2026-06-11)

Bug Fixes

  • entity: Accept both --no-return and --return-record on create and update (#241, ddfb1a9)

v3.1.1 (2026-06-11)

Bug Fixes

  • metadata: Derive bind_key from referencing nav property, not referenced (#240, e8fdb5e)

Documentation

  • claude: Mandate worktree-per-feature for shared checkout (59a7d97)

v3.1.0 (2026-06-11)

Documentation

  • Record-clone glossary terms + ADR 0007 (no rollback) (#231, 89956a3)

Features

  • solution: Add layer-conflicts to detect unmanaged-layer overlap (#239, 93599ce)

v3.0.0 (2026-06-11)

Documentation

  • out-of-scope: Reject raw http passthrough verb (#235, 512275a)

Features

  • Dry-run previews only mutations; reads execute (#236, fb347fa)

Breaking Changes

  • Read verbs under --dry-run (query odata, entity get, action function, …) now execute the real GET and return live data instead of returning the {_dry_run, method, url, …} request echo. The envelope still carries meta.dry_run: true. The root --dry-run help ("Preview HTTP request without issuing it") was a documented contract, so this is a breaking change.

v2.12.0 (2026-06-11)

Features

  • skill: Default install to claude, confirm-overwrite, refresh on self-update (3f12923)

v2.11.1 (2026-06-11)

Bug Fixes

  • self-update: Suppress upgrade notice after self-update runs (c63890f)

v2.11.0 (2026-06-11)

Build System

  • docs: Cap mkdocs deps below 2.0 to keep strict build reproducible (2b38ee9)

Documentation

  • adr: Record self-update refreshes installed skills (#225, 902a03a)

  • adr: Record stay-on-Material-9x, ProperDocs when forced (#224, f8bf24f)

Features

  • profile: Inline questionary pickers + --client-secret alias (#226, 48d3d06)

v2.10.0 (2026-06-11)

Features

  • self-update: Add crm self-update + passive update notice (#219, 9959404)

v2.9.0 (2026-06-10)

Features

  • workflow: Assess classic workflows for cloud-flow migration (#199, d6c91fc)

v2.8.1 (2026-06-10)

Bug Fixes

  • entity: Rank did_you_mean for numbered field families (#198, 903b2c1)

v2.8.0 (2026-06-10)

Documentation

  • scope: Record environment-admin verbs as out-of-scope (#201, 93e1adc)

Features

  • view: Descending sort in view create --order (#217, 9b8937f)

v2.7.0 (2026-06-10)

Documentation

  • skill: Add crm batch standalone section to records.md (#189, bc4cd98)

  • skill: Add edit-existing-view recipe to authoring.md (#188, 0d1ea0c)

  • skill: Add plug-in trace-log debugging recipe to automation.md (2770f1d)

  • skill: Teach early-bound codegen toolchain + ADR no-wrapper (#194, 2664786)

  • skill: Teach role component-type + managed-import privilege strip (#190, caa0326)

  • skill: Validate-first default + on-prem business-rule deactivate caveat (b6c91a3)

Features

  • solution: Managed-lifecycle verbs clone-as-patch, stage-and-upgrade, uninstall (#216, a42fa32)

v2.6.2 (2026-06-10)

Bug Fixes

  • batch: Parse inner error from failing $batch, reject leading-slash op urls (#212, b42f0dd)

v2.6.1 (2026-06-10)

Bug Fixes

  • Reject OData params in query odata entity-set arg client-side (#185, 056c5b2)

v2.6.0 (2026-06-10)

Features

  • translation: Add translation export/import verbs (#210, c5355e7)

v2.5.0 (2026-06-10)

Documentation

  • skill: Add failed-import investigation recipe to solutions reference (#183, c72f4d4)

  • skill: Add records-verb router to command discovery (#184, 7153e53)

Features

  • plugin: Add step-image registration verbs (#193, f1560d7)

v2.4.4 (2026-06-10)

Bug Fixes

  • solution: Sync ImportSolution fallback so import-result is usable on-prem (#182, 6082d77)

v2.4.3 (2026-06-10)

Bug Fixes

  • solution: Remove-component sends SolutionComponent entity-reference shape (#181, 255fcd5)

v2.4.2 (2026-06-10)

Bug Fixes

  • commands: Wrap _load_payload JSON parse failure as UsageError (#206, 8bc4582)

v2.4.1 (2026-06-10)

Bug Fixes

  • metadata: Get-optionset 400 — drop invalid $expand=Options (#205, 42683e9)

Documentation

  • research: Backlog filed — issue numbers recorded (3d597e3)

  • research: Cleanup confirmation and final criteria check (336a2a4)

  • research: Coverage matrix (Phase 2 complete) (177903e)

  • research: CRM developer scenarios report (Phase 4 report) (ca8a142)

  • research: Draft enhancement backlog for review (2bee836)

  • research: Harvest scenario candidates from five source families (acb5566)

  • research: Ingest prior art into scenario harvest (510fa70)

  • research: Minimal repros for trial-discovered CLI bugs (2777245)

  • research: Scenario catalogue (Phase 1 complete) (d5738f4)

  • research: Skill trial log (Phase 3 complete) (3d18a8c)

  • research: Trial plan and environment readiness (e7dc7b7)

v2.4.0 (2026-06-10)

Documentation

  • copilot: Add custom instructions for Copilot code review (f50598c)

Features

  • sla: Add sla activate orchestrating backing workflows with structured compile-error reporting (#168, 9c010f1)

v2.3.0 (2026-06-10)

Documentation

  • plans: CRM developer scenarios research implementation plan (694cd59)

  • specs: Add CRM developer scenarios research design (68f45fc)

  • workflow: Add duplicate-definition detection recipe (#165, ecde5ce)

Features

  • workflow: Add delete verb resolving activation records to their definition (#164, 7f5d6de)

v2.2.0 (2026-06-10)

Documentation

  • Add ADR 0003 (Web API only, no SOAP) + workflow record glossary (1ab94e1)

Features

  • workflow: Auto-resolve activation-record GUIDs to parent definition (#176, 8d966c7)

v2.1.2 (2026-06-09)

Bug Fixes

  • solution: Validate --against-org detects BPF stage GUID collisions in Workflows XAML (#163, 7059231)

Documentation

  • out-of-scope: Reject plugin step clone-from-existing (#169, f734374)

v2.1.1 (2026-06-09)

Bug Fixes

  • query: Detect unsupported OData 'in' operator, redirect to In function (#174, 7c6b2b3)

Documentation

  • triage: Record solution-clone as out-of-scope (#166, d3e211d)

v2.1.0 (2026-06-09)

Features

  • Entity delete emits deactivate-parent hint on workflow activation-record GUID (#173, 009732c)

  • Entity delete emits deactivate-parent hint on workflow activation-record GUID (#161) (#173, 009732c)

Refactoring

  • Gate workflow import on exact code; fix overpromising delete-hint doc (#173, 009732c)

  • Lazy-import workflow module on entity delete error path (#173, 009732c)

v2.0.2 (2026-06-09)

Bug Fixes

  • Workflow activate/deactivate hint resolves parent draft on activation-record GUID (#160, ada1bb5)

v2.0.1 (2026-06-09)

Bug Fixes

  • Register_step binds sdkmessageprocessingstep lookups with lowercase nav-property names (#159, 768231b)

v2.0.0 (2026-06-09)

Documentation

  • plan: Profile & credential UX revamp implementation plan (ebf862b)

  • spec: Crm update command + startup update nudge design (93b4281)

  • spec: Profile & credential UX revamp design (v2.0.0) (1beca81)

Features

  • Profile-first credential UX — replace init/connect, drop .env/env-var creds (#171, fa0df11)

Breaking Changes

  • crm init and crm connection connect (plus the migrated/removed profiles / disconnect / set-password / delete-password verbs under connection) are removed — use crm profile add|use|list|edit|rm|set-password|delete-password. .env autoload and every D365_* / CRM_* credential env var (and the CRM_AUTH_SCHEME override) are no longer read. CRM_HOME is the only retained env knob.

v1.9.0 (2026-06-09)

Features

  • skill: Split SKILL.md into thin router + on-demand reference files (#158, d1be79a)

v1.8.0 (2026-06-09)

Bug Fixes

  • clone: Consolidate publish_all to single call after all opt-in layers (#157, 737f382)

Documentation

  • Update stale opt-in layer text to include charts (#157, 737f382)

Features

  • metadata: Add --with-charts to clone-entity (#157, 737f382)

  • metadata: Add --with-charts to clone-entity (#153) (#157, 737f382)

v1.7.0 (2026-06-09)

Documentation

  • plans: Add export-spec and cli-robustness implementation plan (473d5a7)

Features

  • Add crm form command group (list/clone/export) (#156, f180386)

v1.6.1 (2026-06-09)

Bug Fixes

  • Export-spec warns on dropped columns; Windows UTF-8 output; metadata-update hint; apply adds referenced option sets (4816f97)

v1.6.0 (2026-06-08)

Chores

  • Add .worktrees/ to .gitignore (2a01d5d)

Features

v1.5.0 (2026-06-08)

Bug Fixes

  • workflow: Address Copilot round-1 findings (#152, a4f9fcd)

  • workflow: Address Copilot round-2 findings (#152, a4f9fcd)

  • workflow: Address Copilot round-3 findings (#152, a4f9fcd)

Documentation

  • plan: Implementation plan for crm workflow clone/export/import (#152, a4f9fcd)

  • spec: Design for crm metadata clone-entity (#152, a4f9fcd)

  • spec: Design for crm workflow clone/export/import (#152, a4f9fcd)

  • spec: Link clone-entity spec to follow-up #151 (#152, a4f9fcd)

  • spec: Sequence #144 before #143; --with-workflows back in scope (#152, a4f9fcd)

  • workflow: Document clone/export/import (#152, a4f9fcd)

Features

  • workflow: Add 'workflow clone' command (#152, a4f9fcd)

  • workflow: Add 'workflow export' and 'workflow import' commands (#152, a4f9fcd)

  • workflow: Add clone, export, and import commands (#144) (#152, a4f9fcd)

  • workflow: Add export_workflow/import_workflow round-trip (#152, a4f9fcd)

  • workflow: Add get_workflow definition retrieval (#152, a4f9fcd)

  • workflow: Add retarget_xaml transform for clone (#152, a4f9fcd)

  • workflow: Clone_workflow_to_entity with tiered category guard (#152, a4f9fcd)

Testing

  • workflow: Align clone with live-org field requirements (#152, a4f9fcd)

v1.4.0 (2026-06-08)

Bug Fixes

  • ribbon: Add dry-run support, fix pyright basic pragma in tests (#150, 2f20ed3)

  • ribbon: Fix remaining pyright errors in test_ribbon.py (#150, 2f20ed3)

  • ribbon: Guard empty slugify, replace assert with if-guard, remove unused code (#150, 2f20ed3)

  • ribbon: Guard None solution before _load_solution_ribbon_diff (#150, 2f20ed3)

  • ribbon: Harden ZIP decode, improve error messages, fix bundler spec (#150, 2f20ed3)

Documentation

  • plan: Implementation plan for crm ribbon command group (#150, 2f20ed3)

  • ribbon: How-to, README capability, SKILL entry (#150, 2f20ed3)

  • spec: Design for crm ribbon command group (#150, 2f20ed3)

  • spec: Verify ribbon decode + group ids against live org (#150, 2f20ed3)

Features

  • ribbon: Add crm ribbon command group (#142) (#150, 2f20ed3)

  • ribbon: Add_custom_action injects button + command nodes (#150, 2f20ed3)

  • ribbon: Apply_ribbon_change export->validate->import->publish (#150, 2f20ed3)

  • ribbon: Customizations.xml entity + RibbonDiffXml navigation (#150, 2f20ed3)

  • ribbon: Decode RetrieveEntityRibbon CompressedEntityXml zip (#150, 2f20ed3)

  • ribbon: Group mapping + deterministic button id helpers (#150, 2f20ed3)

  • ribbon: Parse custom buttons from RibbonDiffXml (#150, 2f20ed3)

  • ribbon: Remove_custom_action drops action + orphaned command (#150, 2f20ed3)

  • ribbon: Retrieve_entity_ribbon via inline-literal RetrieveEntityRibbon (#150, 2f20ed3)

  • ribbon: Ribbon add-button with webresource pre-flight (#150, 2f20ed3)

  • ribbon: Ribbon export command + group registration (#150, 2f20ed3)

  • ribbon: Ribbon list reads custom buttons from solution (#150, 2f20ed3)

  • ribbon: Ribbon remove with destructive confirm (#150, 2f20ed3)

v1.3.0 (2026-06-08)

Chores

  • plan: Add the plan file for issue 140 (a7c26ec)

Documentation

  • spec: Design for crm solution validate (#141, 361510a)

Features

  • solution: Add crm solution validate for offline pre-import checks (#141, 4b1806a)

v1.2.0 (2026-06-08)

Features

  • keyring: Promote keyring to core dependency (3500745)

v1.1.1 (2026-06-08)

Bug Fixes

  • solution: Omit ImportJobId on on-prem; recover server-assigned id from asyncop (2870969)

  • solution: Suppress false Pyright unused-function warning on _import_job_id_rejected (ec37686)

Documentation

  • install: Add uv tool install option for ASR-blocked machines (bed9523)

v1.1.0 (2026-06-07)

Documentation

  • connection: Document 'set-password' for storing profile secrets (#139, 1dc7387)

  • plan: Add set-password (#137) implementation plan (#139, 1dc7387)

Features

  • connection: Add 'set-password' to store a secret for any profile (#139, 1dc7387)

  • connection: Add 'set-password' to store a secret for any profile (#137) (#139, 1dc7387)

v1.0.0 (2026-06-07)

Documentation

  • claude: CHANGELOG is PSR-owned, don't hand-edit (68b3101)

Features

  • release: Graduate to 1.0.0 with configure-once credentials (#130, 7be46b0)

Breaking Changes

  • release: Secrets can now be persisted (opt-in keyring/plaintext); profile resolution restores the session active_profile when no --profile is given. Flips allow_zero_version so PSR cuts v1.0.0.

v0.13.1 (2026-06-06)

Bug Fixes

  • repl: Keep --session sticky across REPL command lines (#128, a53577b)

v0.13.0 (2026-06-06)

Features

  • solution: Add 'solution dependencies' uninstall-blocker read (#116, 27b401f)

[0.12.0] — 2026-06-07

Security

  • Profile and session names are now validated as single path components at creation/load, preventing path traversal in on-disk profile/session/cache paths. (#126)

Fixed

  • crm --profile <missing> now emits the standard {ok:false, ...} error envelope (exit 1) instead of a raw FileNotFoundError traceback (#109).
  • async: list_async_operations / list_all_async_operations now normalize the owner_id GUID filter to canonical form (braced/uppercase/urn inputs were sent to Dataverse verbatim). (#120)

Added

  • metadata export-spec <logical_name> [--with-views] [--with-relationships] [-o FILE] — project a live entity into the crm apply -f desired-state spec (round-trip: export-spec → apply re-creates the entity). Pure GETs. Without -o the spec is emitted under the standard JSON envelope; with -o the bare YAML is written directly to FILE. The projection is always re-appliable: a string column whose live format is Json/RichText (which apply cannot create) is exported without format_name and re-created as Text, and a string/decimal column whose deep read lacks the mandatory max_length/precision (sparse reads) is skipped rather than emitted unappliable (#92).
  • solution import result (real and dry-run) now includes a managed field (true = managed solution, false = unmanaged, null when undeterminable) sniffed from solution.xml inside the zip. The sniff is best-effort and never blocks the import (#91).

[0.11.0] — 2026-06-06

Added - crm scaffold table DISPLAY --column 'DISPLAY:KIND[:opts]' ... — create an entity + N columns in a single publish by building a one-entity in-memory spec and running it through the apply engine. Column shorthand: KIND is one of string, memo, integer, bigint, decimal, double, money, boolean, datetime, picklist, multiselect, lookup, image, file; each resource is created with if_exists=skip (re-running is a no-op); honors global --dry-run and --stage-only; requires a publisher_prefix on the active profile (#90). - Append-only JSONL audit journal of every mutating command. On success, each mutating CLI verb (entity create/update/upsert/delete/associate/disassociate/ set-lookup/clear-lookup; all metadata create/update/delete-*; solution create/ create-publisher/set-version/add-component/remove-component/publish/publish-all/ import/job-cancel; batch; workflow activate/deactivate/run; action invoke; webresource create/update; app create/add-components/build-sitemap/set-sitemap; view create; data import; plugin register-assembly/register-step/ unregister-assembly/unregister-step; security assign-role; async cancel; apply) appends one JSON line to ${CRM_HOME:-~/.crm}/audit/<session>.jsonl. Each line carries: ts (ISO-8601 UTC), profile, command, target, solution, staged, dry_run, ok, and result_id. Read/query/get/list/export verbs never write to the journal. --dry-run previews are journaled with dry_run: true so they are never mistaken for real changes. The request payload is never stored (#89). - crm session audit [--tail N] [--session NAME] prints the current (or a named) session's audit journal; honors --json (#89). - Opt-in, persistent, read-only on-disk cache of entity definitions, per connection profile, to speed up repeated one-shot agent invocations. Enable with --cache-metadata (global flag) or CRM_CACHE_METADATA=1 (truthy: 1/true/yes/on). Force a one-shot refresh with --refresh-metadata. When the cache is active, crm metadata entities emits meta.cache = "hit" | "miss" | "refreshed" in both --json and human output. Cache-mode caveat: with --cache-metadata the command returns only the 2-field rows (LogicalName / EntitySetName); the full 5-field listing is unchanged when the flag is absent. --custom-only is incompatible with --cache-metadata (the cache lacks the custom flag) and errors (exit 2). --top works (client-side slice). Cache files live at <CRM_HOME or ~/.crm>/cache/<profile-name>/entitydefs.json. The cache stores the {logical, set_name} list plus the source url, api_version, and cached_at timestamp; a url/api_version mismatch is treated as a miss. Invalidation: any successful metadata write (entity/attribute/optionset/relationship create/update/delete, and publish-all/publish-xml) deletes the profile's cache file so a stale cache cannot outlive a schema change; a ~15-minute TTL backstop also forces a refresh. Cache misses and read errors degrade gracefully (fall back to a live fetch). Read-only schema only — records and secrets are never cached. When launched with --cache-metadata, the REPL's entity-name completion is served from the same on-disk cache. crm metadata cache-clear deletes the active profile's cache file; emits {"cleared": true|false} (#88). - crm entity get and crm metadata attribute gain a repeatable --expect ATTR=VALUE flag — a field-comparison verify primitive. Each pair is split on the FIRST = (so a VALUE may itself contain =); every pair must match str(record[ATTR]) == VALUE (AND-gate, a missing key never matches). The first mismatch (CLI order) exits 1 with {ok:false, error:"Expectation failed: …", meta:{attr, expected, actual}} (actual is the raw value); all match passes through unchanged as ok:true (exit 0). For entity get the check runs against the full record before any --minimal projection. A malformed --expect (no =, or empty attr) is a usage error (exit 2) raised before any server call. Enables a create→publish→verify loop, e.g. metadata add-attribute … && solution publish-all && metadata attribute <entity> <attr> --expect AttributeType=String (#86). - --minimal on crm query odata / fetchxml / saved / user and crm entity get strips OData annotation keys (any key containing @: @odata.etag, *@OData.Community.Display.V1.FormattedValue, *@Microsoft.Dynamics.CRM.lookuplogicalname) from each record in --json output, keeping business fields, _*_value lookup GUIDs, and the primary id. Shallow prune (top-level record keys only; expanded records under --expand are untouched), and the value-list envelope (@odata.count / @odata.nextLink / @odata.context) is preserved. No-op in human/table mode. Raw output remains the default (non-breaking) — a token-efficient projection for agents (#85). - --retry-on-ambiguous root flag (env: CRM_RETRY_ON_AMBIGUOUS) re-enables auto-retry of non-idempotent POST creates on transport error / 429 / 503, opting back into the duplicate-create risk (#84). - crm solution components <name> --save <path> writes a normalized component inventory (a bare JSON list, each entry {"componenttype": int, "objectid": str, "rootcomponentbehavior": int|null}) to <path>, creating parent dirs as needed. Emits {"saved": "<path>", "count": N}. --diff <expected.json> fetches live components and compares them against the saved file; exits non-zero (1) on drift. Components are keyed on the tuple (componenttype, objectid, rootcomponentbehavior) after normalisation — missing = in expected but not live, unexpected = in live but not expected. The two flags are mutually exclusive; bare components <name> is unchanged. The round-trip --save then --diff against the same org reports no drift (#82). - crm metadata picklist and crm metadata get-optionset now emit a flattened meta.options list ([{value, label}]) in --json mode, so agents need not dig through Label.UserLocalizedLabel.Label. The raw data is unchanged (no contract break). Labels resolve via UserLocalizedLabel then LocalizedLabels. Boolean attributes have no Options array (TrueOption / FalseOption instead), so meta.options is empty for them — read those raw fields directly (#76). - crm security command group: list and assign Dynamics 365 security roles. list-roles lists all security roles (optionally filtered to a business unit via --business-unit GUID). list-user-roles USER_ID and list-team-roles TEAM_ID return the roles currently assigned to a system user or team (both positional args are GUIDs). assign-role ROLE_ID assigns a security role to a user (--to-user GUID) or a team (--to-team GUID) — exactly one target flag is required. Assignment is cumulative and not cleanly reversible, so the command is gated by an interactive confirmation prompt (bypass with --yes) and the destructive-op PreToolUse hook. Standard admin-header options (--as-user, --as-user-object-id, --suppress-dup-detection, --bypass-plugins) are available on assign-role (#83). - crm metadata dependencies <target> is a new read-only command that returns can_delete (bool) plus a blockers[] list for any metadata component. --kind selects the component type (entity / attribute / optionset / relationship; default entity); attribute targets use dotted notation (entity.attribute). --for delete (default) calls RetrieveDependenciesForDelete and lists what would block deletion; --for dependents calls RetrieveDependentComponents and lists what currently depends on the target (#81). - --check-dependencies flag (default off) on metadata delete-entity, delete-attribute, delete-relationship, and delete-optionset: folds can_delete and blockers[] into the result via a pre-delete RetrieveDependenciesForDelete call. Pair with --dry-run for a non-destructive dependency preview without issuing the DELETE (#81).

Changed - Extracted the destructive-verb classification (DESTRUCTIVE, ROLE_VERBS, is_destructive()) into a new dependency-free crm/core/destructive.py. The PreToolUse destructive-op gate (.claude/hooks/destructive_op_gate.py) keeps its standalone stdlib copy so it stays import-free and offline on every Bash call; a new crm/tests/test_destructive_sync.py asserts the two copies stay aligned (#87). - Non-idempotent POST record-creates (and actions) are no longer auto-retried on transport error / 429 / 503 by default — a lost response may have already committed the record, so a blind re-send risks a duplicate (#84). Idempotent verbs (GET/PUT/PATCH/DELETE) are unchanged, and $batch keeps its own independent retry loop. Pass --retry-on-ambiguous (or set CRM_RETRY_ON_AMBIGUOUS) to restore the old behavior when the re-send risk is acceptable.

Fixed - crm metadata delete-entity, delete-attribute, delete-relationship, and delete-optionset under --dry-run now return {"_dry_run": true, "would_delete": true, ...} instead of falsely reporting {"deleted": true, ...} (#81). - crm metadata picklist (human/table mode) now resolves option labels via the full UserLocalizedLabelLocalizedLabels fallback, matching --json meta.options. Previously the table read UserLocalizedLabel only, so an option whose label is carried solely via LocalizedLabels rendered blank (#76).

[0.10.0] — 2026-06-05

Added - crm plugin command group: register and manage Dynamics 365 plug-in assemblies and processing steps via the pluginassemblies / plugintypes / sdkmessageprocessingsteps Web API entity sets. register-assembly PATH uploads a .dll as a plug-in assembly (base64-encoded into content); --update re-uploads the binary of an existing assembly by name without touching its identity metadata. list-types lists platform-generated plug-in types (typename, friendlyname, plugintypeid), optionally filtered by --assembly NAME. register-step registers an sdkmessageprocessingstep bound to a message (--message Create/Update/…) and a fully qualified type (--plugin-type); stage (prevalidation/preoperation/postoperation) and mode (sync/async) are configurable — async forces postoperation (other combinations are rejected). The step name is auto-derived as <typename>: <message> of <entity> (pass --name when the derived string would exceed 256 chars). unregister-assembly ASSEMBLY cascades — it deletes dependent steps first, then the assembly. unregister-step STEP deletes a step by name or GUID; an ambiguous name errors. --solution sends MSCRM.SolutionUniqueName on the writes. --dry-run skips all writes (resolution GETs still fire). --json mode returns the standard {ok, data, meta} envelope (#80). - crm app build-sitemap <SITEMAP_NAME> builds a valid SiteMapXml from structured input and creates the sitemap. Three repeatable options describe the tree: --area 'id[:Title]' (at least one required), --group 'areaId/groupId[:Title]' (nested under an area), and --subarea 'areaId/groupId:entity=<logical>[:Title]', where a SubArea binds a table via the SiteMapXml Entity= attribute (Title optional — omit it and the platform derives the label from the entity). SubArea Ids are auto-allocated from the entity logical name to stay unique across the document, and every attribute value is XML-escaped; Area/Group Ids and the references between them are validated, so broken references or duplicate Ids fail with an error. After building the XML it delegates to the set-sitemap path so the POST body is byte-identical, then optionally publishes. --unique-name sets sitemapnameunique to auto-associate the sitemap with that app (same as set-sitemap); --publish/--no-publish (default: publish) runs PublishAllXml after creation. crm --dry-run app build-sitemap ... prints the generated SiteMapXml and issues no POST. Complements set-sitemap, which uploads a pre-built XML file (#79). - crm webresource create / update / get / list manage web resources (webresourceset). create base64-encodes the --file bytes into the content column and infers the webresourcetype from the file extension (.html=1, .css=2, .js=3, .xml=4, .png=5, .jpg=6, .gif=7, .xap=8, .xsl=9, .ico=10, .svg=11, .resx=12 — the real D365 webresource_webresourcetype option set, so CSS=2 and 8 is Silverlight, not the other way around); --type <int> overrides inference and an unknown extension without it is rejected. --display-name defaults to the name. update <name> resolves the resource by name and issues a plain PATCH of only the sent fields (content from --file and/or --display-name; at least one required) — not retrieve-merge-write. Both honor --solution (sent as MSCRM.SolutionUniqueName) and publish after the write (--no-publish / --stage-only suppress it). list --custom-only keeps unmanaged resources; get <name> prints a record (#78). - crm app create --icon-webresource <name|guid> sets the app icon to a web resource: a GUID is used directly, a name is resolved to its id, and omitting the flag keeps the platform default icon (#78). - crm data import <ENTITY_SET> <INPUT_FILE> bulk-imports records via the Dataverse $batch endpoint — the only on-prem bulk mechanism (CreateMultiple / UpsertMultiple are cloud-only). Supports JSONL and CSV input (format inferred from the file suffix; override with --format). Two modes: --mode create (POST, default) and --mode upsert (PATCH by GUID via --id-column). Records are sent in chunks of --chunk-size (default 100); each chunk is a transactional changeset (atomic, all-or-nothing) by default. --no-transaction sends each operation as a top-level batch operation instead. --continue-on-error sends Prefer: odata.continue-on-error to skip past individual failures — it requires --no-transaction (a changeset is itself all-or-nothing; combining the two is rejected with a usage error). CSV values are coerced best-effort (empty→null, true/false→bool, integers, floats); non-finite tokens (NaN/inf) and integer-looking strings ("007") are not preserved — use JSONL for IDs, postal codes, and lookup binds. Dry-run via the global crm --dry-run data import ... produces zero writes; the summary carries dry_run: true. Output: {imported, failed, chunks, entity_set, mode, dry_run, format}; failed > 0 surfaces a meta.warnings advisory; exit code is 0 on partial failure, consistent with crm batch (#75). - crm connection doctor (also exposed as the top-level alias crm doctor) runs a live, ordered connection probe and renders a five-line checklist: dns_tcp, tls, version (the configured api_version), auth, and an informational rate_limit. Each layer's failure is classified distinctly (DNS vs TCP vs TLS vs wrong api_version vs 401/403) with an actionable hint. It is a read-only diagnostic — it never negotiates or mutates the profile, and the raw GETs run regardless of --dry-run. --json emits {ok, data:{checks:[{check,ok,detail,hint}]}}; overall ok (and the exit code) is the AND of the four diagnostic checks, rate_limit never affects it (#74). - crm solution extract and crm solution pack bridge the CoreTools SolutionPackager.exe to turn an exported solution zip into a source-controllable folder tree and back (git diff on the extracted tree is the solution diff). These are offline local-file transforms: no connection, profile, or backend is required. --package-type selects Unmanaged (default) / Managed / Both; the executable is resolved via --solutionpackager-pathCRM_SOLUTIONPACKAGER env → PATH, and an absent binary fails with an actionable error naming the Microsoft.CrmSdk.CoreTools NuGet package (no bundling or auto-download). The subprocess honors --timeout and the emitted envelope carries {action, exit_code, folder, zipfile, stdout_tail}; a non-zero SolutionPackager exit fails the command (#73). - crm entity create and crm entity update accept an opt-in --validate flag that field-name-checks the payload before the write. It runs 1-3 read-only metadata GETs (resolve entity-set → logical name, the entity's attribute names, and the ManyToOne navigation-property names), then flags any payload key absent from the union with a did_you_mean suggestion. <nav>@odata.bind deep-link keys validate against the nav-name union, so a bound lookup is not a false positive. On a miss the write is blocked with {ok:false, meta:{unknown_fields, did_you_mean}}. Composable with --dry-run (the validation GETs run for real even under dry-run). Scope is field-NAME only; option-set values are not checked (#72). - crm solution add-component and crm solution remove-component add or remove an existing component to/from an unmanaged solution via the AddSolutionComponent / RemoveSolutionComponent Web API actions. --type accepts a componenttype integer or a friendly name (entity, attribute, relationship, optionset, webresource, … — names are case- and separator-insensitive; pass a raw int for any type not in the map). Both pre-flight solution_info and refuse a managed target client-side. add-component is non-destructive and supports --no-add-required (AddRequiredComponents: false) and --no-subcomponents (DoNotIncludeSubcomponents: true). remove-component is gated as a destructive operation: it prompts for confirmation (aborting cleanly in a non-TTY context unless --yes), and the verb-name PreToolUse hook blocks it without --yes (#71). - crm solution import now parses the import job's data column into a solution-level result (success/warning/failure) plus a components list ({name, type, result, errorcode?, errortext?} per imported component). Any non-success component adds a meta.warnings note, so a partial failure under an overall-succeeded async op (status: succeeded) is no longer masked. crm solution import-result <import_job_id> re-fetches a completed job and runs the same parser to verify a prior import without re-importing. Both accept --formatted to also attach the Excel-format RetrieveFormattedImportJobResults report verbatim under formatted_results (opt-in, a separate round-trip) (#70). - crm metadata delete-attribute <entity> <attribute> and crm metadata delete-relationship <schema-name> delete a custom column or a custom relationship (1:N or N:N). Both pre-flight against the metadata to refuse managed and non-custom targets client-side; delete-attribute additionally refuses primary (id/name) and sub-attribute (AttributeOf-set) columns. Each honors --solution (sent as MSCRM.SolutionUniqueName) and is gated as a destructive operation: each prompts for confirmation, aborting cleanly in a non-TTY context unless --yes is passed, and the verb-name PreToolUse hook blocks them without --yes. Remaining-dependency conflicts are left to the server's 4xx (#69). - crm metadata describe <entity> returns a one-shot, read-only write-readiness brief: the entity set name, primary id/name, and every writable attribute with its required level. Lookups carry bind_key (<Nav>@odata.bind, self-derived from ManyToOne relationship metadata) plus targets[] with both the logical name and the EntitySetName so the bind VALUE is usable; picklist / state / status attributes carry inline {value, label} options, and a picklist bound to a global option set also carries its global_optionset_id GUID (which on-prem 9.1 needs to bind on create). Built from pure GETs, gated so only the attribute kinds an entity actually uses cost a round-trip (#68). - crm solution import is now gated as a destructive operation: an overwrite import (the default) clobbers unmanaged customizations in the target org, so it prompts for confirmation and, in a non-TTY context, aborts cleanly (exit 1; under --json the body is the standard {"ok": false, "error": "aborted by user"} envelope, otherwise a human-formatted error) unless --yes is passed. The PreToolUse destructive-op gate also blocks any crm solution import without --yes (verb-only, so a --no-overwrite import is gated too — any import mutates the org). Default import semantics are unchanged (#67). - crm solution set-version <unique_name> updates an unmanaged solution's version / friendlyname / description in place. At least one field is required and --version is validated as 4-part dotted numeric before any HTTP; managed solutions and patches are rejected client-side (the server returns CannotUpdateSolutionPatch for a patch). Delegates to the shared record-update path, so If-Match:* and --dry-run are reused with no new HTTP path (#66). - Non-interactive REPL guard: bare crm (no subcommand) now fails fast with exit 2 and a usage message pointing at crm --help whenever the caller is non-interactive — under --json, with CRM_NO_REPL set (1/true/yes/on), or when stdin is not a TTY (piped/redirected, as agents and CI invoke it). Under --json the message is the standard {ok:false,error} envelope; otherwise it goes to stderr. An interactive human and explicit crm repl still launch the REPL. A proactive isatty probe so an agent invocation never hangs (#65). - Structured warnings channel: the JSON envelope now carries a meta.warnings array — the single place to scan for advisories (staged-but-unpublished, created-but-read-back-failed, partial-optionset). *_lookup_error read-back keys are mirrored into it (and left in data for back-compat), and a non-transactional optionset update that fails mid-stage surfaces meta.completed_steps / meta.failed_stage on the error envelope so a partial mutation is observable. Every other error site is unchanged (#64).

Changed - Breaking (envelope): the singular meta.warning scalar is replaced by the meta.warnings array, so multiple advisories no longer clobber each other (#64).

Fixed - crm metadata update-optionset --dry-run previously returned {"updated": true, ...}, incorrectly signalling a completed write. Under dry-run it now returns {"_dry_run": true, "name": ..., "diff": {...}, "actions": [...]} (#77). The diff classifies each pending change as inserts / updates (with old_label / new_label looked up from the live option set) / deletes (with old_label) / reorder ({old, new} value lists). The live GET fires for real to build the diff; no POSTs are issued.

[0.9.0] — 2026-06-04

Added - crm describe [GROUP]: machine-readable command/option/choice discovery. Walks the live Click tree (no live D365 connection, no mkdocs-click dependency) and in --json mode emits {ok, data:{root_options, commands:[{name, path, help, is_group, args, params}]}}. Each option carries name, opts, secondary_opts, type, required, is_flag, multiple, choices, default, envvar (secondary_opts holds the off-form of a flag-pair, e.g. --no-publish); Choice enums (ownership, the 14 attribute kinds, if-exists, cascade behaviors, …) are surfaced verbatim. Root sticky globals (--json, --dry-run, --profile, --auth-scheme, --log-level, --stage-only, --session, …) are listed under root_options. The interactive repl leaf is excluded. describe <group> scopes to one subtree, importing just that module (a lazy win over the full walk) (#63). - Agent-readable docs surface: the docs site now publishes llms.txt (curated index, per llmstxt.org) and llms-full.txt (every page concatenated into one fetch) at the site root, generated at build time by the mkdocs-llmstxt plugin from the existing docs/ tree. Any agent with web access — not just skill-aware harnesses loading crm/skills/SKILL.md — can pull the full CLI reference and how-tos from <site>/llms-full.txt. Adds a docs-only dependency on mkdocs-llmstxt. - crm apply -f spec.yaml: declarative desired-state from a single YAML/JSON spec. Orchestrates the existing metadata cores in dependency order (publisher → solution → entities → option sets → attributes → relationships → views), each with if_exists=skip, and runs PublishAllXml once at the end, so re-applying an unchanged spec is a no-op. Emits {ok, data:{applied, skipped, planned, failed}, meta:{staged}}. Honors --dry-run (greenfield specs report dependents as planned-create instead of erroring) and --stage-only (create without publishing). Metadata POSTs are non-transactional, so a failure aborts-and-reports, leaving staged-but-unpublished residue. The spec is validated up front. Adds a runtime dependency on PyYAML (#60). - Machine-readable error taxonomy: in --json mode the error envelope now carries meta.category (a closed enum: not_found, auth_failed, forbidden, concurrency_conflict, duplicate_detected, validation, throttled, server_error, transport_error) and meta.retryable, alongside the existing meta.status / meta.code. Classification is status-first, with two D365 error codes (0x80040217not_found, 0x80040237duplicate_detected) honored regardless of status; retryable is true only for the transient classes. The backend auto-retries the transport_error / throttled (429) / server_error (5xx) classes, so for those retryable is a post-exhaustion hint; concurrency_conflict (412) is not auto-retried — the caller refetches a fresh ETag and retries. The status-less transport path now carries a transport_error signal, and the fragile MissingPrivilege message-substring synthesis is subsumed (403 → forbidden) (#62). - Canonical meta.dry_run signal: in --json mode every dry-run invocation now carries meta.dry_run: true in the envelope. It is keyed off the invocation-level --dry-run flag (not by sniffing the data for the _dry_run sentinel), so list-shaped batch previews and poll previews are covered uniformly and forced-real existence-probe GETs do not false-positive. Existing meta keys (e.g. staged) are preserved; the in-data _dry_run sentinel is retained for back-compat (#61).

Changed - Bundled agent skill (crm/skills/SKILL.md) is now fully standalone — it reads correctly once crm skill install drops it into a harness skill dir, with no references to files that don't ship alongside it. Install section shows the per-host one-liners (install.ps1 / install.sh) instead of pip install -e .; removed repo-only pointers (D365.md, crm/tests/TEST.md, README.md, docs/adr/…, docs/how-to/apply.md, the .claude/hooks gate) in favor of in-CLI discovery (crm describe, crm <group> --help); broadened the frontmatter description to cover both on-prem (NTLM) and Dataverse online (OAuth). - Docs examples and test fixtures now use a neutral contoso org/publisher prefix (contoso_, host internalcrm.contoso.local) instead of an internal org name, so the published docs and shipped SKILL.md carry no environment-specific names.

[0.8.0] — 2026-06-04

Added - Installer SHA-256 integrity verification: install.sh / install.ps1 verify the downloaded archive against a published SHA256SUMS (uploaded per release to <tag>/ and latest/ in R2) before extracting, and abort on a mismatch or if it can't be fetched. CRM_SHA256 / $env:CRM_SHA256 pins a hash out-of-band (#46). - Cloud impersonation by Entra ID object id via the CallerObjectId header: new --as-user-object-id <guid> flag (alongside --as-user) and CRM_AS_USER_OBJECT_ID env default, on every command that already carries --as-user. Header selection is by which input you supply, independent of auth_scheme; --as-user (MSCRMCallerID) and --as-user-object-id (CallerObjectId) are mutually exclusive per request (#54). - CHANGELOG is now published on the docs site at /changelog/, rendered from this file via mkdocs-include-markdown-plugin.

0.7.0 — Fast startup + R2 install

Performance - CLI subcommands and the D365 backend stack now load lazily: crm --version and direct command invocations no longer import every command module (and their requests/NTLM/prompt_toolkit dependencies), cutting cold startup substantially. crm --help still loads all modules (accepted trade-off).

Changed - PyInstaller builds switched from --onefile to --onedir (dist/crm/), eliminating per-launch self-extraction overhead. - Install is now a one-line script served from a public Cloudflare R2 bucket (irm …/install.ps1 | iex on Windows, curl …/install.sh | sh on Linux), replacing the private-repo GitHub release URL that 404'd for users.

Added - scripts/install.ps1 (Windows) and scripts/install.sh (Linux): download the prebuilt onedir bundle from R2, install to a user dir, wire up PATH / a symlink, and support uninstall.

0.6.0 — Spec E: DX Polish

Refactor - Split crm/cli.py (2098 lines) into focused modules under crm/commands/ (one Click group per file). Pure refactor — zero behavior change.

Added - --log-level debug|info|warning|error + --log-format text|json-line on the root CLI group (env: CRM_LOG_LEVEL, CRM_LOG_FORMAT). - --verbose flag (alias for --log-level debug). - --auth-scheme ntlm|kerberos|negotiate on the root CLI group (env: CRM_AUTH_SCHEME). Kerberos/Negotiate via requests_negotiate_sspi (install with pip install crm[kerberos]). - crm init command: --template writes .env.example; no args runs an interactive profile wizard. - query count <entity> — calls RetrieveTotalRecordCount. - metadata list-actions — parses $metadata and lists OData actions. - metadata list-functions — parses $metadata and lists OData functions. - REPL tab completion for entity-name argument slots, backed by a lazy in-memory MetadataCache.

Changed - ConnectionProfile gains an auth_scheme field (default "ntlm", backward compatible). - crm/utils/repl_skin.py::create_prompt_session accepts an optional completer argument.

[0.5.0] — 2026-05-25

Added

  • metadata add-attribute — add columns to existing entities. Supports 14 attribute kinds: string, memo, integer, bigint, decimal, double, money, boolean, datetime, picklist, multiselect, lookup, image, file.
  • metadata create-one-to-many + metadata create-many-to-many — create 1:N and N:N relationships via the dedicated Dataverse actions.
  • Global option set CRUD: metadata list-optionsets, get-optionset, create-optionset, update-optionset, delete-optionset. update is granular: --insert-option / --update-option / --delete-option / --reorder flags map to the matching bound actions.
  • metadata delete-entity — drop a custom table, guarded by interactive confirm + --yes skip + client-side IsCustomEntity + IsManaged pre-flight check.

All new write verbs accept --solution <uniquename> (header MSCRM.SolutionUniqueName) and --publish/--no-publish (default ON), matching metadata create-entity. Delete verbs skip publish.

0.4.0 — 2026-05-25

This release lands Spec C from the post-code-review roadmap: $batch support, on-prem-correct impersonation via MSCRMCallerID, two admin headers for write paths, an asyncoperations browse surface, and explicit optimistic concurrency via If-Match. See docs/superpowers/specs/2026-05-24-spec-c-throughput-admin-design.md for the full design.

Added

  • D365Backend.batch(operations, *, transactional=True, continue_on_error=False, timeout=None) — execute a list of operations via POST $batch. Consecutive writes are auto-grouped into one changeset; GETs go as top-level operations.
  • crm batch <file.json> CLI command with --no-transaction, --continue-on-error, --output, --timeout flags.
  • Backend typed kwargs on every verb: caller_id, suppress_duplicate_detection, bypass_custom_plugin_execution, etag. Env defaults: CRM_AS_USER, CRM_SUPPRESS_DUP, CRM_BYPASS_PLUGINS.
  • Per-command CLI flags on every write/action verb: --as-user <guid>, --suppress-dup-detection, --bypass-plugins. --if-match <etag> on entity update and entity delete.
  • crm async list/get/cancel plus crm solution job-status / job-cancel aliases.
  • New TypedDicts: BatchOperation, BatchResult, AsyncOperationRow.

Changed

  • HTTP 412 responses now map to D365Error(code="PreconditionFailed").
  • HTTP 403 responses whose body references prvBypassCustomPluginExecution map to D365Error(code="MissingPrivilege").

Deferred

  • CreateMultiple / UpdateMultiple / UpsertMultiple — Dataverse cloud only; not present on Contoso 9.1.x on-prem.
  • CallerObjectId impersonation header — requires Microsoft Entra ID; on-prem AD users use MSCRMCallerID.
  • Server-side $batch size limits (typical Dataverse: 100 changesets per batch; 1000 ops per changeset) are not enforced client-side; the server's MaxBatchSize / MaxChangesetSize error surfaces verbatim.

Notes for callers

  • POST $batch is retried only on 429 and 503 (Spec B conservative-POST policy). A retried batch re-sends the assembled body verbatim — idempotency is the caller's responsibility.

0.3.0 — 2026-05-24

This release lands Spec B from the post-code-review roadmap: a retry layer on every HTTP call plus a switch to the asynchronous variants of ImportSolution and ExportSolution. See docs/superpowers/specs/2026-05-24-spec-b-resilience-design.md for the full design.

Breaking

  • crm.core.solution.import_solution return shape changes. Now returns {import_job_id, async_operation_id, status, progress, started_on, completed_on, duration_ms}. Any caller reading the old ImportSolution response keys (ImportJobKey, etc.) must switch.
  • crm.core.solution.export_solution return shape gains keys. New fields: async_operation_id, export_job_id, duration_ms. The existing output, bytes, managed, solution keys are preserved.
  • Both functions can now block for up to CRM_ASYNC_TIMEOUT seconds (default 1800). The sync versions blocked for up to profile.timeout seconds per HTTP call (default 120) with no client-side polling.

Added

  • D365Backend.request now retries on 429, idempotent 5xx (502/503/504 on GET/PUT/PATCH/DELETE; 503 only on POST), and retryable transport errors (ConnectionError, Timeout, ChunkedEncodingError). Honors Retry-After; falls back to capped exponential backoff with full jitter.
  • D365Backend.poll_async_operation(async_operation_id, *, timeout, import_job_id, on_progress) — blocks until an asyncoperations(<id>) row reaches statecode=3. Raises D365Error on failure (statuscode=31), cancellation (32), or timeout.
  • ConnectionProfile gains seven new fields: retry_max, retry_base_delay, retry_max_delay, retry_jitter, async_poll_initial, async_poll_max, async_timeout.
  • Env overrides: CRM_RETRY_MAX, CRM_RETRY_BASE_DELAY, CRM_RETRY_MAX_DELAY, CRM_RETRY_JITTER, CRM_ASYNC_TIMEOUT, CRM_NO_RETRY. Env wins over profile.
  • New CLI flags on crm solution export and crm solution import: --timeout N (override async_timeout for this call), --no-retry (set CRM_NO_RETRY=1 for this call). crm solution import also gets --quiet / -q to suppress per-tick progress lines.
  • x-ms-ratelimit-* headers are logged to stderr on every retried 429, and on every response under CRM_VERBOSE=1.

Changed

  • crm solution import and crm solution export now block until the async operation reports completion, emitting per-tick progress to stderr (import only; suppress with --quiet).

0.2.0 — 2026-05-24

This release lands Spec A from the post-code-review roadmap: nine correctness fixes plus pyright strict (zone-scoped) across crm/core/* and crm/utils/d365_backend.py. See docs/superpowers/specs/2026-05-24-spec-a-correctness-pyright-design.md for the full design.

Breaking

  • Error envelope meta.status and meta.code now emit JSON null when absent, instead of the literal string "n/a". Scripts that string-match "n/a" must switch to a null check. (§3.5)

Added

  • --export-setting <name> flag on crm solution export, repeatable. Accepted names: autonumbering, calendar, customizations, email-tracking, general, isv-config, marketing, outlook-sync, relationship-roles, sales. (§3.6)
  • crm/utils/d365_types.pyTypedDict shapes for Web API responses.
  • pyright (>=1.1.380) as a dev dependency and a CI step in .github/workflows/build.yml. Strict mode on crm/core/* + crm/utils/d365_backend.py; basic mode (via file-level # pyright: basic pragma) on crm/cli.py, crm/utils/repl_skin.py, and crm/tests/*.

Changed

  • metadata create-entity now reads EntitySetName back from the server instead of guessing it via English pluralisation. Adds one round-trip per create call. On read-back failure the entity is still reported as created, with entity_set_name: null and a diagnostic entity_set_lookup_error field. (§3.3)
  • REPL keeps a single D365Backend per session instead of rebuilding on every command. Invalidated by connection connect / connection disconnect. (§3.7)
  • $count queries parse text/plain directly in one HTTP call on the happy path. Falls back to ?$count=true if the body is missing or non-numeric. (§3.9)
  • fetchxml_query passes the FetchXML via params= instead of manual URL concatenation. No on-wire change. (§3.4)

Fixed

  • entity create no longer sends the non-spec If-None-Match: null header on POST. (§3.1)
  • data export CSV no longer leaks _value lookup columns and @odata.* annotations into headers — _ordered_keys boolean precedence bug. (§3.2)
  • .env value parser is now pair-aware: KEY="foo's bar" resolves to foo's bar, not foos bar. (§3.8)