---
id: release/al-18.0
type: release
title: AL Language extension 18.0
summary: "AL Language extension 18.0 for Business Central 2026 release wave 2 (BC29): previewed 2026-03-06, released 2026-09-09; 38 changelog entries, 25 GitHub issues linked."
tier: official
language: en
system: development
tags:
  - share packaged resources between apps
  - query readstate supports readcommitted
  - search symbols installed on the connected environment with `al_symbolsearch`
  - agents in business central move to gpt-5.3-chat
  - audit how your app is reached across boundaries with `al graph`
  - new language features
  - isolatedstorage.get with read isolation level
  - testhandlers — lifecycle hooks for test codeunits
review:
  state: derived
  by: null
  at: null
  flags: []
generated:
  at: "2026-10-11T00:26:06.897Z"
  pipeline: 0.2.0
  prompts: {}
  input_hash: 12b52861fb0789cbd386ad5f43e671df057d69fa2f3f962faa2a6dfaea6cc6e0
evidence:
  - kind: marketplace
    url: https://marketplace.visualstudio.com/items/ms-dynamics-smb.al/changelog
    title: AL Language extension changelog, version 18.0
    date: "2026-09-09"
    commit: null
    t: null
    quote: null
links:
  learn: []
  objects: []
  features: []
  topics: []
  localizations: []
  videos: []
  posts: []
  guidelines: []
source_id: al-language-extension
extension: ms-dynamics-smb.al
version: "18.0"
wave: 2026 release wave 2
major: "29"
preview_at: "2026-03-06"
released_at: "2026-09-09"
published_at: "2026-09-09"
prerelease: false
entry_count: 38
issues:
  - 7811
  - 8091
  - 8117
  - 8146
  - 8168
  - 8205
  - 8218
  - 8220
  - 8223
  - 8226
  - 8227
  - 8237
  - 8240
  - 8241
  - 8247
  - 8249
  - 8250
  - 8265
  - 8267
  - 8270
  - 8276
  - 8279
  - 8289
  - 8290
  - 8327
changes:
  - at: "2026-10-11"
    added:
      - snapshot-debugging-al-tests
    changed:
      - github-issues
    removed:
      - inherent-permissions-validation-in-multi-root-workspaces
      - miscellaneous-2
---

# AL Language extension 18.0

> AL Language extension 18.0 for Business Central 2026 release wave 2 (BC29): previewed 2026-03-06, released 2026-09-09; 38 changelog entries, 25 GitHub issues linked.

