Commit 7466db2
Add Calendar / Calculation Group / Function templates + Phase M modernization (2.0.0) (#60)
* Phase 0: TOM 19.114.0 bump + offline golden-file test harness
Foundations for adding new DAX template entities (calendars, calc groups, UDFs)
with regression safety.
- Bump Microsoft.AnalysisServices + AdomdClient 19.86.6 -> 19.114.0.
- Guard the 3 Table.RequestRefresh calls in Engine via RequestTableRefresh: a
disconnected (in-memory) model has no Server and cannot be refreshed (TOM throws),
so skip the request when not connected. Server deployments always run connected,
so behavior is unchanged; this enables offline metadata generation and tests.
- Offline golden-file harness (Dax.Template.Tests):
- OfflineModelFixture builds a synthetic disconnected model (Sales fact + target
measures, Orders table) to drive the real Engine.ApplyTemplates dispatch.
- GoldenFile serializes to BIM, normalizes volatile lineageTag GUIDs + line
endings, and snapshot-compares against _data/Golden (UPDATE_GOLDEN=1 regenerates).
- ApplyTemplatesGoldenTests snapshots the Standard config (holidays, custom date
table + reference, and time-intelligence measures).
- LiveServerFactAttribute: opt-in, skippable live-server test category (env-gated),
never gating CI.
- Copy HolidaysDefinition.json into test _data/Templates (Standard config needs it).
Suite: 7 passed + 1 skipped on net6.0 and net8.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Small config changes for Claude plugins
* Include current state
* Updated my-claude-teams reference
* Updated claude and session_handoff to correctly use agents
* Bumped to 0.5.1 with tool name
* Bump to 0.5.3
* Removed user plugin
* Added my-claude-teams at project level
* Added MCP permissions
* Fix AddHierarchies internal Tabular back-references
AddHierarchies populated Level.TabularLevel with an orphaned object and
never assigned Hierarchy.TabularHierarchy at all: a first loop created a
TabularLevel, stored it on level.TabularLevel, then discarded it, while a
second loop created and added a different instance to the model without
storing it back. The emitted BIM was correct, so the golden-file test could
not catch the defect (it lived purely in the internal back-references).
Collapse to a single loop mirroring the correct AddColumns pattern: create
each TabularHierarchy/TabularLevel once, assign it to the model object's
back-reference, and add that same instance to the model. Preserve ordinal,
names, column binding, IsHidden/DisplayFolder and the compat-level LineageTag
guard, and restore a per-level cancellation check for parity with AddColumns.
Also remove a redundant Description re-assignment in CustomTableTemplate
(value already set in the object initializer; no-op).
Add HierarchyTabularReferenceTests covering the back-reference identity
contract plus ordinal/column-binding and Reset() behavior. Output unchanged
(golden BIM byte-identical); offline suite 13/13 on net6.0 and net8.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add CHANGELOG with Unreleased AddHierarchies fix entry
Introduce a Keep a Changelog-style CHANGELOG.md. Document the AddHierarchies
internal back-reference fix and the new HierarchyTabularReferenceTests under
[Unreleased] (version is build-managed, so no concrete version assigned yet).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Target .NET 10 only with C# 14 and SDK 10
Migrate the whole solution off multi-targeting net6.0;net8.0 to a single
net10.0 target (net10.0-windows for the TestUI), raise LangVersion to 14.0,
and pin the build to .NET SDK 10 (global.json 8.0.400 -> 10.0.301).
- global.json: SDK 8.0.400 -> 10.0.301 (rollForward latestFeature unchanged)
- Dax.Template / Dax.Template.Tests: net6.0;net8.0 -> net10.0, C# 12 -> 14
- Dax.Template.TestUI: net8.0-windows -> net10.0-windows, add LangVersion 14.0
- GitHub Actions ci.yml: install .NET 10.0.x SDK (was 6.0.x)
- Azure pipeline ci.yml: drop orphaned ".NET 6.0 runtime" install step
(SDK comes from useGlobalJson; net6 is EOL)
Verified with SDK 10.0.301: restore/build/pack succeed, offline suite 13/13
on the single net10.0 target, golden BIM snapshot unchanged.
BREAKING: the published Dax.Template NuGet now targets net10.0 only and no
longer supports net6.0/net8.0 consumers. Documented in CHANGELOG [Unreleased].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Bump test tooling and config packages to latest stable for net10
Align lagging NuGet dependencies with the net10.0 / C# 14 / SDK 10 toolchain.
Test project (Dax.Template.Tests):
- Microsoft.NET.Test.Sdk 17.11.1 -> 18.7.0 (major)
- xunit 2.9.2 -> 2.9.3
- xunit.runner.visualstudio 2.8.2 -> 3.1.5 (major; 3.x runs v1/v2/v3 assemblies,
so it remains compatible with xunit 2.9.3)
- coverlet.collector 6.0.2 -> 10.0.1 (major)
TestUI (Dax.Template.TestUI):
- Microsoft.Extensions.Configuration.CommandLine 8.0.0 -> 10.0.9
Note: three coupled major test-tooling bumps land together here (Test.Sdk 18,
runner.visualstudio 3, coverlet 10) -- natural bisection point if a CI test-
runner regression ever surfaces. Verified with SDK 10.0.301: clean restore,
0 build errors, offline suite 13/13 on net10.0 (golden snapshot unchanged).
Shipped Dax.Template package unaffected (test/TestUI-only deps).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add AGENTS.md + docs/design architecture docs
Onboard the repo with agent-facing documentation:
- AGENTS.md (root, tool-agnostic, <200 lines): what the project is,
build/test/single-test commands, local run, architecture mental model,
layout & dependency direction, repo-specific conventions (additive JSON
templates, offline golden-file harness, SQLBI_Template idempotency,
internal Tabular* back-references), a Documentation map, and a
documentation-maintenance rule.
- docs/design/: index + focused design docs (overview, apply-templates
lifecycle with a Mermaid dispatch flow, table generation, measures,
domain model & conventions, testing).
- CLAUDE.md: add @AGENTS.md import alongside the existing experiment-team
delegation policy (policy retained verbatim).
- Dax.Template.sln: add a Solution Items folder referencing the new docs
(verified: dotnet sln list still lists all three projects).
Content verified against source with Serena; reviewer-approved. Root
README.md intentionally left untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Allowlist read-only git --no-pager forms
Add Bash(git --no-pager log|diff|status *) to project permissions.allow to
reduce prompts for read-only inspection commands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Plan Phase M (modernization/refactor) in session handoff
Record the Phase M initiative that precedes Phase 1 (Calendars): modernize to
C# 14 / .NET 10 idioms, uniform style, readability, and safe behavior-preserving
refactors. Captures Stages 0-4 (test-hardening safety net first, then style/
analyzer infra, mechanical modernization sweeps, deeper refactors, docs sync),
the per-subsystem specialist cadence, and the locked decisions:
warnings-as-errors (CI-only), file-scoped namespaces + primary constructors,
required-member migration under a major version bump, public API open to
improvement, and a coverage target (80% core floor / ~90% on refactor targets +
Stryker.NET mutation testing) instead of a flat 100%.
Planning only -- no code/tests/infra changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Integrate dotnet-claude-kit with experiment-team delegation
Adopt the dotnet-claude-kit plugin alongside experiment-team and codify
how the two work together, without weakening existing conventions.
- CLAUDE.md: add "Using dotnet-claude-kit" delegation subsection (lead-only
cross-plugin composition, modern-C# injection into briefs, direct kit-agent
delegation for .NET-idiom work, unchanged mandatory reviewer gate, scope
discipline); rewrite navigation section to cover Serena + cwm-roslyn-navigator
as complementary; document disabled post-edit-format hook and active
pre-bash-guard / post-scaffold-restore hooks.
- AGENTS.md: note Roslyn Navigator MCP availability and C# 14 / .NET 10 baseline,
cross-referencing CLAUDE.md.
- .claude/settings.json: enable dotnet-claude-kit plugin for this project.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Align Phase M and feature phases with dotnet-claude-kit tooling
Add an additive tooling layer to the roadmap mapping work to kit skills,
Roslyn Navigator tools, and kit agents. The five LOCKED Phase M decisions
and all stage/phase scope, targets, and checklists are unchanged.
- New "Phase M — dotnet-claude-kit alignment" subsection: Stage 0-4 -> skills
(testing/tdd, ci-cd, modern-csharp, de-sloppify, error-handling, verify,
security-scan), Roslyn tools (get_public_api, get_test_coverage_map,
get_diagnostics, find_dead_code, detect_antipatterns,
detect_circular_dependencies), and agents (build-error-resolver,
refactor-cleaner, dotnet-architect, code-reviewer, security-auditor), with
scope-out of web/EF/HTTP-oriented material and a de-sloppify dead-code
safety note for the reflection-heavy paths.
- New "Feature phases (1-3) — kit defaults" subsection extends the same
baseline to Calendars/calc-groups/UDFs: modern-csharp + dotnet-architect for
new code, Serena+Roslyn navigation to wire Engine dispatch, get_public_api
API-baseline, tdd/testing (scoped, FakeTimeProvider) + verify self-check,
reviewer supplemented by code-reviewer/security-auditor.
- Generalize the alignment intro (kit is the repo-wide default per CLAUDE.md;
Phase M is its heaviest usage) and relocate the annotated specialist cadence
to close the alignment subsection so it no longer forward-references skills.
- Hard gates kept authoritative (additive JSON, byte-identical golden BIM,
opt-in live-server, mandatory experiment-team:reviewer); kit verify framed
as a pre-reviewer self-check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Flip Phase M to IN PROGRESS (Stage 0 active) in handoff
Make SESSION_HANDOFF.md execution-ready for the next session:
- Resume banner now points at Phase M Stage 0 (test hardening).
- Phase M heading and locked-decisions preamble flipped from PLANNED/under
review to IN EXECUTION with Stage 0 as the active work.
- "Next session" steps updated: add-calendar is pushed (@6bbad5d); Stage 0 is
the active work, using the kit tooling per the alignment subsection.
No change to the five LOCKED decisions or any stage/phase scope.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Moved from experiment-team to dotnet-team
* Revert pre stage 0
* test: add public-API baseline change-detector (Phase M Stage 0 P0)
Reflection-based dump of Dax.Template's public+protected surface,
snapshotted via the existing GoldenFile harness (PublicApi.txt).
Deterministic (stable ordering, culture-invariant); regenerate with
UPDATE_GOLDEN=1. Surfaces intended-vs-accidental public API changes
for review per PR (change-detector, not a freeze gate).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test: add P1 characterization tests (Phase M Stage 0 P1)
Pin current behavior as a pre-refactor safety net: Engine dispatch
(Class->handler, unknown/invalid Class), idempotency (apply-twice +
SQLBI_Template orphan cleanup), dependency ordering (TSort DAG + cycle
detection), reflection paths (ReflectionHelper, GetModelChanges), and
broadened golden coverage (measures-only, holidays-only configs).
Additive configs/goldens only; existing snapshots untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test: add P2 characterization tests (Phase M Stage 0 P2)
Pin StringExtensions helpers, Package load/invalid-config exception
mapping, CustomTableTemplate.GetHierarchies non-date path, MeasuresTemplate
time-intelligence wrapping, determinism, and cancellation honoring.
Also pins two reviewer-surfaced quirks: AutoScan-omitted ->
InvalidMacroReferenceException, and the Holidays handler's phantom-table-
on-validation-throw. Additive configs only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test: raise Measures + Package coverage to clear 80% (Phase M Stage 0)
Targeted top-up on the thin refactor-target subsystems: MeasuresTemplate
86%->100%, MeasureTemplateBase 51%->98%, Package 48%->100% (SaveTo
round-trip + packaged-config load branches). Overall core coverage
75.2%->81.1%, clearing the locked 80% target. Meaningful assertions only,
no coverage padding.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci: activate coverage baseline + 80% floor, add Stryker (Phase M Stage 0 P0)
Restore the missing coverlet.runsettings (Dax.Template-only, Cobertura)
that ci.yml already referenced (CI was broken), record the coverage
baseline in docs/design/coverage.md, and set the CI gate to the locked
80% line-coverage floor (measured 81.1%, ~1.1pt headroom). Add
stryker-config.json (Tables/Measures/dependency-sort) as a non-gating
mutation-testing signal. Add three justified [ExcludeFromCodeCoverage]
attributes on genuinely live-server-only / trivial members
(ModelChanges.PopulatePreview, GetPreviewData; EntityBase.ToString).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: mark Phase M Stage 0 complete, record backlog, tee up Stage 1
Update SESSION_HANDOFF: Stage 0 (test hardening) COMPLETE — suite 13->129
passed + 1 skipped, coverage 81.1% / CI floor 80, public-API baseline +
Stryker. Add the defect backlog surfaced by characterization tests (for
Stage 2/3) and the deferred PublicApiSurface renderer nits. Repoint
"next session" at Stage 1 (style/analyzer infrastructure).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* build: add Directory.Build.props + enable analyzers (Phase M Stage 1A)
Centralize LangVersion 14 / Nullable / EnableNETAnalyzers /
AnalysisLevel=latest-recommended / EnforceCodeStyleInBuild in
src/Directory.Build.props (TargetFramework left per-project so TestUI
stays net10.0-windows; no TreatWarningsAsErrors — WAE is CI-only, wired
later). Remove the now-redundant LangVersion/Nullable from the three
csprojs. Fix the pre-existing CS8602 in TestUI/ApplyDaxTemplate.cs:315
(SelectedItem?.ToString() — turns a latent NRE into the intended
TemplateException). Analyzers now surface 200 warnings (WAE off; build
green) to be triaged/cleaned in Stage 1B/1C and Stage 2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style: dotnet format whitespace/imports baseline — Dax.Template (Stage 1B)
Pure mechanical formatting (whitespace, final newlines, using-directive
ordering) from `dotnet format`. No file-scoped conversion, no using
relocation, no analyzer/style auto-fixes, no logic/token changes. Golden
BIM + PublicApi.txt byte-identical; suite 129 passed + 1 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style: dotnet format whitespace/imports baseline — Tests (Stage 1B)
Pure mechanical formatting (whitespace, final newlines, EOL normalization,
using ordering) from `dotnet format`. No behavior/token changes; goldens +
PublicApi.txt byte-identical; suite 129 passed + 1 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style: dotnet format whitespace/imports baseline — TestUI (Stage 1B)
Pure mechanical formatting from `dotnet format`. No behavior changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci: wire format-verify gate + CI-only warnings-as-errors (Phase M Stage 1C)
Add a `dotnet format --verify-no-changes` gate and CI-only
TreatWarningsAsErrors to both CI pipelines (local dev stays lenient).
A WarningsNotAsErrors allowlist in Directory.Build.props exempts the 17
currently-present analyzer codes as Stage-2-deferred debt (shrinks as
Stage 2 fixes each); compiler (CSxxxx) and any new analyzer code still
break CI. Disable CA1707 for the test project only (idiomatic xUnit
names); set file-scoped namespaces as house style (suggestion; conversion
deferred to Stage 2). Document the policy in AGENTS.md. Config/docs only —
no production code changes; suite 129 passed + 1 skipped, goldens + API
baseline byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: mark Phase M Stage 1 complete, tee up Stage 2
Update SESSION_HANDOFF: Stage 1 (style/analyzer infrastructure) COMPLETE —
Directory.Build.props + analyzers, dotnet format baseline (72 files), CI
format-verify gate + CI-only warnings-as-errors with a shrinking
WarningsNotAsErrors allowlist. Add the Stage 2 analyzer-debt ratchet
(code->work mapping) and repoint "next session" at Stage 2 (mechanical
modernization sweeps, leaf->core).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize leaf subsystems — Constants/Enums/Exceptions (Stage 2.1)
File-scoped namespaces across all 11 leaf files; primary constructors on
the 4 single-ctor exceptions (byte-identical messages/base-calls). Multi-
overload exceptions (TemplateException, InvalidConfiguration/MacroReference)
left explicit — a primary ctor would corrupt their distinct base-call
messages. No public API change (PublicApi.txt byte-identical), no const/
enum value change, goldens byte-identical, suite 129 passed + 1 skipped,
CI warnings-as-errors simulation clean. UTF-8 BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Extensions + tighten WAE allowlist (Stage 2.2)
File-scoped namespaces across the 6 Extensions files; CA1510 (ThrowIfNull
×2 in ReflectionHelper), CA1860 + CA1868 (TSort), 4 dead usings removed,
expression-bodied StringExtensions helpers. TSort algorithm/recursion/
cycle-detection untouched (pinned by characterization tests). Remove
CA1510 and CA1868 from the Directory.Build.props WarningsNotAsErrors
allowlist — both now fully eliminated repo-wide (verified: WAE build green
with them enforced). No public API change (PublicApi.txt byte-identical),
goldens byte-identical, suite 129 passed + 1 skipped, BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Model + tighten WAE allowlist (Stage 2.3)
File-scoped namespaces across the 7 Model files; CA1805 ×6 (Column/
Hierarchy/Measure redundant init), CA1860 ×4 (ModelChanges), collection
expressions, expression-bodied members. ModelChanges.cs treated
conservatively — only file-scoped + the 4 CA1860 rewrites (each verified
logically identical on the reflection/diff path the offline suite can't
exercise); reflection logic, [ExcludeFromCodeCoverage] attrs, and the
`= default!` members (EntityBase.Name, Level.Column — Stage 2.9 required
targets) left untouched. Remove CA1860 from the WarningsNotAsErrors
allowlist (fully eliminated repo-wide; WAE build green with it enforced).
No public API change, goldens byte-identical, suite 129 passed + 1
skipped, BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Syntax subsystem (Stage 2.4)
File-scoped namespaces across the 11 Syntax files; CA1805 ×4 (redundant
= false init in DaxElement/Var/VarGlobal), CS8019 ×2 unused usings,
expression-bodied members (DaxStep/Var/DaxElement debug/ToString, Var-
Global/VarRow ctors). Dependency-ordering logic (Dependencies/Expression/
IDependencies) untouched — DependencySortCharacterizationTests unchanged.
Meaningful = true initializers left intact. No public API change
(PublicApi.txt byte-identical), goldens byte-identical, suite 129 passed
+ 1 skipped, BOM preserved. CA1805 stays allowlisted (still present in
Tables/CustomTemplateDefinition).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Measures subsystem (Stage 2.5)
File-scoped namespaces; CA1805 ×2 (MeasuresTemplate); collection
expressions ([] for Array.Empty/new List/new); expression-bodied members;
removed a dead unused local (Union result never read) and a redundant
identity LINQ projection in FindTable. Emitted DAX byte-identical
(Config-01/02/03 goldens unchanged); raw-string + [GeneratedRegex]
conversions deliberately skipped to protect the golden gate. No public
API change (PublicApi.txt byte-identical), suite 129 passed + 1 skipped,
BOM preserved. CA1805 stays allowlisted (still in Tables/CustomTemplate-
Definition).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Tables non-date base classes (Stage 2.6a)
File-scoped namespaces across the 5 non-date Tables files; CA1805 ×3
(TableTemplateBase), CA1874 (CalculatedTableTemplateBase Regex.IsMatch),
CS8019 ×1, collection expressions, is null/is not null (audited: no type
in the codebase overloads ==, so all conversions are reference-equality-
identical), expression-bodied members. Calculated-table DAX byte-identical
(goldens unchanged); AddHierarchies Description defect deliberately left
untouched (pinned by characterization test). Kept columnDependencies as an
explicit DaxStep[] to preserve exact runtime array type. Deferred CA1051/
CA1707/CA1716 untouched. No public API change, suite 129 passed + 1
skipped, BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Tables date branch + tighten WAE allowlist (Stage 2.6b)
File-scoped namespaces across the 6 Tables/Dates files; CA1874 ×5
(BaseDateTemplate Regex.IsMatch), CA1805 ×8 (HolidaysDefinitionTable),
collection expressions, expression-bodied members, inlined out-vars
(TryGetValue always-assigns). Emitted date/holidays DAX byte-identical
(every @"..."/$@"..." literal untouched; goldens unchanged). Collection
expressions applied only where the declared type was already identical
(no covariance drift). CA1305/CA1309 (culture formatting in date code)
deliberately left untouched — deferred correctness decision. Remove CA1874
from the WarningsNotAsErrors allowlist (fully eliminated repo-wide; WAE
build green with it enforced). No public API change, suite 129 passed + 1
skipped, BOM preserved. Completes the Tables subsystem.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Interfaces subsystem (Stage 2.7a)
File-scoped namespaces across the 7 interface files; CA1805 ×1
(ITemplates.TemplateEntry.IsHidden redundant = false). Public interface +
TemplateEntry POCO shapes unchanged (JSON-deserialization back-compatible).
No public API change (PublicApi.txt byte-identical), goldens byte-identical,
suite 129 passed + 1 skipped, BOM preserved. CA1805 stays allowlisted
(5 sites remain in CustomTemplateDefinition/Translations — next sweep).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize Engine/Package/root + tighten WAE allowlist (Stage 2.7b)
File-scoped namespaces (Engine/Package/CustomTemplateDefinition/Translations);
CA1805 ×5 (CustomTemplateDefinition ×4, Translations ×1), collection
expressions, foreach over the Templates array (dropped a redundant ToList()
allocation — Templates is a fixed array, no mutation during iteration),
dropped unused locals/tuple element, expression-bodied FindTemplateFiles,
removed dead usings. Engine dispatch throw-semantics, GetModelChanges
reflection, and Package LoadFromFile/SaveTo/ReadDefinition exception types
untouched (pinned characterization tests green). Remove CA1805 from the
WarningsNotAsErrors allowlist — fully eliminated repo-wide (WAE build green
with it enforced). No public API change, goldens byte-identical, suite 129
passed + 1 skipped, BOM preserved. Completes the Dax.Template library
modernization.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: modernize TestUI + tighten WAE allowlist (Stage 2.8)
File-scoped namespaces across the 3 hand-written TestUI files; CA2263 ×5
(generic JsonSerializer.Deserialize<T>), CA1869 ×3 (hoisted two static
readonly JsonSerializerOptions, settings identical + correctly wired,
mutation-safe). Conservative sweep — TestUI has no automated coverage, so
mechanical/file-scoped only, no logic/UI change; Designer.cs untouched;
CS8602 fix intact; per-file BOM state preserved. Remove CA2263 from the
WarningsNotAsErrors allowlist (fully eliminated repo-wide; WAE build green
with it enforced). Goldens + PublicApi.txt byte-identical, suite 129 passed
+ 1 skipped. Completes Stage 2 non-breaking modernization.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: clear bucket-C analyzer debt + tighten WAE allowlist (Stage 2)
Non-breaking fixes for the last 4 mechanical allowlist codes: CA1852 ×2
(seal internal test helpers), CA1816 (GC.SuppressFinalize in
PackageSaveToCharacterizationTests.Dispose), CA1859 (ModelChanges.
GetPreviewData private return object?->List<object>?, exact widening,
caller unaffected), CA1869 (Package.SaveTo JsonSerializerOptions hoisted to
static readonly, settings identical -> SaveTo JSON byte-identical). Remove
CA1816/CA1852/CA1859/CA1869 from the WarningsNotAsErrors allowlist — all
fully eliminated repo-wide (WAE build green with them enforced). No public
API change (PublicApi.txt byte-identical), goldens byte-identical, suite
129 passed + 1 skipped, BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat!: required members migration + major version bump to 2.0.0 (Stage 2.10a)
BREAKING: convert 4 `= default!` members to `required` — EntityBase.Name
(and subclasses Column/DateColumn/Hierarchy/Level/Measure), Level.Column,
Var.Name (and VarGlobal/VarRow), DaxStep.Name. Source-breaking for
consumers constructing these via object initializers; behavior-preserving
at runtime; JSON template config unaffected (none of these types are
JSON-deserialized — verified). All existing construction sites already set
the members (0 CS9035). Bump Dax.Template to 2.0.0 (Assembly/File/Version-
Prefix). Enhance PublicApiSurface to detect RequiredMemberAttribute and
regenerate the PublicApi.txt baseline (the 4 members now render `required`).
CHANGELOG updated. Golden BIM byte-identical, suite 129 passed + 1 skipped,
WAE build green.
NOTE: maintainer must bump the Azure DevOps `AppVersionMajor` pipeline
variable to 2 (ADO variables can't be changed from the repo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor!: public API naming renames + empty the rename allowlist (Stage 2.10b)
BREAKING (2.0.0): rename public identifiers flagged by CA1707/CA1711/
CA1716/CA1725 — de-underscore 18 public constants (values unchanged, e.g.
SQLBI_TEMPLATE_ATTRIBUTE -> SqlbiTemplate = "SQLBI_Template"); drop the
Enum suffix on 5 enum types (AutoScanEnum->AutoScan, AutoNamingEnum->
AutoNaming, SubstituteEnum->Substitute, TestUI WeeklyType/QuarterWeekType,
DayOfWeekEnum->WeekDay to avoid System.DayOfWeek); rename reserved-keyword
identifiers (param template->templateDefinition, nested type Step->
TemplateStep); align BaseDateTemplate.ApplyTemplate param dateTable->
tabularTable with the base. Enum source files renamed to match. All
identifier-only: constant values, enum member values, and JSON property
names unchanged -> emitted BIM byte-identical, existing template JSON still
loads. PublicApi.txt regenerated (renames only); CHANGELOG updated. Remove
CA1707/CA1711/CA1716/CA1725 from the WarningsNotAsErrors allowlist — all
fully eliminated repo-wide (WAE build green with them enforced). Allowlist
now holds only CA1051 (deferred) + CA1305/CA1309 (Stage 3). Suite 129
passed + 1 skipped, BOM preserved. Completes the option-2 breaking pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: sync design docs + test comments to 2.10b renames
Update stale identifier references in docs/design/domain-model-and-
conventions.md and measures.md (AutoScanEnum->AutoScan, AutoNamingEnum->
AutoNaming, SQLBI_TEMPLATE_ATTRIBUTE->SqlbiTemplate, CONFLICT_RENAME_PREFIX
->ConflictRenamePrefix) and comments in MeasureTemplateBaseCharacterization
Tests (ENTITY_*->Entity*). Identifier references only — string values
("SQLBI_Template", "_old") unchanged; CHANGELOG's rename mapping left as
historical record. Build + suite unaffected (129 passed + 1 skipped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: mark Phase M Stage 2 complete, tee up Stage 3
Record Stage 2 outcomes: 10 modernization sweeps + bucket-C, WAE allowlist
ratcheted 17->3 (CA1051/CA1305/CA1309 remaining), and the required/2.0.0
API-breaking pass (2.10a/2.10b). Note the deferred CA1051 field->property
follow-up and the ADO AppVersionMajor bump. Repoint "next session" at
Stage 3 (deeper refactors + Stage 0 defect-backlog fixes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor!: CA1051 protected fields -> properties + shrink allowlist (Stage 2 tail)
BREAKING (2.0.0): convert the 4 remaining visible protected instance fields
to properties — MeasureTemplateBase.Template (get-only, ctor-assigned,
readonly semantics preserved), TableTemplateBase.FixRelationshipsTo/From
(get/set), Translations.LanguageDefinitions (get/set, ctor-assigned).
Source/binary-breaking for subclasses; no runtime/DAX-BIM/JSON impact
(these hold template-build state, none are JSON-deserialized; verified no
ref/out usage). PublicApi.txt regenerated (only the 4 field->property
changes); CHANGELOG updated. Remove CA1051 from the WarningsNotAsErrors
allowlist — fully eliminated repo-wide (WAE build green with it enforced).
Allowlist now holds only CA1305/CA1309 (Stage 3 culture decision). Golden
BIM byte-identical, suite 129 passed + 1 skipped, BOM preserved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: mark CA1051 follow-up done in handoff (Stage 2 fully closed)
Record that the CA1051 field->property follow-up completed (9ead9ae); the
2.0.0 public-API cleanup is complete and the WAE allowlist now holds only
CA1305/CA1309 (Stage 3 culture decision). Stage 3 remains the active,
opt-in-per-item stage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: dedup annotation upsert into shared extension (Stage 3 item 1)
Extract the duplicated annotation-upsert loop (closing the long-standing
TODO in TableTemplateBase) into a new internal extension
AnnotationCollectionExtensions.UpsertAnnotations, generic over TOM's
NamedMetadataObjectCollection<Annotation, TOwner> base. TableTemplateBase
.AddAnnotations keeps its protected virtual seam and delegates (retaining
its upfront cancellation throw); MeasureTemplateBase's local ApplyAnnotations
is removed in favor of the shared helper.
Behavior-preserving: build green, offline suite 129 passed + 1 skipped,
golden BIM and PublicApi.txt byte-identical. Reviewed (code-reviewer: GO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor!: exceptions naming consistency (Stage 3 item 4)
Fix the 'Circulare'->'Circular' typo in CircularDependencyException and
rename misleading public ctor params: daxExpressionmessage->daxExpression
(CircularDependency, InvalidVariableReference, InvalidMacroReference x3)
and entitymessage->entityName (InvalidAttributeException). These name a DAX
expression / entity, not a 'message'.
Public ctor param renames -> PublicApi.txt regenerated deliberately (diff
limited to the 6 renamed lines); ships under the in-progress 2.0.0. All
in-repo throw sites use positional args, so no internal breakage. Golden BIM
byte-identical; offline suite 129 passed + 1 skipped. Reviewed (code-reviewer: GO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: XML summaries for undocumented Syntax types (Stage 3 item 3)
Add /// summaries to the 9 previously-undocumented public/internal Syntax
types (DaxBase, Var, VarGlobal, VarRow, VarScope, IDependencies<T>, IDaxName,
IDaxComment, IGlobalScope), matching the existing DaxElement/DaxStep tone.
The subsystem is already modern (Stage 2.4) and holds only POCOs/interfaces,
so documentation was the genuine readability win rather than a refactor.
Doc-comment-only: no code/signature changes, build green (no CS1570), offline
suite 129 passed + 1 skipped, golden BIM and PublicApi.txt byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor!: encapsulate TOM reflection + document fragility (Stage 3 item 2)
Make ReflectionHelper and its GetPropertyValue/SetPropertyValue extensions
internal (were public; tests reach them via InternalsVisibleTo) -> PublicApi.txt
loses those 3 lines; ships under the in-progress 2.0.0. SetPropertyValue kept
(no prod caller yet; reserved for Phase 1 Calendar internal-member writes).
Centralize the 6 reflected TOM-internal member names (TxManager, CurrentSavepoint,
AllBodies, Owner, LastParent, Parent) as documented consts in a private nested
TomInternalMembers class, extract the TxManager->AllBodies traversal into
GetChangedBodies (exact null-propagation + hard (IEnumerable) cast preserved),
modernize string.Format->interpolation, and document the TOM-version fragility
plus the offline-apply-returns-empty behavior on GetModelChanges.
Behavior-preserving: whole-solution build green, offline suite 129 passed +
1 skipped, golden BIM byte-identical. Reviewed (code-reviewer: GO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: harden two template error paths (Stage 3 Group B: B1+B2)
B1 - Engine.ApplyHolidaysDefinitionTable validated an empty Template only
AFTER adding the table, leaving a phantom empty table in the model on throw.
Hoist the validation above the create/add block so it fails before any model
mutation (matching ApplyCustomDateTable/ApplyMeasuresTemplate).
B2 - CustomTableTemplate.GetHierarchies used Columns.First(...) for a level's
column, throwing a bare InvalidOperationException on an unknown column. Use
FirstOrDefault + a TemplateException naming the hierarchy, level, and column.
Both are error-path fixes: the two characterization tests that pinned the buggy
behavior are converted to fix-tests. Golden BIM + PublicApi.txt byte-identical,
offline suite 129 passed + 1 skipped. Reviewed (code-reviewer: GO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: copy Hierarchy/Level Description into TOM objects (Stage 3 Group B: B3)
AddHierarchies built TOM Hierarchy/Level objects without copying Description,
silently dropping descriptions supplied in template JSON (the source
Model.Hierarchy/Level.Description are populated by GetHierarchies). Add the
Description assignment to both initializers, mirroring the existing AddColumns
pattern. Converted the characterization test that pinned the drop into a
fix-test.
Latent-correctness fix: no shipped config carries hierarchy/level descriptions
today, so golden BIM + PublicApi.txt are byte-identical; offline suite 129
passed + 1 skipped. Reviewed (code-reviewer: GO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: detect multi-node dependency cycles promptly (Stage 3 Group B: B4)
TSort.VisitDependencies only caught direct self-cycles immediately; a 2+ node
mutual cycle (A->B->A) was caught only after the crude nestedCalls > 1000
backstop, throwing a generic {STACK OVERFLOW} message. Replace both the
nestedCalls/MAX_NESTED_CALLS guard and the self-only Contains(item) check with
proper DFS recursion-path tracking (HashSet path + try/finally backtrack), so
self-, 2-node, and N-node cycles all throw CircularDependencyException promptly
with the real offending node's expression. Remove the now-unused MAX_NESTED_CALLS.
Valid acyclic graphs sort identically (backtrack preserves the level arithmetic
and diamond re-joins), so golden BIM + PublicApi.txt stay byte-identical; offline
suite 129 passed + 1 skipped. Converted the characterization test that pinned the
nested-guard behavior into a prompt-detection fix-test. Reviewed (code-reviewer:
GO-WITH-NITS; the tautological assertion nit was fixed).
Note: removing the soft 1000-depth backstop means a valid but pathologically deep
acyclic graph now overflows the CLR stack (uncatchable) instead of throwing; no
current config approaches this.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: remove CustomDateTable output when disabled (Stage 3 Group B: B5)
A disabled CustomDateTable entry returned early without removing a
previously-created date table, asymmetric with the Holidays handlers that
clean up on disable. Remove the date table (and its ReferenceTable, if set)
in the disabled branch; orphan relationships are swept centrally by the
existing post-pass, so this is safe and consistent with precedent.
Converted the characterization test that pinned the no-cleanup behavior into
a fix-test, added a Dispatch-08 fixture + test covering the ReferenceTable
removal branch, and corrected two now-stale comments. Golden BIM + PublicApi.txt
byte-identical; offline suite 130 passed + 1 skipped (+1 new test). Reviewed
(code-reviewer: GO-WITH-NITS; the coverage + comment nits are addressed here).
Completes Phase M Stage 3 Group B (all five Stage-0 defect-backlog items fixed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: invariant-culture DAX formatting; empty WAE allowlist (Stage 3 culture)
Make all year-int -> DAX formatting culture-invariant so generated DAX is
locale-independent: int.ToString() -> ToString(CultureInfo.InvariantCulture)
and interpolated int holes wrapped in FormattableString.Invariant(...) across
GenerateMinYearExpression / GenerateMaxYearExpression / GenerateCalendarExpression
(CALENDARAUTO fallback). DAX text (incl. trailing spaces) byte-preserved.
Make MeasuresTemplate's measure-name match explicitly StringComparison.Ordinal
(was already ordinal via the one-arg overload).
Removes CA1305 and CA1309 -- the last two codes -- from the CI warnings-as-errors
allowlist in Directory.Build.props, which is now empty: the Stage-2 analyzer debt
is fully cleared and any new analyzer/compiler warning fails CI.
Behavior-preserving on Latin-digit cultures: golden BIM + PublicApi.txt
byte-identical, offline suite 130 passed + 1 skipped, solution build green with
-p:TreatWarningsAsErrors=true (0 warnings repo-wide). Reviewed (code-reviewer:
GO-WITH-NITS; the CALENDARAUTO-fallback consistency nit is addressed here).
Completes Phase M Stage 3 (deeper refactors).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: sync design docs, CHANGELOG, AGENTS for Stage 3 (Phase M Stage 4 closeout)
Document all Phase M Stage 3 changes and close out Phase M:
- CHANGELOG [Unreleased]/2.0.0: exception ctor param renames (daxExpression/
entityName) + Circular typo; ReflectionHelper -> internal; the five Group B
behavior fixes (Holidays phantom table, GetHierarchies TemplateException,
Hierarchy/Level Description copy, prompt multi-node cycle detection,
CustomDateTable disable cleanup); invariant-culture DAX year formatting.
- AGENTS.md: WarningsNotAsErrors allowlist now empty; CA1707 production-clean.
- .editorconfig: correct the stale CA1707 'deferred to Stage 2' comment.
- docs/design: TSort DFS cycle detection, dispatch disable/validation behavior,
GetModelChanges offline note, Description copy-through, invariant year
formatting, Syntax XML docs; coverage.md dated Stage-3 suite-count note.
Reviewed (code-reviewer: GO-WITH-NITS; all claims verified against source, the
coverage.md wording nit fixed). Completes Phase M (Stages 0-4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(handoff): resolve Calendar column-binding open question (public TOM API)
Investigation (reflection over TOM 19.114.0 + cross-check against Tabular
Editor 2) shows the Calendar binding has a fully PUBLIC typed API in our pinned
19.114.0 via the concrete subclasses TimeUnitColumnAssociation /
TimeRelatedColumnGroup (public ctors, public TimeUnit/PrimaryColumn/
AssociatedColumns/Columns). The earlier RISK looked at the abstract base
CalendarColumnGroup and its internal low-level members and missed the subclasses.
Phase 1 therefore needs NO reflection and NO TMSL/JSON injection. Remaining
constraints recorded: min compatibility level 1701, and Calendar has no
Annotations (idempotency must key off the parent table's SQLBI_Template
annotation). Updates the RISK section, Open Question #1, and the TOM object-model
note in SESSION_HANDOFF.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(handoff): point resume instructions at Phase 1 (Calendars)
Phase M (Stages 0-4) is complete; update the top-of-file resume line from the
stale 'start Phase M Stage 3' to 'start Phase 1 (Calendars)' with the resolved
public-API binding note, and bump Last updated to 2026-07-04.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(calendar): add CalendarTemplate for native TOM Calendar attachment (Phase 1)
Add a new template Class "CalendarTemplate" that attaches a native TOM
Calendar (with column groups) to an existing table, using the public typed
API (TimeUnitColumnAssociation / TimeRelatedColumnGroup) — no reflection,
no TMSL. JSON config is purely additive (reuses Class/Table/Template/
IsEnabled; no TemplateEntry change).
Backend:
- CalendarTemplateDefinition POCO + nested CalendarColumnGroupDefinition
(Tables/Calendars/).
- CalendarTemplate.ApplyTemplate: find-or-create the Calendar keyed by
Calendar.Name (Calendar has no Annotations, so SQLBI_Template can't live
on it); re-apply clears+rebuilds column groups; IsEnabled=false removes
the named calendar.
- Engine dispatch wired via nameof(CalendarTemplate). Disabled entry with a
missing target table is a safe no-op, matching sibling handlers; enabled
entry with a missing table throws TemplateException.
- Compatibility guard: TOM enforces level >= 1701 at Table.Calendars.Add();
the handler pre-checks and throws InvalidConfigurationException (also
guards Model?/Database null on the public method).
Tests:
- Separate compat-1701 CalendarOfflineModelFixture (shared 1600 fixture
untouched, existing goldens byte-identical).
- CalendarGoldenTests: shape, snapshot (Config-02 - Calendar.bim),
idempotency, disabled-removal, disabled-with-missing-table regression,
and opt-in live-server. New template fixtures added.
- PublicApi.txt regenerated (diff limited to the 3 new Calendar types).
- Suite: 135 passed + 2 skipped; build green under warnings-as-errors;
dotnet format clean.
Known limitation (documented, deferred): deleting/renaming a CalendarTemplate
entry orphans its calendar — matches the existing CustomDateTable rename-TODO
and MeasuresTemplate entry-deletion precedent.
Docs: CHANGELOG, AGENTS, apply-templates-lifecycle, table-generation,
SESSION_HANDOFF updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(calcgroup): add CalculationGroupTemplate for generic calc-group generation (Phase 2)
Add a new template Class "CalculationGroupTemplate" that generates a native
TOM calculation group (a Table whose Table.CalculationGroup holds
CalculationItems). Generic generator — the JSON authors any calc group; it
has NO dependency on the Measures/Syntax/time-intelligence machinery (calc
groups are being deprecated for time intelligence). JSON config is purely
additive (reuses Class/Table/Template/IsHidden/IsEnabled; no TemplateEntry
change).
Backend:
- CalculationGroupTemplateDefinition POCO + nested CalculationItemDefinition
(Tables/CalculationGroups/); CalculationGroupExpression selection
expressions (MultipleOrEmptySelection / NoSelection, each with an optional
format-string expression) supported from day 1.
- CalculationGroupTemplate.ApplyTemplate validates the whole definition
before mutating (non-empty items; per-item name + expression; non-empty
ColumnName; ordinal uniqueness), then stamps the table ownership
annotation, builds/reuses the CalculationGroup (Precedence/Description +
selection expressions), ensures one String backing column + a
CalculationGroupSource partition, and reconciles calc items
full-replace-by-name (orphans removed).
- Engine dispatch wired via nameof(CalculationGroupTemplate). Disabled entry
removes the table (missing table = safe no-op). Foreign-table guard: a
pre-existing table lacking the SQLBI_Template="CalculationGroup" annotation
throws TemplateException rather than being overwritten. Validate-before-
mutate + build-then-add => no phantom table on invalid input. Backing
column receives a LineageTag at compat >= 1540. No RequestTableRefresh.
- New const Attributes.SqlbiTemplateTableCalculationGroup = "CalculationGroup".
Compatibility (enforced by TOM at assignment): calc group >= 1470,
calc items >= 1500, selection expressions >= 1605.
Idempotency: keyed on the calc-group table's SQLBI_Template annotation
(the table has Annotations, unlike a Calendar). Re-apply reconciles items and
clears omitted selection expressions.
Known limitation (documented, deferred): renaming ColumnName orphans the old
backing column — matches the Calendar / CustomDateTable rename precedent.
Tests:
- Dedicated compat-1605 CalcGroupOfflineModelFixture (shared 1600 + Calendar
1701 fixtures untouched, existing goldens byte-identical).
- CalculationGroupGoldenTests: shape, snapshot (Config-03), idempotency,
orphan-item removal, selection-expression clear-on-empty, disabled-removal,
disabled-with-missing-table, foreign-table collision, ordinal uniqueness,
and opt-in live-server.
- PublicApi.txt regenerated (diff limited to the new types + the new const).
- Suite: 144 passed + 3 skipped; build green under warnings-as-errors;
dotnet format clean.
Docs: CHANGELOG, AGENTS, apply-templates-lifecycle, table-generation,
README, SESSION_HANDOFF updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(functions): add FunctionLibraryTemplate for DAX user-defined functions (Phase 3)
Add a new template Class "FunctionLibraryTemplate" that generates DAX
user-defined functions (UDFs) into Model.Functions (model-level; one
sub-template file declares a library of one-or-more functions). Adopts the
current DAX UDF standard (GA Sept 2025 + March 2026 reference types),
including the full parameter type system and optional parameters. JSON
config is purely additive (reuses Class/Template/IsHidden/IsEnabled; no
TemplateEntry change).
Schema (hybrid): parameters are modeled structurally and the engine
assembles the "( params ) => body" signature per the grammar, with a
RawExpression escape hatch used verbatim.
- Types: ANYVAL (default), SCALAR + subtype, TABLE, and the REF family
(ANYREF/MEASUREREF/COLUMNREF/TABLEREF/CALENDARREF, which force EXPR and
never carry an explicit passing mode); scalar-subtype shorthands
(INT64/DECIMAL/DOUBLE/STRING/BOOLEAN/DATETIME/VARIANT/NUMERIC).
- Passing modes VAL/EXPR; a trailing "= <DefaultExpression>" makes a
parameter optional. Body via Body or MultiLineBody.
Backend:
- FunctionLibraryTemplateDefinition + FunctionDefinition (+ GetBody()) +
ParameterDefinition POCOs in src/Dax.Template/Functions/.
- FunctionLibraryTemplate.ApplyTemplate validates the entire definition
before mutating, then find-or-creates each function by name, sets
Expression/Description/IsHidden (+ LineageTag on new functions), stamps
the ownership annotation, and reconciles by name with orphan cleanup.
IsEnabled=false removes all functions carrying this template's annotation.
- Validation (all throw InvalidConfigurationException): RawExpression XOR
Body/MultiLineBody; SCALAR requires a Subtype; PassingMode rejected on REF
types; PassingMode requires an explicit Type; optional params must trail
mandatory ones; no duplicate function names in a library; non-blank
function/parameter names.
- Engine dispatch wired via nameof(FunctionLibraryTemplate); model-level, no
table, no RequestTableRefresh.
- New const Attributes.SqlbiTemplateFunctions = "Functions".
Compatibility: UDFs require level >= 1702 (TOM enforces at
Model.Functions.Add); the handler pre-checks and throws a friendly
InvalidConfigurationException below that. TOM performs no grammar validation
of Function.Expression, so the engine's structured validation is the only
offline safety net; the generated signatures are only compiled by the
opt-in live-server test.
Idempotency: annotation-keyed (SQLBI_Template="Functions"), like Measures
and calculation groups. Re-apply reconciles by name and sweeps orphans; a
renamed function's old object is correctly removed (no rename limitation,
unlike Calendars).
Known limitation (documented, deferred): cross-file name collisions across
separate library entries are not detected (last-applied-wins) — matches the
MeasuresTemplate precedent.
Tests:
- Dedicated compat-1702 FunctionOfflineModelFixture (the 1600/1605/1701
fixtures untouched, existing goldens byte-identical).
- FunctionLibraryGoldenTests: shape (exact rendered Expression strings),
snapshot (Config-04), idempotency, orphan cleanup, enable/disable, the
compat-1702 guard, RawExpression pass-through, and every validation rule
(incl. PassingMode-without-Type, blank names, and the neither-branch).
- PublicApi.txt regenerated (diff limited to the 4 new types + the new const).
- Suite: 161 passed + 4 skipped; build green under warnings-as-errors;
dotnet format clean.
Docs: new docs/design/functions.md (indexed in AGENTS.md + README.md),
CHANGELOG, AGENTS, apply-templates-lifecycle updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(handoff): mark Phases 1-3 complete; point resume at live-server tests in a new session
Offline roadmap (Calendars, Calc groups, UDFs) is done, reviewed, and pushed.
Update the top-of-file resume instructions to start a NEW session (this one is
ending for maintenance) and run the opt-in [LiveServerFact] tests for all three
phases against a compat >= 1702 database — the only place the generated DAX is
actually compiled. Record the Phase 3 completion detail and the 161+4 suite count.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Added PBIX for live tests
* docs(handoff): record live-server validation PASS for Phases 1-3; reconcile committed/pushed status
All three [LiveServerFact] tests plus the Phase-0 baseline passed against the
published EmptyTestTemplates.pbix (compat >= 1702): 4 passed / 0 failed / 0 skipped.
Non-destructive (ApplyTemplates + GetModelChanges, never SaveChanges). The Phase-3
UDF pass confirms the generated ( params ) => body signatures compile server-side.
Also reconciled the stale 'NOT yet committed' per-phase notes with HEAD d70fb32.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Apply suggestion from @albertospelta
Co-authored-by: Alberto Spelta <albertospelta@users.noreply.github.com>
* Apply suggestion from @albertospelta
Co-authored-by: Alberto Spelta <albertospelta@users.noreply.github.com>
* Apply suggestion from @albertospelta
Co-authored-by: Alberto Spelta <albertospelta@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Alberto Spelta <albertospelta@users.noreply.github.com>1 parent 32b86b0 commit 7466db2
164 files changed
Lines changed: 19381 additions & 4167 deletions
File tree
- .azure/pipelines
- .claude
- .config
- .github/workflows
- docs/design
- src
- Dax.Template.TestUI
- Dax.Template.Tests
- Infrastructure
- _data
- Golden
- Templates
- pbix
- Dax.Template
- Constants
- Enums
- Exceptions
- Extensions
- Functions
- Interfaces
- Measures
- Model
- Syntax
- Tables
- CalculationGroups
- Calendars
- Dates
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | 47 | | |
53 | 48 | | |
54 | 49 | | |
| |||
57 | 52 | | |
58 | 53 | | |
59 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
60 | 58 | | |
61 | 59 | | |
62 | 60 | | |
63 | 61 | | |
64 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
65 | 70 | | |
66 | 71 | | |
67 | 72 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
111 | 115 | | |
112 | 116 | | |
113 | 117 | | |
| |||
264 | 268 | | |
265 | 269 | | |
266 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
28 | 30 | | |
29 | | - | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
30 | 40 | | |
31 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
32 | 76 | | |
33 | 77 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
66 | 69 | | |
67 | 70 | | |
68 | 71 | | |
| |||
361 | 364 | | |
362 | 365 | | |
363 | 366 | | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
0 commit comments