[Changelog on the Visual Studio Marketplace](https://marketplace.visualstudio.com/items/ms-dynamics-smb.al/changelog) · Business Central 2026 release wave 2 · BC29

## What changed on this page

- 2026-10-11: added "Snapshot debugging AL tests"
- 2026-10-11: changed "GitHub issues"
- 2026-10-11: removed "inherent-permissions-validation-in-multi-root-workspaces"
- 2026-10-11: removed "miscellaneous-2"

## Share packaged resources between apps

Apps can now expose selected packaged resources to other apps. Add private resources to `resourceFolders` and shared resources to `publicResourceFolders` in `app.json`:

```json
{
  "resourceFolders": [
    ".resources"
  ],
  "publicResourceFolders": [
    ".publicResources"
  ]
}
```

Successful builds list every packaged public resource in both the ALC console and the Visual Studio Code output:

```text
Public resources included in the package (2):
  schemas/customer.json
  templates/invoice.txt
```

The existing `NavApp` overloads without an app ID continue to operate in the current app and can access both private and public resources:

```al
AllOwnResources := NavApp.ListResources();
PrivateConfiguration := NavApp.GetResourceAsJson('private-config.json');
```

The new app ID overloads cross the app boundary and can access only resources declared public by the provider:

```al
PublicResources := NavApp.ListResources(ProviderAppId, 'schemas/*.json');
CustomerSchema := NavApp.GetResourceAsJson(
    'schemas/customer.json',
    ProviderAppId,
    TextEncoding::UTF8);
```

Apps can also discover providers that expose a matching public resource and then enumerate each provider:

```al
ProviderAppIds := NavApp.ListAppsWithPublicResources('schemas/*.json');
foreach ProviderAppId in ProviderAppIds do begin
    PublicResources := NavApp.ListResources(ProviderAppId, 'schemas/*.json');
    foreach ResourceName in PublicResources do
        ProcessSchema(
            ProviderAppId,
            ResourceName,
            NavApp.GetResourceAsJson(ResourceName, ProviderAppId));
end;
```

`NavApp.ListResources()` returns the current app's combined private and public resources. `NavApp.ListResources(AppId)` is always public-only, even when `AppId` is the current app. Attempts to retrieve a private resource through an app ID return the same not-found error as a resource that does not exist.

## Query ReadState supports ReadCommitted

AL queries can now use `ReadState = ReadCommitted` to read only committed data without guaranteeing that rows remain consistent throughout the transaction.

## Search symbols installed on the connected environment with `al_symbolsearch`

The `al_symbolsearch` language model tool can now search the AL objects installed on the **connected Business Central environment**, not just your local project and downloaded symbols. Pass `filters.source = "environment"` to search the objects actually present on the tenant (Base App, System App, and installed apps / per-tenant extensions). Each match includes its **owning app** (`appId`, `appName`, `appPublisher`, `appVersion`) so an agent can add the missing `app.json` dependency and download that app's symbols before using the object. When a local (workspace) search finds nothing, the tool now hints that the object may be available on the connected environment.

## Agents in Business Central move to GPT-5.3-chat

From May through June, Agents in Business Central are upgrading to GPT-5.3-chat as the default model. This update rolls out gradually to environments across all regions.

**What's changing:**

- **New default model** — GPT-5.3-chat replaces the previous model, bringing improved quality and performance for agents in Business Central.
- **Model control for coded agents** — New methods when coding agents let you control model selection programmatically from AL.
- **Model visibility and selection in agent design experience** - Administrators can see which language model their agents use and select their preferred model from the available options in the agent configuration page.

**What to expect during rollout:**

The model update and the new UI for model selection roll out independently. Your environment might receive GPT-5.3-chat before the model selection UI and new methods become available. During this period, agents continue to work as expected with the new model.

Environments in the UK, India, and Australia will receive the update at a later date.

If you want to switch models for your *designed* agents after the selection UI becomes available, you can do so from the **Agents** page in Business Central. For coded agents, use the new model selection methods to control which model your agent uses. For those of you having started on the journey of designing and coding agents, then we highly recommend evaluating your agents again once the model has updated, as agent behavior, as well as accuracy, might be impacted.

## Audit how your app is reached across boundaries with `al graph`

The `al` command-line tool gains a new **`graph`** command group that helps you audit how the code in your apps is reachable across **accessibility and debuggability boundaries**. Build a static call graph across your extension and its dependencies, then ask questions such as: which of my **internal** APIs are reachable from a **public** surface, which public entry points expose a specific internal implementation, or what `[NonDebuggable]` code can be reached from the outside.

First build a graph from source (compiling the apps together so cross-app calls resolve):

```powershell
al graph extract-whole --corpus C:\src\MyApp --corpus C:\src\MyDependencies --out .\shards --graph .\g.jsonl
```

Then query it for the boundary you care about.

**How is my internal API reached from public entry points?** (internal ← public call transitions)

```powershell
al graph query --graph .\g.jsonl --from access:public --to "access:internal+app:<my-app-guid>" --paths
```

**What is the first public surface that exposes a specific internal target?** — the outermost entry points a caller could actually hit:

```powershell
al graph query --graph .\g.jsonl --to "obj:Codeunit/My Impl#DoSensitive" --frontier access:public
```

**What code that can't be inspected in the debugger is reachable from a public surface?**

`debuggable` is the single end-effect concept — can a method be stepped into in the AL debugger and have its parameters, locals, and return value inspected? A method is *not* debuggable (`!debuggable`) for any of three reasons: its app didn't opt into debugging (`resourceExposurePolicy.allowDebugging`, default `false`), the method is marked `[NonDebuggable]`, or it's a platform built-in. (Debuggability is independent of cloud/OnPrem scope.)

```powershell
al graph query --graph .\g.jsonl --from access:public --to "!debuggable+app:<my-app-guid>" --paths
```

**Where does non-debuggable code hand data to code you can debug?**

A secret handled inside non-debuggable code stays out of the debugger — until it crosses into a method you *can* step through, where its value (as a parameter or a return value) becomes inspectable. That crossing — code you can't inspect flowing into code you can — is expressed directly:

```powershell
al graph query --graph .\g.jsonl --from !debuggable --to debuggable --paths
```

For the *first* place execution becomes inspectable on each path (the nearest debuggable frontier), use `--frontier`:

```powershell
al graph query --graph .\g.jsonl --from !debuggable --frontier debuggable
```

Scope it to a single object to review one boundary at a time:

```powershell
al graph query --graph .\g.jsonl --from "obj:Codeunit/My Secret Handler+!debuggable" --to "obj:Codeunit/My Secret Handler+debuggable" --paths
```

Add `--format sarif` on `al graph export` to turn any such query into a clickable code-flow (see below).

Selectors combine with `,` (OR), `+` (AND) and `!` / `not:` (negate a single term) over primitives such as `access:`, `scope:`, `kind:`, `ns:`, `obj:`, `app:` — plus the named aliases `debuggable` (the end-effect above), `onprem-surface`, and `elevation`. `onprem-surface` is the OnPrem-gated boundary, broader than `scope:onprem` because it also covers OnPrem `Scope` tables (which compile under Cloud scope). Add `--exclude "ns:*Test*"` to drop test code. Use `al graph export --format dgml|graphml|sarif --from <selector> --to <selector>` to render any query as an interactive graph in Visual Studio or a clickable SARIF code-flow. Exported nodes carry their source location, so you can jump straight from the graph to the `.al` file — SARIF steps open at the exact line in Visual Studio or the VS Code SARIF viewer.

## New language features

### IsolatedStorage.Get with read isolation level

`IsolatedStorage.Get` now accepts a read isolation level that controls how the key row is locked during the read. For example, `IsolationLevel::UpdLock` locks the row so it can be safely read, modified, and written back within the same transaction. This feature requires runtime version 18.0 or later.

```al
if IsolatedStorage.Get(Key, DataScope::Module, IsolationLevel::UpdLock, Value) then
    IsolatedStorage.Set(Key, NewValue, DataScope::Module);
```

### TestHandlers — lifecycle hooks for test codeunits

The new `TestHandlers` property on test codeunits lets you attach handler codeunits that receive callbacks at every stage of a test run. Use them for cross-cutting concerns such as logging, database cleanup, performance measurement, or custom test reporting — without modifying each test codeunit individually.

**Overview**

The feature introduces:

| Element | Description |
|---|---|
| `ITestHandler` interface | Defines six lifecycle callbacks that handler codeunits must implement. |
| `TestHandler` system enum (ID 2000000023) | Extensible registry where you map enum values to handler codeunit implementations. |
| `DefaultTestHandler` system enum (ID 2000000024) | Like `TestHandler`, but handlers registered here run automatically for **every** test codeunit without being listed in `TestHandlers`. |
| `TestHandlers` codeunit property | Comma-separated list of `TestHandler` enum value names. Only valid on codeunits with `Subtype = Test`. |
| `TestHandlerContext` type | Read-only context object passed to every callback with information about the running test. |

**Step 1 — Implement a handler codeunit**

Create a codeunit that implements `ITestHandler`. All six procedures have default (empty) implementations, so you only need to override the ones you care about:

```al
codeunit 50100 MyTestLogger implements ITestHandler
{
    procedure OnBeforeTestCodeunitRun(Context: TestHandlerContext)
    begin
        Message('Starting codeunit %1 (%2)', Context.CodeunitId, Context.CodeunitName);
    end;

    procedure OnAfterTestCodeunitRun(Context: TestHandlerContext)
    begin
        Message('Finished codeunit %1, Success = %2', Context.CodeunitId, Context.Success);
    end;

    procedure OnBeforeTestProcedureRun(Context: TestHandlerContext)
    begin
        Message('  Starting %1', Context.ProcedureName);
    end;

    procedure OnAfterTestProcedureRun(Context: TestHandlerContext)
    begin
        Message('  Finished %1, Success = %2', Context.ProcedureName, Context.Success);
    end;

    procedure OnBeforeTestCaseRun(Context: TestHandlerContext)
    begin
        Message('    Case %1', Context.TestCaseName);
    end;

    procedure OnAfterTestCaseRun(Context: TestHandlerContext)
    begin
        Message('    Case %1, Success = %2', Context.TestCaseName, Context.Success);
    end;
}
```

**Step 2 — Register the handler in the `TestHandler` enum**

Extend the system `TestHandler` enum and map a value to your implementation codeunit:

```al
enumextension 50100 MyTestHandlers extends TestHandler
{
    value(50100; MyLogger)
    {
        Implementation = ITestHandler = MyTestLogger;
    }
}
```

**Step 3 — Subscribe a test codeunit**

Set the `TestHandlers` property on any test codeunit. Multiple handlers are separated by commas and are invoked in the order listed:

```al
codeunit 50200 MyTests
{
    Subtype = Test;
    TestHandlers = MyLogger;

    [Test]
    procedure TestSomething()
    begin
        // ...
    end;
}
```

**Default handlers**

To run a handler for *all* test codeunits automatically (without listing it in every `TestHandlers` property), extend `DefaultTestHandler` instead:

```al
enumextension 50101 MyDefaultHandlers extends DefaultTestHandler
{
    value(50101; GlobalLogger)
    {
        Implementation = ITestHandler = MyGlobalLogger;
    }
}
```

Default handlers run before any codeunit-specific handlers.

**Callback invocation order**

For a test codeunit with two test procedures where the second uses `[TestDataSource]` with two cases, and one handler registered, the callbacks fire in this order:

```
OnBeforeTestCodeunitRun          — once, before any procedure
  OnBeforeTestProcedureRun       — before procedure 1
  OnAfterTestProcedureRun        — after procedure 1
  OnBeforeTestProcedureRun       — before procedure 2
    OnBeforeTestCaseRun          — before case A of procedure 2
    OnAfterTestCaseRun           — after case A of procedure 2
    OnBeforeTestCaseRun          — before case B of procedure 2
    OnAfterTestCaseRun           — after case B of procedure 2
  OnAfterTestProcedureRun        — after procedure 2
OnAfterTestCodeunitRun           — once, after all procedures
```

`OnBeforeTestCaseRun` / `OnAfterTestCaseRun` are only invoked for procedures that use the `[TestDataSource]` attribute for data-driven testing.

**`TestHandlerContext` members**

| Member | Type | Description |
|---|---|---|
| `CodeunitId` | `Integer` | The ID of the test codeunit being run. Always populated. |
| `CodeunitName` | `Text` | The fully qualified name of the test codeunit. Always populated. |
| `ProcedureName` | `Text` | The name of the test procedure. Empty in codeunit-level callbacks (`OnBeforeTestCodeunitRun`, `OnAfterTestCodeunitRun`). |
| `Success` | `Boolean` | Whether the test completed successfully. Only meaningful in `OnAfter*` callbacks; always `false` in `OnBefore*` callbacks. |
| `TestCaseName` | `Text` | The name of the data-driven test case. Only populated in `OnBeforeTestCaseRun` and `OnAfterTestCaseRun`. |

### Translations with namespaces

We have added a new compiler feature flag, `TranslationsWithNamespaces`, that includes the object's namespace in translation IDs to avoid collisions between objects with the same name. When enabled, the translation ID format also changes so the object and property names are visible.
If a readable translation ID would contain non-ASCII characters, the compiler now uses the hashed format so generated translation IDs remain ASCII-only.
The new Ids:
`Namespace AL.ExampleNamespace - Page SamplePage - Property Caption`

The previous Ids:
`Page 1066360047 - Property 2879900210`

## Improvements to Agentic development

- New AL0926 diagnostic to help coding agents (and developers) that have unintentionally mixed up the order of sections in an object by, for example, putting the `fieldgroups` section before the `keys` section on a table object. The current diagnostics for this case are an AL0104 ('}' expected) and an AL0198 (Expected one of the application object keywords) in combination, which might not give full transparency into solving the issue.

## GitHub issues

- [#8276](https://github.com/microsoft/AL/issues/8276) Fixed slow or unresponsive debugging on Linux when the debug adapter recursively scanned the process working directory instead of the project's package cache.
- [#8327](https://github.com/microsoft/AL/issues/8327) Fixed workspace initialization hanging when a local ruleset includes a ruleset from a URL.
- [#7811](https://github.com/microsoft/AL/issues/7811) Fixed AA0237 false positive when a variable references a table declared with TableType = Temporary.
- [#8091](https://github.com/microsoft/AL/issues/8091) Authentication settings in workspace folder settings are now correctly propagated when working in multi-root workspaces.
- [#8117](https://github.com/microsoft/AL/issues/8117) Fixed symbol download reporting that all symbols were already in the cache when a cached dependency declares `propagateDependencies` and one of its own dependencies is missing from `.alpackages`. Such packages are now downloaded again so their propagated dependencies are fetched as well.
- [#8146](https://github.com/microsoft/AL/issues/8146) Fixed a compiler crash (`InvalidCastException`) and duplicate misleading diagnostics when a member access is applied to an application object reference, such as `Codeunit::MyCodeunit.Member` or `Database::Customer.Field`. The compiler now reports a single `ERR_InvalidObjectReference` diagnostic.
- [#8168](https://github.com/microsoft/AL/issues/8168) Fixed AA0139 (possible overflow assigning to string with smaller capacity) not being reported on `Rec.Validate(Field, Value)` calls, mirroring the existing behavior for direct `Rec.Field := Value` assignments.
- [#8205](https://github.com/microsoft/AL/issues/8205) Control Add-In XML comments are not properly handled/shown
- [#8218](https://github.com/microsoft/AL/issues/8218) Fixed AA0137 (unused variable) not being reported for variables declared in multi-variable (comma-separated) declarations.
- [#8220](https://github.com/microsoft/AL/issues/8220) Fixed symbol download, publishing, and debugging failing when a cloud environment falls back to the legacy web endpoint.
- [#8223](https://github.com/microsoft/AL/issues/8223) Fixed cross-tenant sandbox connections when `useVsCodeAuthentication` is enabled. Cross-tenant connections now fall back to interactive or device code login depending on configuration.
- [#8226](https://github.com/microsoft/AL/issues/8226) Fixed `altool workspace compile` crashing on Windows with `IOException: The handle is invalid` when stdout is redirected (e.g., in CI/CD pipelines).
- [#8227](https://github.com/microsoft/AL/issues/8227) Fixed the MCP server so projects added through `al_addproject` remain available after `al_downloadsymbols` refreshes the workspace, keeping tools such as `al_symbolsearch` working correctly.
- [#8237](https://github.com/microsoft/AL/issues/8237) Fixed an issue where `Microsoft.Dynamics.Nav.EditorServices.Host` processes could remain running after Visual Studio Code closed.
- [#8240](https://github.com/microsoft/AL/issues/8240) Fixed `InvalidCastException` in `OperationExtensions.GetSymbol()` when called on `DATABASE::`, `CODEUNIT::`, or similar application object reference expressions in custom code analyzers.
- [#8241](https://github.com/microsoft/AL/issues/8241) Fixed a CLR exception in `List.AddRange` when adding an empty list of interface values.
- [#8247](https://github.com/microsoft/AL/issues/8247) Fixed Dictionary.Get(Key, var Value) emitting incorrect C# for Interface and Codeunit value types.
- [#8249](https://github.com/microsoft/AL/issues/8249) Fixed duplicate AL0197/AL0264 diagnostics when `textDocument/didOpen` URI casing differs from on-disk casing on Windows.
- [#8250](https://github.com/microsoft/AL/issues/8250) Fixed variable resolution in `Directory.App.Props.json` where variables referencing other variables would fail to resolve if they did not sort alphabetically after their dependencies.
- [#8265](https://github.com/microsoft/AL/issues/8265) Fixed AL0254 (the sorting field should be part of a key) being reported for a sorting field that is added by a table extension in an app separate from the base table. Because a key that includes such a field cannot be added from your own extension (it is blocked by AL0885/AL0886, which only allow fields from the base table in an extension key), the unactionable AL0254 is now replaced by the new diagnostic AL1430, which keeps the performance signal and points you at the app that owns the field so a key can be added there.
- [#8267](https://github.com/microsoft/AL/issues/8267) Fixed `Specified part does not exist in the package` packaging error when AL source files have names containing `(`, `)`, or `'`. `PackageCommon.CreatePackagedUri` is now idempotent and no longer double-encodes already percent-encoded path segments.
- [#8270](https://github.com/microsoft/AL/issues/8270) Fixed `al_symbolsearch` crashing when dependency symbol descriptors include a table extension key whose target table cannot be resolved.
- [#8279](https://github.com/microsoft/AL/issues/8279) Fixed AL0920 false positive when a codeunit implements an internal interface member with `Access = Internal`. The warning is now only raised when the implementation could actually widen the member's visibility beyond the module boundary.
- [#8290](https://github.com/microsoft/AL/issues/8290) Fixed AL Explorer source views omitting object details such as codeunit procedures and table methods.

## Bug fixes

- Fixed Snapshot Debugging MCP snappoint input and decoded snapshot output being shifted by one line because internal zero-based positions were exposed instead of the one-based lines shown in Visual Studio Code.
- Fixed breakpoints in default interface method implementations not being hit when continuing execution with F5.
- Fixed AL0886 being reported from runtime 18 when a table extension key references a field added by a table extension that is packaged in the same app as the base table.
- AL report layout and analysis view file path properties (`RDLCLayout`, `WordLayout`, `ExcelLayout`, `LayoutFile`, and `DefinitionFile`) now accept both `/` and `\` directory separators on all operating systems, so the same AL source compiles identically on Windows and Linux. AL0363 ("The directory separator used in this property value is not compatible with the current operating system.") is no longer reported; the diagnostic ID is reserved for backwards-compatible suppression syntax.
- Fixed an issue where a `case` statement over a `Time` variable with `Time` literal labels would cause the compiler to emit invalid C# (`case NavTime.Create(...):`) that fails with CS0426. The compiler now correctly emits an `if`/`else` chain for `Time` literals, consistent with the existing behavior for `Date` and `Decimal`.
- Fixed an issue where certain AppSourceCop upgrade rules would crash with `AD0001` when a table or tableextension defines two keys with the same name.
- Fixed an issue where publishing an extension would launch the browser even when publishing failed.
- Fixed the agent extension template so that toggling the agent's **State** inside the embedded Agent Setup Part correctly enables the **Update** action on the parent setup page, unblocking activation.
- Fixed an issue where AL sources declaring `dotnet { assembly(mscorlib) }` or referencing BCL types such as `DotNet System.Int64` failed on Linux (and other CoreCLR hosts) with `AL0451: An assembly named 'System.Private.CoreLib' could not be found`, even when the .NET runtime directory was supplied via `-assemblyprobingpaths`. The default `AssemblyLocator` now resolves BCL assemblies that ship with the running .NET runtime, and the compatibility check no longer filters out .NET 10 composite ReadyToRun BCL DLLs.
- Fixed an issue where the "Add namespace" code action for CodeCop rule [AA0247](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop-aa0247) was not offered when none of the resolution strategies worked. Strategies using the `$(parentfolder)` placeholder, the publisher name, or project name are now normalized into a valid namespace, and the action falls back to `Default.Namespace` when no valid name can be derived, so the action is always available.
- Fixed CodeCop rules AA0181 and AA0233 handling for explicit `Find` search modes. `Find('-')` and `Find('+')` are recognized as dataset operations, while `Find('=')` and `Find('<>')` remain single-record operations.
- CodeLens element for code coverage now uses the user-selected default run profile when running the related tests.
- Fixed an issue where an enum value or option member used as a field's `InitValue` was not detected by **Find All References** and was not updated when renaming that enum value or option member.
- Fixed a compiler issue where pages with user-defined GetDecimalString methods could result in C# compilation errors.
- Fixed an issue where the generated AL table proxies for CDS/Dataverse integration referenced base tables by their bare short name. When two referenced apps defined a table with the same name (for example `FS Customer Asset` in both the Base Application and Field Service Integration), the reference was ambiguous and the compile failed with AL0275. Generated `extends`, `TableRelation`, and `CalcFormula` references are now fully namespace-qualified.
- Added the `IsDirty` property to the Record API.
- Applied additional validation and filtering when loading rule sets from external `http`/`https` URLs.
- [#8289](https://github.com/microsoft/AL/issues/8289) Fixed an issue where the AL Language extension entered a perpetual "Loading workspace" state when opening a multi-root workspace whose folder roots contained no `app.json` (for example, a documentation folder combined with a folder whose AL projects live only in nested subfolders). The client now skips the progress notification when no AL workspace folder can be activated, and the language server completes the progress explicitly when the active-workspace request is not backed by a manifest.
- Fixed an issue where a multi-project workspace would not load all dependencies.

## ALTool changes

- Added `runtests` command to run AL test codeunits directly from the command line without the MCP server, enabling CI/CD test automation scenarios.
- `runtests` now emits structured, machine-parseable JSON on stdout by default (pass/fail/skip counts and per-method results), keeping stdout free of log noise; pass `--raw` for the previous human-readable console output.
- Cloud targets continue to use AAD/OAuth by default. For headless on-premises and container scenarios, `publishapp` and `runtests` now read missing connection values from `BC_SERVER_URL`, `BC_SERVER_INSTANCE`, and `BC_SERVER_PORT`; when `BC_SERVER_USERNAME` and `BC_SERVER_PASSWORD` are also set, UserPassword authentication is selected automatically. Explicit command-line options and cloud targets continue to take precedence.
- Added `--noauth` support to `al LaunchMcpServer` so headless MCP scenarios can bypass server-side auth prompting while still using the configured connection details for publish, test, and symbol operations.
- Added `al launchlspserver`, a standard LSP server over stdio that any LSP-capable AI agent or editor can consume for full AL language intelligence (completions, hover, go-to-definition, find references, rename, formatting, inlay hints, etc.). Distributed as part of the [AL dotnet tool](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-tool-package) nuget package; see [AL Language Server for agentic development](#al-language-server-for-agentic-development-al-launchlspserver) below for usage.
- Added `altool launchprofilingmcpproxy`, a stdio MCP proxy that connects an AI agent to a Business Central server's Performance Profiling MCP context to capture `.alcpuprofile` CPU profiles from a slow session; the target session id is supplied per tool call. See [Performance Profiling MCP](#performance-profiling-mcp-capture-cpu-profiles-from-a-running-session) below for usage.
- Added `altool launchsnapshotmcpproxy`, a stdio MCP proxy that connects an AI agent to a Business Central server's Snapshot Debugging MCP context to capture an AL snapshot from the next session that reproduces a failing scenario — no debugging session or up-front session id required. See [Snapshot Debugging MCP](#snapshot-debugging-mcp-capture-al-snapshots-from-a-running-session) below for usage.
- Added `--errorlogdirectory` to `altool workspace compile`. When set, each project emits a structured error log (`<project>_<timestamp>.errorLog.json`) into the specified directory, enabling AL code-analysis alert tracking for the workspace-compilation build path. When omitted, no error logs are written.
- `runtests` can now execute a batch of codeunits over a single connection via `--testgroups <file>`, a JSON file of `[{ "codeunitId": 134001, "testMethods": ["MethodA"] }, ...]` where each group pairs a codeunit with the methods to run in it (an empty or omitted `testMethods` runs all of that codeunit's methods). The whole batch runs with one authentication and one server session, avoiding the per-codeunit connect cost of invoking `runtests` once per codeunit. `--testgroups` cannot be combined with the `codeunitId` argument or `--testmethods`.

<!-- ### Runtime changes
Make a subheading for the change, write an introductory paragraph that addresses what's added, and what the usage scenario is, and if relevant, add a code example or screenshot.

#### Miscellaneous -->

## Breaking Changes

### Removed al.editorServicesPath configuration property

The `al.editorServicesPath` configuration property has been removed. The extension now always uses the standard `bin/` folder for all binaries. If you have this property configured in your settings, you should remove it as it will no longer have any effect.

## Visual Studio Code Editor changes

### Automatic .NET runtime acquisition

The AL Language extension now uses automatic .NET runtime acquisition through the [.NET Install Tool for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscode-dotnet-runtime). When the extension starts, it automatically downloads and installs the required .NET 10.0 runtime if it's not already present on your system.

Private marketplace environments must make the .NET Install Tool available so VS Code can install the AL Language extension dependency. If runtime acquisition fails after activation, AL Language falls back to a compatible ASP.NET Core runtime already installed through the system `dotnet` command.

This change significantly reduces the extension size by eliminating platform-specific runtime copies for Windows, macOS, and Linux.

### Code analyzers now deployed in bin folder

Code analyzers (CodeCop, AppSourceCop, UICop, PerTenantExtensionCop) are now deployed directly in the `bin/` folder alongside other extension binaries, avoiding duplication. Previously, analyzers were in a separate `bin/Analyzers` subfolder.

The unused `al.editorServicesPath` configuration property has been removed as the extension now always uses the standard `bin/` folder for all binaries.

### Visual Studio Code authentication is now enabled by default

The `useVsCodeAuthentication` setting now defaults to `true`. Authentication tokens are acquired through Visual Studio Code's built-in authentication instead of the extension's interactive or device code login.

This applies to all debug configurations (`launch` and `attach`) and the `al.useVsCodeAuthentication` workspace setting. If you need to use the legacy interactive or device code login, set `useVsCodeAuthentication` to `false` in your `launch.json` or workspace settings.

### Restrict global symbol downloads to a specific minor version

The new `al.globalSourcesEnforceMinorVersion` setting controls how the AL extension resolves package versions when downloading symbols from global sources.

By default (`false`), the extension downloads the latest available version within the same major version as specified in `app.json`. When set to `true`, resolution is restricted to versions matching both the major and minor version, downloading the highest available patch within that minor. This applies to both direct dependencies and propagated dependencies.

```json
"al.globalSourcesEnforceMinorVersion": true
```

### MCP Server: Add projects at runtime with `al_addproject`

The `--projects` parameter for `al LaunchMcpServer` is now optional. The server can start without any projects loaded and projects can be added dynamically at runtime using the new `al_addproject` tool.

```bash
al LaunchMcpServer --port 5000
```

Once the server is running, agents can call `al_addproject` with the path to an AL project folder (containing an `app.json` file) to load it into the workspace. All other tools (`al_compile`, `al_build`, `al_symbolsearch`, etc.) will return an actionable error if invoked before any projects have been added.

### MCP Server: File logging support

The MCP server now supports file-based logging for diagnostics and troubleshooting. Use the `--logfile` and `--loglevel` arguments to enable logging:

```bash
al LaunchMcpServer --port 5000 --logfile C:\logs\almcp.log --loglevel Verbose
```

Available log levels are `Debug`, `Verbose`, `Normal`, `Warning`, and `Error`. Use `--nolog` to disable logging entirely. If `--logfile` is not specified, logging defaults to `~/.al-mcp/almcp.log` (portable across Windows, Linux, and macOS).

### Allocate free AL object IDs with `al_getnextobjectid`

A new tool, `al_getnextobjectid`, is now available across the three agent-facing surfaces — Visual Studio Code's Language Model tools (Copilot Chat), the AL MCP server (`al LaunchMcpServer`), and the AL LSP server (`al launchlspserver`). Agents can call it during scaffolding to allocate free object IDs for any application object (Table, Page, Codeunit, Report, Query, XmlPort, Enum, PermissionSet, and the matching `*Extension` types) without having to read `app.json` themselves or risk colliding with existing objects in the project.

Inputs:

- `objectType` (required) — e.g. `"Codeunit"`, `"PageExtension"`. The tool's error message lists every supported value if an unknown type is passed.
- `count` (optional, default `1`, max `50`) — return up to N free IDs in one call.
- `rangeStart` / `rangeEnd` (optional) — narrow allocation to an explicit range; the range is intersected with the project's `idRanges` from `app.json`. When omitted, the project's manifest ranges are used in full.

The result includes the allocated IDs, the resolved ranges, and an `outOfRange` indicator (with the next ID past the configured ranges) when fewer IDs were available than requested. The scan is syntax-only and project-scoped — IDs declared in installed app dependencies or other extensions published to the same dev sandbox are not considered, so the tool always emits an advisory warning describing this limitation.

### Performance Profiling MCP: capture CPU profiles from a running session

A new Performance Profiling MCP surface lets an AI agent capture `.alcpuprofile` CPU profiles from a slow Business Central session and analyze the hot AL code paths. Unlike the troubleshooting MCP — which targets the session you are actively debugging — profiling is a standalone scenario: the target session id is supplied per tool call (the agent reads it from the prompt, e.g. *"why is session 10 slow?"*), so no debugging session is required.

**Command line (`altool launchprofilingmcpproxy`)**

The [AL dotnet tool](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-tool-package) now includes a `launchprofilingmcpproxy` command — a stdio MCP server that forwards an agent's profiling tool calls (`schedule_profiling`, `stop_schedule_and_get_profiles`, `get_profile_for_activity`) to a Business Central server's Profiling MCP context. The connection is configured once from launch.json-style parameters; the target session id is a per-call tool argument.

```bash
# On-premises
altool launchprofilingmcpproxy --environmenttype OnPrem --server http://localhost --serverinstance BC --port 7047 --authentication UserPassword

# Cloud (sandbox)
altool launchprofilingmcpproxy --environmenttype Sandbox --environmentname sandbox --tenant contoso.onmicrosoft.com
```

Credentials come from the environment and are never stored in the configuration. For **cloud** connections the proxy authenticates non-interactively (it never opens a browser) and resolves a token in this order: (1) the `BC_ACCESS_TOKEN` environment variable — in Visual Studio Code agent mode the extension injects your VS Code sign-in token here automatically, and for headless/CI hosts you set it yourself; (2) a token cached by a prior interactive `altool auth login`. For **on-premises** hosts, `BC_SERVER_USERNAME`/`BC_SERVER_PASSWORD` supply `UserPassword` (Basic) authentication, and `Windows` authentication uses the current Windows identity with no credentials. For a multitenant on-premises server, pass `--tenant` to target a specific tenant. The same `--logfile`, `--loglevel`, and `--nolog` diagnostics as the other commands are supported.

**Visual Studio Code (agent mode)**

The extension now registers a **Business Central Profiling MCP Server** as an MCP server definition provider, so GitHub Copilot agent mode can profile a session directly — with no manual `mcp.json` setup. The connection is derived from the active AL project's launch.json (cloud environment/tenant or on-premises server/instance), and authentication reuses your Visual Studio Code sign-in. Ask the agent a question that names the slow session (for example, *"Business Central is slow for session 10, can you find out why?"*) and it starts the capture, asks you to reproduce the slow scenario, stops, and analyzes the resulting profile.

**Delivery of the captured profiles**

The proxy serves the captured schedule archive over MCP `resources/read` (it advertises the `resources` capability) and also writes each captured `.alcpuprofile` / archive to a local profile folder, returning the file path in the tool result — so MCP hosts that do not surface embedded binary resource content (such as the GitHub Copilot CLI) can still read the profile from disk. Auto-captured profiles are grouped in per-schedule subfolders under a dedicated `schedules` folder, and removed once past a retention window (1 day by default, configurable with `--profileretentiondays`); cleanup is call-driven (proxy startup and the next capture), not a background timer. The profile folder location is configurable with `--profilefolder` (default `%TEMP%/bc-profiling`). A new **`analyze_profiles_in_folder`** tool analyzes profiles you place in the `user` subfolder — which the proxy never auto-deletes — for profiles obtained out of band. These local files can contain sensitive business data, so handle them securely and in line with your privacy requirements.

**Session memory usage**

Alongside the CPU samples, each captured `.alcpuprofile` now carries the profiled session's estimated total managed memory and object count, and the agent presents them in a human-readable form so you can see at a glance whether memory is a factor. This is on by default, with nothing extra to turn on.

**Recovering the target session from a schedule id**

`stop_schedule_and_get_profiles` and `get_profile_for_activity` no longer require `targetSessionId` when the agent passes a `scheduleId` armed by an earlier `schedule_profiling` call in the same session — the proxy remembers which session each schedule was armed on and routes the call automatically. `targetSessionId` remains accepted and takes precedence when supplied.

### Snapshot Debugging MCP: capture AL snapshots from a running session

A new Snapshot Debugging MCP surface lets an AI agent capture an AL snapshot from a Business Central session that reproduces a failing scenario and then debug the recorded execution offline. Snapshot debugging *arms* the server and records the **next** session that reproduces the problem: the agent initializes a debugging context (optionally scoped to a specific user), you reproduce the scenario, and the agent stops and collects the snapshot. There is often no target session yet, so a session id isn't required up front; if you already have a running session in mind, its id can be supplied to attach to that session directly.

**Command line (`altool launchsnapshotmcpproxy`)**

The [AL dotnet tool](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-tool-package) now includes a `launchsnapshotmcpproxy` command — a stdio MCP server that forwards an agent's snapshot tool calls (`initialize_snapshot_debugging`, `stop_snapshot_debugging`, `get_snapshot_status`) to a Business Central server's Snapshot Debugging MCP context. The connection is configured once from launch.json-style parameters.

```bash
# On-premises
altool launchsnapshotmcpproxy --environmenttype OnPrem --server http://localhost --serverinstance BC --port 7047 --authentication UserPassword

# Cloud (sandbox)
altool launchsnapshotmcpproxy --environmenttype Sandbox --environmentname sandbox --tenant contoso.onmicrosoft.com
```

Credentials come from the environment and are never stored in the configuration. For **cloud** connections the proxy authenticates non-interactively (it never opens a browser) and resolves a token in this order: (1) the `BC_ACCESS_TOKEN` environment variable — in Visual Studio Code agent mode the extension injects your VS Code sign-in token here automatically, and for headless/CI hosts you set it yourself; (2) a token cached by a prior interactive `altool auth login`. For **on-premises** hosts, `BC_SERVER_USERNAME`/`BC_SERVER_PASSWORD` supply `UserPassword` (Basic) authentication, and `Windows` authentication uses the current Windows identity with no credentials. For a multitenant on-premises server, pass `--tenant` to target a specific tenant. The same `--logfile`, `--loglevel`, and `--nolog` diagnostics as the other commands are supported.

**Visual Studio Code (agent mode)**

The extension now registers a **Business Central Snapshot MCP Server** as an MCP server definition provider, so GitHub Copilot agent mode can capture a snapshot directly — with no manual `mcp.json` setup. The connection is derived from the active AL project's launch.json (cloud environment/tenant or on-premises server/instance), and authentication reuses your Visual Studio Code sign-in. Ask the agent to investigate a failing scenario (for example, *"a user hits an error posting a sales order — capture a snapshot so we can debug it"*); it initializes the context, asks you to reproduce the scenario, then stops and returns the recorded snapshot.

**Delivery of the captured snapshot**

`stop_snapshot_debugging` returns the recorded snapshot archive either inline (a base64-encoded `.zip`) or, for larger captures, as an MCP `resources/read` `resourceUri` the proxy serves on demand — so MCP hosts that do not surface embedded binary resource content can still read the archive by its URI. The archive can contain sensitive business data, so handle it securely and in line with your privacy requirements.

**Environment moves and restarts**

A snapshot that is still being captured may not survive the environment being moved or restarted. If that happens, `get_snapshot_status` reports the context as not found — just initialize a new snapshot and reproduce the scenario again.

### AL Language Server for agentic development (`al launchlspserver`)

The [AL dotnet tool](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-tool-package) now includes a new `launchlspserver` command. This starts a standard [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) server over stdio, allowing AI agents and editors that speak LSP to get full AL language intelligence without requiring a Visual Studio Code client.

```bash
al launchlspserver --projects "C:\Users\MyUser\MyALProject"
```

Once running, the server responds to standard LSP requests including completions, hover, go-to-definition, find references, document symbols, rename, formatting, inlay hints, folding ranges, and type hierarchy. Projects and per-project options can also be supplied at runtime via the LSP `initialize` request's `initializationOptions`.

Per-workspace configuration can additionally be sourced from a `<rootUri>/.vscode/settings.json` — the same file the AL Visual Studio Code extension reads. Recognized `al.*` keys (`packageCachePath`, `assemblyProbingPaths`, `ruleSetPath`, `enableCodeAnalysis`, `codeAnalyzers`) override CLI defaults at startup, and LSP `initializationOptions` from the client take final precedence. JSONC comments and trailing commas are tolerated. Use `--settingspath` to point at a specific settings.json (any Visual Studio Code scope — user, workspace, or folder) when the LSP host doesn't pass the workspace's actual `rootUri`, or when one user account works across multiple AL workspaces.

For multi-app workspaces, `--workspacefile` accepts a Visual Studio Code `.code-workspace` file directly: its `folders` populate the projects loaded at startup and its inline `settings` block contributes the same `al.*` keys. This lets a single file drive both project layout and configuration, matching how Visual Studio Code itself opens the workspace.

The command supports the same compilation and logging options as `launchmcpserver`:

```
  [projects]                Optional. AL project paths to load at startup.

  --packagecachepath        Paths to the package cache folders.

  --assemblyprobingpaths    Paths to probe for dependent assemblies.

  --ruleset                 Path to the ruleset file.

  --outfolder               Output folder for compilation artifacts.

  --codeanalyzers           Code analyzers to enable.

  --settingspath            Optional. Path to a settings.json (any VS Code scope
                            — user, workspace, or folder) whose al.* keys
                            override CLI defaults at startup. When omitted,
                            auto-discovered from the initialize request's
                            rootUri (walking ancestors).

  --workspacefile           Optional. Path to a VS Code .code-workspace file. Its
                            `folders` extend the projects loaded at startup
                            (merged with the positional projects argument), and
                            its inline `settings` block contributes al.* keys
                            as workspace-level config. --settingspath, when also
                            supplied, overrides the inline settings.

  --logfile                 Path to the log file. Defaults to ~/.al-mcp/almcp.log.

  --loglevel                Log level: Debug, Verbose, Normal (default), Warning, Error.

  --nolog                   Disable logging entirely.

  --disableTelemetry        Disable telemetry collection.
```

To use the AL Language Server with Claude Code or another LSP-capable AI agent, configure the server as an LSP plugin pointing to `altool launchlspserver`. See the [Claude Code plugin documentation](https://code.claude.com/docs/en/plugins) for how to create a plugin, and the [LSP server reference](https://code.claude.com/docs/en/plugins-reference#lsp-servers) for the full `.lsp.json` schema.

<!-- ### Visual Studio Code Editor changes
Make a subheading for the change, write an introductory paragraph that addresses what's added, and what the usage scenario is, and if relevant, add a code example or screenshot.

#### Miscellaneous -->

## AppSourceCop

- [AS0116](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0116), [AS0119](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0119), [AS0121](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0121): Fixed moved objects validation for scenarios where the source and destination apps have no direct dependency on each other. The moved objects cache now correctly resolves table extensions by including all transitive dependencies when building reference symbols.
- [AS0125](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0125): An existing app can now adopt namespaces. Adding a namespace changes the translation identifier of every translated property, which previously blocked the uptake. The rule is no longer reported when the baseline object has no namespace, the new one has one, and nothing else changed. Moving an object to a different namespace, removing its namespace, or renaming it in the same version as the uptake is still reported.
- [AS0148](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0148): An interface method was made required without first being marked `[RequiredPending]`.
- [AS0149](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0149): Adding or removing a required method on an interface may break dependent extensions at runtime.
- [AS0152](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0152): Removing a codeunit used as a default implementation may break dependent extensions at runtime.

## AL Language changes

### Default interface method implementations

Interfaces can now include method bodies that serve as default implementations. Codeunits implementing the interface can omit these methods to inherit the default behavior, or provide their own implementation to override it. This feature requires runtime version 18.0 or later.

```al
interface IPaymentMethod
{
    // Default implementation — implementors can omit or override
    procedure GetTransactionFee(): Decimal
    begin
        exit(0);
    end;

    // Abstract — must be implemented
    procedure ValidateAmount(Amount: Decimal): Boolean;
}

codeunit 50100 CashPayment implements IPaymentMethod
{
    // GetTransactionFee is inherited from the interface default

    procedure ValidateAmount(Amount: Decimal): Boolean
    begin
        exit(Amount > 0);
    end;
}
```

Default methods are dispatched through the interface variable, not inherited by the implementing codeunit's public API. Calling `GetTransactionFee()` on a `Codeunit CashPayment` variable will fail — use an `Interface IPaymentMethod` variable instead.

The new `[RequiredPending]` attribute marks a default interface method as becoming required in a future version. When applied, the compiler reports:

```al
interface IPaymentMethod
{
    [RequiredPending('Will become mandatory in v28.0', '28.0')]
    procedure GetTransactionFee(): Decimal
    begin
        exit(0);
    end;
}
```

New AppSourceCop rules support the transition lifecycle:

- [AS0148](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0148): Flags methods whose default body was removed (made required) without first being marked `[RequiredPending]`.
- [AS0149](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop-as0149): Warns when interface method changes alter the runtime identifier, which may break dependent extensions until recompiled.

### New properties on ModuleInfo

The `ModuleInfo` type now exposes additional app manifest metadata. These properties require runtime version 18.0 or later:

- `Help` — Returns the help link of the specified application.
- `EULA` — Returns the End User License Agreement (EULA) link of the specified application.
- `PrivacyStatement` — Returns the privacy statement link of the specified application.
- `ContextSensitiveHelpUrl` — Returns the context sensitive help URL with the user's locale resolved from the application's supported locales.

```al
procedure ShowAppMetadata()
var
    appInfo: ModuleInfo;
begin
    NavApp.GetCurrentModuleInfo(appInfo);
    Message('Help: %1\EULA: %2', appInfo.Help, appInfo.EULA);
end;
```

### ToolTip and ToolTipML properties are now available on pages

The [`ToolTip`](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/properties/devenv-tooltip-property) and [`ToolTipML`](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/properties/devenv-tooltipml-property) properties can now be set at the page object level. When a navigation action has `RunObject = page X` and the page has a `ToolTip` or `ToolTipML` set, the action will inherit the page's tooltip if no tooltip is explicitly specified on the action itself. This mirrors the existing behavior for reports.

```al
page 50100 MyPage
{
    ToolTip = 'Opens the My Page page.';
    // ...
}
```

### Report layout subtype on Word layouts

A new `Subtype` property has been introduced on `Word` layouts where the subtype can be specified as `Default`, `HeaderFooter`, `Theme` or `Body`. Notice that layouts of subtype `Theme` must be implemented as template files with the `dotx` file extension. The `Body` subtype follows the same file type constraints as `Default` (a `docx` file): use `Default` for existing layouts, and `Body` for layouts that support composite layouts with selectable header/footer and theme parts. A body layout can set `HeaderFooterPart` and `ThemePart` to reference layouts with the `HeaderFooter` and `Theme` subtypes.

```al
report 50000 "Report With Parts"
{
    DefaultRenderingLayout = DefaultPart;

    dataset { ... }

    requestpage {...}

    rendering
    {
        layout(DefaultPart)
        {
            Type = Word;
            Subtype = Body;
            HeaderFooterPart = StandardHeaderFooter;
            ThemePart = StandardTheme;
            LayoutFile = 'Reports\Layouts\DefaultPart.docx';
        }
        layout(StandardHeaderFooter)
        {
            Type = Word;
            Subtype = HeaderFooter;
            LayoutFile = 'Reports\Layouts\StandardHeaderFooter.docx';
        }
        layout(StandardTheme)
        {
            Type = Word;
            Subtype = Theme;
            LayoutFile = 'Reports\Layouts\StandardTheme.dotx';
        }
    }
}
```

### Default header/footer and theme parts for composite Word layouts

Two new report properties, `DefaultHeaderFooterPart` and `DefaultThemePart`, let a report specify the default header/footer and theme parts used by its Word layouts. Following the same pattern as `DefaultRenderingLayout`, each property references a layout declared in the report's `rendering` section: `DefaultHeaderFooterPart` must point to a layout with the `HeaderFooter` subtype, and `DefaultThemePart` to a layout with the `Theme` subtype. Referencing a layout with a different subtype is reported as an error.

```al
report 50000 "Report With Parts"
{
    DefaultRenderingLayout = DefaultPart;
    DefaultHeaderFooterPart = StandardHeaderFooter;
    DefaultThemePart = StandardTheme;

    dataset { ... }

    requestpage { ... }

    rendering
    {
        layout(DefaultPart)
        {
            Type = Word;
            Subtype = Body;
            LayoutFile = 'Reports\Layouts\DefaultPart.docx';
        }
        layout(StandardHeaderFooter)
        {
            Type = Word;
            Subtype = HeaderFooter;
            LayoutFile = 'Reports\Layouts\StandardHeaderFooter.docx';
        }
        layout(StandardTheme)
        {
            Type = Word;
            Subtype = Theme;
            LayoutFile = 'Reports\Layouts\StandardTheme.dotx';
        }
    }
}
```

### Report custom XML parts in Microsoft Word layouts now contain CompanyMetadata

The custom XML parts in Microsoft Word layouts have been extended with a new `CompanyMetadata` element in the `BCReportInformation` element for use with composite layout rendering.

### Data-driven tests

When writing tests, you can now create data-driven tests by using the `TestDataSource` attribute. It has two arguments: the codeunit that generates the test cases and a text literal for the dataset identifier. The test method must take exactly one parameter of type `interface ITestContext` (or an interface that extends it).

```AL
[TestDataSource(Codeunit::"Late Fee Test Data Source", 'LateFeeTestData.json')]
procedure LateFeeTest(Context: interface "ILate Fee Test Context")
begin
    VerifyLateFee(Context);
end;
```

The referenced codeunit must implement the `ITestDataSource` interface. This interface has a single function, `GetDataRows`, where the first argument is the text literal from the `TestDataSource` attribute. The second argument is a type that provides context about the test codeunit. Each item in the returned list is a test case.
```AL
procedure GetDataRows(DataSetIdentifier: Text; context: DataSourceContext): List of [interface ITestContext]
begin
end;
```

Each test case is based on the `ITestContext` interface, which has a single function that returns the identifier of the test case. This identifier must be deterministic and remain the same for each run of the test function.
```AL
interface ITestContext
{
    procedure Identifier(): Text;
}
```

### Support for BigInteger field migration

The compiler now supports gradual migration between Integer and BigInteger field types:

**1. Field type changes (Integer → BigInteger)**: Changing an existing table field from Integer to BigInteger is now supported when targeting runtime version 18.0 (Fall 2026) or later. A new AppSourceCop rule (AS0141) warns about the potential consequences for dependent extensions that reference the field.

**2. Property context warnings**: When referencing a BigInteger field in property contexts (TableRelation, CalcFormula WHERE clauses, etc.), the compiler emits warnings for narrowing conversions (BigInteger → Integer) to alert about potential overflow at runtime.

Key behaviors:

- **Field upgrades**: Integer → BigInteger changes are allowed in upgrade scenarios
- **Property warnings**: BigInteger → Integer,Option,Enum conversions in WHERE clauses emit warnings about potential overflow
- **CalcFormula support**: LOOKUP, MAX, and MIN operations allow Integer ↔ BigInteger conversions with appropriate warnings for narrowing conversions

### Miscellaneous

- The compiler now warns when a table (AL0914) or table extension (AL0915) declares a large number of normal fields, which may limit extensibility due to SQL column limits.
- Word report layouts now always include extended custom XML parts for Word Add-in compatibility, simplifying layout generation and improving consistency. The compiler feature WordAddInCompatibleLayouts is now deprecated.
- Table extension keys can now mix both base table fields and extension fields in key properties (KeyFields, SumIndexFields, SqlIndex, IncludedFields) to create indexes that span table and table extension fields.
- `al` now matches command names case-insensitively, so for example `al Compile` works the same as `al compile`.
- Added four new system flow fields on Normal and Temporary tables: `SystemCreatedByUserName` (Text[50]), `SystemCreatedByFullName` (Text[80]), `SystemModifiedByUserName` (Text[50]), and `SystemModifiedByFullName` (Text[80]). These fields provide the username and full name of the user who created or last modified a record, complementing the existing `SystemCreatedBy`/`SystemModifiedBy` GUID fields.

## Snapshot debugging AL tests

- Added `run_tests` to the Snapshot Debugging MCP proxy so agents can execute selected AL tests in an isolated session attached to the armed snapshot, then collect the test capture through `stop_snapshot_debugging`.
- Added optional `snapshotVerbosity` to Snapshot Debugging MCP initialization. `SnapPoint` remains the default; `Full` captures every method context and encountered non-IP-protected AL source object for any attached session and should be used only for short, targeted reproductions.

Source: Visual Studio Marketplace, ms-dynamics-smb.al changelog, Microsoft's text unchanged. Dates from the gallery's upload records. Merged from the stable and pre-release changelogs, which differ for this version.
