Skip to content

fix: align compilers, analyzers and the decompiler with what the API Management gateway accepts - #297

Open
Chris Dayne (cdayne) wants to merge 24 commits into
Azure:mainfrom
cdayne:fix/apim-policy-conformance
Open

Chris Dayne (cdayne) wants to merge 24 commits into
Azure:mainfrom
cdayne:fix/apim-policy-conformance

Conversation

@cdayne

@cdayne Chris Dayne (cdayne) commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Brings the toolkit in line with what the API Management gateway actually accepts and does. Policies the gateway rejects no longer compile, policies it accepts are no longer blocked or mis-emitted, and decompiled C# builds and compiles back to the same XML.

Changes

Output writing

  • Expression brackets are matched with the C# lexer, so a bracket inside a string, character literal or comment no longer breaks formatting or throws.
  • InlinePolicy is always read as rawxml, whether or not it is well-formed XML, and keeps every expression of a value.
  • Text, comments and CDATA next to child elements are written instead of dropped.
  • The XML body of a liquid set-body is written as markup, in normalised form, also when a < or & is written as text in it (as in {% if n < 2 %}); other bodies stay text. An @(...) inside that markup is template text and is left alone by expression formatting and by the decompiler, and an HTML entity such as &nbsp; in it is text.

Policy compilers and authoring model

  • llm-content-safety emits <blocklists> and its streaming window attributes.
  • llm-token-limit accepts a rate limit together with a quota; token-quota is a long.
  • check-header values and set-status reason are optional; retry interval is required.
  • send-request / send-one-way-request require a non-empty URL and method unless mode is copy, and reject a nested authentication-basic.
  • Adds fail-on-cache-removal-error, authentication-endpoint, cache-response and the send-service-bus-message message attributes.
  • Renewal periods, including those of api and operation rate limits, and retry count are range-checked.
  • cors, quota and rate-limit are reported when used more than once in a section.
  • A policy used in a section that does not offer it is an error, judged by the section the method compiles to (OnError(IOutboundContext context) is on-error); only a document's own methods taking a section context are sections.
  • if (true), if (false) and an id on an if statement compile to choose.
  • A null argument, an id with no policy after it, a collection given for a single value, and a collection not written in place (such as a method call) are reported.
  • A non-string set-variable constant is written as an expression so it keeps its type; value types the gateway rejects are reported: Uri, enums, tuples, collections and arrays other than byte[] and sbyte[].
  • Implicit numeric conversions of a returned value, and of a helper to the property it is assigned to (an int helper for the long TokenQuota), are written as casts.
  • A cast to object left around an expanded helper is dropped; the gateway rejects an expression typed object.
  • Emulator-only callback properties such as RetryConfig.ConditionEvaluator are left out of the policy, so a decompiled retry with an expression condition compiles.
  • A non-constant context.NamedValue name is reported.
  • New expression surface: context.Backend, context.Workspace, Deployment.Gateway, SustainabilityInfo, Request.Foundry, byte[] Encrypt/Decrypt, VerifyNoRevocation, Url.ToString().
  • The emulator tolerates the newly optional check-header values and set-status reason, and an existence-only check-header passes for a multi-valued header. Its semantic cache store handlers accept the new optional cacheResponse argument.

Analyzers

  • Accepts Nullable, Tuple, context.Trace, anonymous types and the new context types.
  • Reports members of object (ToString, Equals, GetHashCode, GetType) where the gateway rejects them.
  • Expression return types follow what the gateway accepts as a variable value.
  • New APIM003: a bare Formatting is ambiguous in the gateway between Newtonsoft.Json and System.Xml and must be written with its namespace.
  • Helpers of a document or fragment class, including nested classes, are analysed without [Expression] when an expression can reach them. The analysis starts from the sections as the compiler finds them (Inbound, Outbound, Backend, OnError, or the first Fragment method, taking one section context), the config factories and [Expression] methods of every document and helper library in the project, and the helpers that take the expression context or a section context, and follows calls by name: bare, this., through a derived document, or qualified with the document's name directly or through a using alias, also from another document or library. A method nothing reaches, such as a ToString() override, is not analysed. A using alias of Formatting is not reported, since the compiler writes it out in full.
  • docs/ExpressionHelpers.md describes what the analyzer checks.

Decompiler

  • Keeps validate-parameters / validate-headers children, nested authentication attributes, set-body markup and the id of a choose.
  • Keeps the lines of a multi-line attribute expression.
  • Helpers get valid identifiers, the type their attribute needs (uint, long, decimal) and the using directives their expressions need.
  • A constant choose condition becomes an if; forms with no C# equivalent are kept as InlinePolicy.
  • Policies in one directory get distinct class names and fragment ids; --no-validate is honoured; a file given through two options is decompiled once, and a sibling file that is not a policy does not change a fragment's id.
  • --policy-format (--pf) takes rawxml (default) or xml, as for the compiler: as rawxml a value that begins with an expression is C# as written, so "&amp;" in it is kept; as xml the document is parsed as is. A policy read in the wrong format is reported with a hint naming the option. DecompileOptions.PolicyFormat does the same for the library.
  • A policy that is not well-formed XML has its expressions restored per text node, so one next to child elements keeps its text and a CDATA section stays one.

Templates

  • The solution template references the published 1.1.0 packages, the fragment template has its using, and the item templates name real files as primary outputs.

Known limitations

Deliberate choices, kept simple rather than modelling every case the compiler handles:

  • The analyzer follows helper calls by name, so every overload of a called helper is checked, and using static doesn't qualify a bare name.
  • The analyzer tells a document from a fragment by the interfaces it implements, not by [Document(Type = …)], and roots the first Fragment method of every part of a partial class, where the compiler compiles the attributed part only.
  • A helper in a class that is neither a document, nested in one, derived from one nor marked [Expression] still needs [Expression] to be checked; a helper inherited from a base class that isn't a document isn't checked.
  • The compiler compiles the first Fragment method taking a section context and ignores a second overload, as before.
  • The decompiler writes a file before validating its syntax, so the file can be inspected when validation fails; the exit code is 1.
  • The decompiler drops the <value> children of a set-header or set-query-parameter whose exists-action is delete; they have no effect.

Breaking changes

  • IProduct.SubscriptionLimit is renamed SubscriptionsLimit; IUrl.Port is an int.
  • RetryConfig.Interval is required; CorsConfig.TerminateUnmatchedRequest is a bool?; TokenLimitConfig.TokenQuota is a long?.
  • The semantic cache store methods take an optional cacheResponse.
  • AuthenticationManageIdentityReturnValueCompiler is removed.
  • context.Product, context.Backend and context.Workspace are nullable, and null by default in the emulator.
  • A config property set to a typed null (default(string), (string?)null, a null constant) is skipped instead of emitted as an empty attribute.
  • A null policy argument, send-request without a URL or method, a policy in a section that does not offer it, a config collection not written in place, and a set-variable value typed string[] or as a collection no longer compile.
  • The analyzers ship with the Authoring package, so projects with document helpers not marked [Expression] can now get APIM001–APIM003 errors for code the gateway rejects.
  • The decompiler reads a policy as rawxml by default. A policy whose expressions are XML-escaped needs --policy-format xml (or DecompileOptions.PolicyFormat = PolicyFormat.Xml), where it was read correctly without an option before.

Testing

  • Unit tests: Core 968, Decompiling 191 (2 skipped), Analyzers 91, Testing 303 on .NET 8 and .NET 10. .NET 9 was not run. The round-trip tests build the decompiled C# with full references, and a rawxml round trip of what the compiler writes is compared byte for byte.
  • Verified against a live API Management instance (Developer SKU):
    • save-time acceptance of the compiled policies;
    • runtime behaviour through the gateway where the result matters;
    • a round trip of 162 gateway-accepted policies: decompile, build, recompile, redeploy.
  • Re-verified on 2026-10-07 against a second Developer instance: 581 save-time cases, 43 runtime cases and a 159-policy round trip. That gateway enforces the documented set-variable value types (string[] and List<string> rejected), which the 2026-10-05 one did not; the compiler follows the documented list.
  • Other SKUs were not tested.

Chris Dayne added 5 commits October 5, 2026 15:05
- expression brackets are matched with the C# lexer, so a bracket in a string, a
  character literal or a comment no longer breaks formatting or throws
- InlinePolicy is read as rawxml whether or not it is well-formed XML, keeps every
  expression of a value and keeps a comment at the start of an expression
- text, comments and CDATA next to child elements are written instead of dropped
- the XML body of a liquid set-body is written as markup; other bodies stay text.
  Checked with requests through the gateway: a body without a template written as
  markup comes back empty, a liquid template written as escaped text comes back escaped
- bin and obj folders are only excluded below the source root, and a rooted document
  name stays inside the output folder
…gement gateway

Every change was checked by saving the compiled policy to a gateway, and by calling it
where the result matters.

Policies
- llm-content-safety emits <blocklists> and its streaming window attributes
- llm-token-limit accepts a rate limit together with a quota; token-quota is a long
- check-header values are optional; set-status reason is optional; retry interval is required
- send-request and send-one-way-request need a URL and a method unless mode is copy,
  and reject a nested authentication-basic
- add fail-on-cache-removal-error, authentication-endpoint, cache-response and the
  send-service-bus-message message attributes
- renewal periods and the retry count are checked against the accepted range
- cors, quota and rate-limit are reported when used more than once in a section
- cors terminate-unmatched-request is a bool; ForwardRequest accepts a config factory

Sections and statements
- a policy used in a section whose context does not offer it is an error, also when the
  C# is not compiled first
- only a document's own methods taking the section context are sections
- if (true), if (false) and an id on an if statement compile to choose
- an id that no policy follows, a null argument and a collection given for a single
  value are reported instead of being emitted or dropped

Expressions
- a set-variable constant that is not a string is written as an expression so it keeps
  its type; an expression of a type the gateway rejects as a variable is reported
- an implicit numeric conversion of a returned value is written out as a cast
- a non-constant context.NamedValue name is reported
- add context.Backend, context.Workspace, Deployment.Gateway, SustainabilityInfo,
  Request.Foundry, byte[] Encrypt and Decrypt, VerifyNoRevocation and Url.ToString;
  Product.SubscriptionsLimit and an int Url.Port replace the names the gateway rejects

Also fixes crashes on non-inline collections, copy-pasted policy names in diagnostics
and XML docs that contradicted the gateway.

BREAKING CHANGE: IProduct.SubscriptionLimit is SubscriptionsLimit, IUrl.Port is an int,
RetryConfig.Interval is required, CorsConfig.TerminateUnmatchedRequest is a bool,
TokenLimitConfig.TokenQuota is a long, the semantic cache store methods take an optional
cacheResponse, and AuthenticationManageIdentityReturnValueCompiler is removed.
context.Product, context.Backend and context.Workspace are nullable and null by default in
the emulator, as they are in the gateway without a subscription, a selected backend
entity or a workspace. A null
policy argument, send-request without a URL or method, and a policy in a section that
does not offer it no longer compile.
- Nullable, Tuple, context.Trace, ProductState, Authorization, anonymous types and the
  new context types are accepted
- members of object (ToString, Equals, GetHashCode, GetType) are reported: the gateway
  rejects them on a value typed object or on a type that does not declare them
- the allowed return types of an expression method follow the types the gateway
  accepts as a variable value; of arrays only string[] and byte[]
- APIM003 reports a type name the gateway cannot resolve on its own: Formatting exists
  in both Newtonsoft.Json and System.Xml and has to be written with its namespace
- the helpers of a policy document or fragment, including those in a nested class, are
  analysed without [Expression], as the compiler turns them into policy expressions
  either way

BREAKING CHANGE: the analyzers ship with the Authoring package, so a project whose
document helpers were not marked [Expression] can now get APIM001, APIM002 or APIM003
errors for code the gateway rejects when the policy is saved.
…me XML

Checked by decompiling 162 policies the gateway had accepted, building the C# in one
project, compiling it back and saving the result to the gateway again.

- keep validate-parameters and validate-headers children, nested authentication
  attributes, the markup of a set-body and the id of a choose
- keep the lines of a multi-line attribute expression, so a line comment no longer
  swallows the code after it
- a value with several named values or several expressions stays a string
- helpers get valid identifiers, the type their attribute needs (uint, long, decimal)
  and the using directives their expressions need
- a constant choose condition becomes an if statement; a form that has no C#
  equivalent is kept as InlinePolicy with its expressions written raw
- policies in one directory get distinct class names and fragment ids, and
  --no-validate is honoured
The solution template references the published 1.1.0 packages instead of commented-out
references to a version that was never published, the fragment template has its using
directive, and the item templates name real files as primary outputs.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Section detection, expression validation, serialization, emulator behavior, and analyzer consistency still contain correctness defects.

Review effort: Balanced
Findings: 1 High severity · 6 Medium severity

Open (7)
What changed in this PR

Aligns authoring, compilation, decompilation, analyzers, and emulation with API Management gateway behavior.

Changes:

  • Expands policy/expression APIs and tightens compiler validation.
  • Improves raw XML serialization and decompiler round-tripping.
  • Updates emulation, templates, analyzers, and regression coverage.
File Description
test/​Test.Testing/​Emulator/​Policies/​SetStatusTests.cs Tests optional status reason.
test/​Test.Testing/​Emulator/​Policies/​RetryTests.cs Supplies required retry intervals.
test/​Test.Testing/​Emulator/​Policies/​CheckHeaderTests.cs Tests presence-only header checks.
test/​Test.Testing/​Emulator/​MockUrlTests.cs Updates port tests to integers.
test/​Test.Decompiling/​ToolCliTests.cs Tests CLI naming and validation.
test/​Test.Core/​Serialization/​RazorCodeFormatterLexicalTests.cs Tests lexical expression formatting.
test/​Test.Core/​Serialization/​CustomXmlWriterMixedContentTests.cs Tests mixed XML content.
test/​Test.Core/​IO/​PathUtilsRootTests.cs Tests rooted path handling.
test/​Test.Core/​IO/​FileUtilsTests.cs Tests expression-aware output writing.
test/​Test.Core/​Compiling/​WithIdTests.cs Tests orphaned policy IDs.
test/​Test.Core/​Compiling/​WaitTests.cs Updates valid nested requests.
test/​Test.Core/​Compiling/​ValidateAzureAdTokenTests.cs Tests authentication endpoint and errors.
test/​Test.Core/​Compiling/​SetVariableTests.cs Tests typed variable values.
test/​Test.Core/​Compiling/​SetHeaderTests.cs Tests null argument diagnostics.
test/​Test.Core/​Compiling/​SetBodyTests.cs Tests liquid XML markup.
test/​Test.Core/​Compiling/​SendServiceBusMessageTests.cs Tests new message attributes.
test/​Test.Core/​Compiling/​SectionAlignmentTests.cs Tests section and multiplicity checks.
test/​Test.Core/​Compiling/​ReturnResponseTests.cs Tests nested body validation.
test/​Test.Core/​Compiling/​RetryTests.cs Tests retry count validation.
test/​Test.Core/​Compiling/​RateLimitTests.cs Tests renewal ranges and diagnostics.
test/​Test.Core/​Compiling/​PolicyExpressionCompilerTests.cs Tests new context and cast behavior.
test/​Test.Core/​Compiling/​NamedValueNameTests.cs Tests constant named-value names.
test/​Test.Core/​Compiling/​LlmTokenLimitTests.cs Tests combined rate and quota.
test/​Test.Core/​Compiling/​LlmSemanticCacheStoreTests.cs Tests cache-response emission.
test/​Test.Core/​Compiling/​LlmContentSafetyTests.cs Tests blocklists and streaming attributes.
test/​Test.Core/​Compiling/​LimitConcurrencyTests.cs Updates valid nested requests.
test/​Test.Core/​Compiling/​LanguageVersionCheckInterpolationTests.cs Tests multiline interpolation handling.
test/​Test.Core/​Compiling/​IpFilterTests.cs Tests non-inline address errors.
test/​Test.Core/​Compiling/​InlinePolicyExpressionTests.cs Tests raw inline expressions.
test/​Test.Core/​Compiling/​ForwardRequestTests.cs Tests config factories.
test/​Test.Core/​Compiling/​DocumentTypeTests.cs Replaces invalid fragment base calls.
test/​Test.Core/​Compiling/​CorsTests.cs Tests CORS dependency validation.
test/​Test.Core/​Compiling/​ChooseTests.cs Tests constant conditions and IDs.
test/​Test.Core/​Compiling/​CheckHeaderTests.cs Tests optional values.
test/​Test.Core/​Compiling/​CacheStoreValueTests.cs Adjusts duration expression type.
test/​Test.Core/​Compiling/​CacheStoreTests.cs Adjusts duration expression type.
test/​Test.Core/​Compiling/​CacheRemoveValueTests.cs Tests removal-error behavior.
test/​Test.Core/​Compiling/​AzureOpenAiSemanticCacheStoreTests.cs Verifies numeric casts.
test/​Test.Core/​Compiling/​AuthenticationCertificateTests.cs Tests scalar argument validation.
test/​Test.Core/​CompilerTestInitialize.cs Adds inbound test helpers.
test/​Test.Analyzers/​TypeUsedTests.cs Tests document helper analysis.
test/​Test.Analyzers/​ExpressionReturnTypeTests.cs Tests expression return types.
src/​Testing/​Expressions/​MockWorkspace.cs Adds workspace mock.
src/​Testing/​Expressions/​MockUrl.cs Changes ports to integers.
src/​Testing/​Expressions/​MockSustainabilityInfo.cs Adds sustainability mock.
src/​Testing/​Expressions/​MockRequest.cs Adds Foundry context.
src/​Testing/​Expressions/​MockProduct.cs Renames subscription limit.
src/​Testing/​Expressions/​MockGateway.cs Adds gateway mock.
src/​Testing/​Expressions/​MockFoundry.cs Adds Foundry mock.
src/​Testing/​Expressions/​MockExpressionContext.cs Adds nullable backend/workspace/product.
src/​Testing/​Expressions/​MockDeployment.cs Adds gateway and sustainability data.
src/​Testing/​Expressions/​MockBackend.cs Adds backend mock.
src/​Testing/​Emulator/​Policies/​SetStatusHandler.cs Handles omitted reason.
src/​Testing/​Emulator/​Policies/​ReturnResponseHandler.cs Handles omitted nested reason.
src/​Testing/​Emulator/​Policies/​CheckHeaderHandler.cs Handles omitted allowed values.
src/​Templates/​content/​create-solution/​test/​Project.Test.csproj References published testing package.
src/​Templates/​content/​create-solution/​src/​Project.Source.csproj References published authoring package.
src/​Templates/​content/​create-solution/​.config/​dotnet-tools.json Updates compiler tool version.
src/​Templates/​content/​create-policy-fragment/​PolicyFragment1.cs Adds required authoring import.
src/​Templates/​content/​create-policy-fragment/​.template.config/​template.json Corrects primary output.
src/​Templates/​content/​create-policy-document/​.template.config/​template.json Corrects primary output.
src/​Decompiling/​Program.cs Adds unique names and syntax validation.
src/​Core/​Serialization/​RawXmlContent.cs Classifies liquid markup bodies.
src/​Core/​Serialization/​CustomXmlWriter.cs Writes mixed XML nodes.
src/​Core/​IO/​PathUtils.cs Fixes rooted output and filtering.
src/​Core/​IO/​FileUtils.cs Formats expressions before serialization.
src/​Core/​Decompiling/​RequiredUsings.cs Adds expression-required imports.
src/​Core/​Decompiling/​PolicyDecompiler.cs Preserves whitespace and attributes.
src/​Core/​Decompiling/​Policy/​ValidateParametersDecompiler.cs Preserves parameter children.
src/​Core/​Decompiling/​Policy/​ValidateHeadersDecompiler.cs Preserves header children.
src/​Core/​Decompiling/​Policy/​ValidateContentDecompiler.cs Handles expression-valued booleans.
src/​Core/​Decompiling/​Policy/​ValidateAzureAdTokenDecompiler.cs Preserves authentication endpoint.
src/​Core/​Decompiling/​Policy/​TokenLimitDecompiler.cs Emits long quota expressions.
src/​Core/​Decompiling/​Policy/​SetQueryParameterDecompiler.cs Falls back for unsupported forms.
src/​Core/​Decompiling/​Policy/​SetHeaderDecompiler.cs Falls back for unsupported forms.
src/​Core/​Decompiling/​Policy/​SendServiceBusMessageDecompiler.cs Preserves new message attributes.
src/​Core/​Decompiling/​Policy/​SendRequestDecompilerHelper.cs Preserves authentication attributes.
src/​Core/​Decompiling/​Policy/​SendRequestDecompiler.cs Handles literal certificate bodies.
src/​Core/​Decompiling/​Policy/​SendOneWayRequestDecompiler.cs Handles literal certificate bodies.
src/​Core/​Decompiling/​Policy/​SemanticCacheStoreDecompiler.cs Preserves cache-response.
src/​Core/​Decompiling/​Policy/​SemanticCacheLookupDecompiler.cs Corrects numeric types.
src/​Core/​Decompiling/​Policy/​LlmContentSafetyDecompiler.cs Preserves streaming and blocklists.
src/​Core/​Decompiling/​Policy/​JsonToXmlDecompiler.cs Handles namespace separators safely.
src/​Core/​Decompiling/​Policy/​InlinePolicyDecompiler.cs Emits raw policy XML.
src/​Core/​Decompiling/​Policy/​CorsDecompiler.cs Corrects property types.
src/​Core/​Decompiling/​Policy/​ChooseDecompiler.cs Preserves IDs and invalid forms.
src/​Core/​Decompiling/​Policy/​CacheRemoveValueDecompiler.cs Preserves removal-error option.
src/​Core/​Decompiling/​Policy/​AuthenticationCertificateDecompiler.cs Handles certificate body/password.
src/​Core/​Compiling/​Syntax/​ExpressionStatementCompiler.cs Validates sections and pending IDs.
src/​Core/​Compiling/​Syntax/​BlockCompiler.cs Reports orphaned IDs.
src/​Core/​Compiling/​ProjectCompiler.cs Filters generated folders relative to project.
src/​Core/​Compiling/​PolicyExpressionCompiler.HelperInliningRewriter.cs Adds casts and named-value checks.
src/​Core/​Compiling/​PolicyExpressionCompiler.cs Handles constants and numeric conversions.
src/​Core/​Compiling/​Policy/​XslTransformCompiler.cs Corrects parameter diagnostic.
src/​Core/​Compiling/​Policy/​XmlToJsonCompiler.cs Corrects policy diagnostic name.
src/​Core/​Compiling/​Policy/​ValidateAzureAdTokenCompiler.cs Emits endpoint and avoids exceptions.
src/​Core/​Compiling/​Policy/​TokenLimitCompiler.cs Allows rate and quota together.
src/​Core/​Compiling/​Policy/​SetVariableCompiler.cs Preserves value types.
src/​Core/​Compiling/​Policy/​SetBodyCompiler.cs Emits and validates body content.
src/​Core/​Compiling/​Policy/​SendServiceBusMessageCompiler.cs Emits new message attributes.
src/​Core/​Compiling/​Policy/​SendRequestCompiler.cs Validates request structure/authentication.
src/​Core/​Compiling/​Policy/​SendOneWayRequestCompiler.cs Validates request structure/authentication.
src/​Core/​Compiling/​Policy/​SemanticCacheStoreCompiler.cs Adds cache-response parameter.
src/​Core/​Compiling/​Policy/​RetryCompiler.cs Validates retry count.
src/​Core/​Compiling/​Policy/​RateLimitCompiler.cs Validates range and collections.
src/​Core/​Compiling/​Policy/​RateLimitByKeyCompiler.cs Validates renewal period.
src/​Core/​Compiling/​Policy/​QuotaCompiler.cs Handles malformed collections safely.
src/​Core/​Compiling/​Policy/​QuotaByKeyCompiler.cs Validates renewal period.
src/​Core/​Compiling/​Policy/​MockResponseCompiler.cs Corrects expected type diagnostic.
src/​Core/​Compiling/​Policy/​LlmContentSafetyCompiler.cs Emits gateway-compatible XML.
src/​Core/​Compiling/​Policy/​IpFilterCompiler.cs Reports malformed ranges.
src/​Core/​Compiling/​Policy/​InvokeDaprBindingCompiler.cs Removes unused implementation.
src/​Core/​Compiling/​Policy/​InlinePolicyCompiler.cs Restores all raw expressions.
src/​Core/​Compiling/​Policy/​ForwardRequestCompiler.cs Supports config factories.
src/​Core/​Compiling/​Policy/​EmitTokenMetricCompiler.cs Reports malformed dimensions.
src/​Core/​Compiling/​Policy/​CorsCompiler.cs Validates dependent parameters.
src/​Core/​Compiling/​Policy/​CheckHeaderCompiler.cs Makes values optional.
src/​Core/​Compiling/​Policy/​CacheStoreCompiler.cs Omits null cache-response.
src/​Core/​Compiling/​Policy/​CacheRemoveValueCompiler.cs Emits removal-error option.
src/​Core/​Compiling/​Policy/​CacheLookupCompiler.cs Corrects policy diagnostic name.
src/​Core/​Compiling/​Policy/​AuthenticationManageIdentityReturnValueCompiler.cs Removes obsolete compiler.
src/​Core/​Compiling/​LanguageVersionCheck.cs Allows normalized multiline interpolation.
src/​Core/​Compiling/​DocumentCompiler.cs Restricts sections and multiplicity.
src/​Core/​Compiling/​DirectoryCompiler.cs Fixes source-folder filtering.
src/​Core/​Compiling/​Diagnostics/​CompilationErrors.cs Adds alignment diagnostics.
src/​Core/​Compiling/​CompilerUtils.cs Adds null, scalar, and range checks.
src/​Authoring/​IOutboundContext.cs Updates cache APIs and documentation.
src/​Authoring/​IOnErrorContext.cs Corrects policy documentation.
src/​Authoring/​IBackendContext.cs Corrects policy documentation.
src/​Authoring/​Expressions/​X509Certificate2Extensions.cs Adds no-revocation verification.
src/​Authoring/​Expressions/​IWorkspace.cs Adds workspace surface.
src/​Authoring/​Expressions/​IUrl.cs Corrects port and URL surface.
src/​Authoring/​Expressions/​ISustainabilityInfo.cs Adds sustainability surface.
src/​Authoring/​Expressions/​IRequest.cs Adds Foundry context.
src/​Authoring/​Expressions/​IProduct.cs Corrects subscription limit name.
src/​Authoring/​Expressions/​IGateway.cs Adds gateway surface.
src/​Authoring/​Expressions/​IFoundry.cs Adds Foundry surface.
src/​Authoring/​Expressions/​IExpressionContext.cs Adds nullable context entities.
src/​Authoring/​Expressions/​IDeployment.cs Adds gateway and sustainability.
src/​Authoring/​Expressions/​IBackend.cs Adds backend surface.
src/​Authoring/​Expressions/​ByteArrayExtensions.cs Adds encryption/decryption methods.
src/​Authoring/​Configs/​XslTransformConfig.cs Restricts stylesheet values.
src/​Authoring/​Configs/​ValidateContentConfig.cs Allows conditional expressions.
src/​Authoring/​Configs/​ValidateAzureAdTokenConfig.cs Adds endpoint and audience expressions.
src/​Authoring/​Configs/​TokenLimitConfig.cs Corrects quota types and expressions.
src/​Authoring/​Configs/​StatusConfig.cs Makes reason optional.
src/​Authoring/​Configs/​SetBodyConfig.cs Documents liquid markup behavior.
src/​Authoring/​Configs/​SendServiceBusMessageConfig.cs Adds message attributes.
src/​Authoring/​Configs/​SendRequestConfig.cs Allows response-variable expressions.
src/​Authoring/​Configs/​SemanticCacheLookupConfig.cs Corrects score and count types.
src/​Authoring/​Configs/​RetryConfig.cs Makes interval required.
src/​Authoring/​Configs/​PublishToDaprConfig.cs Allows timeout expressions.
src/​Authoring/​Configs/​MockResponseConfig.cs Corrects content-type semantics.
src/​Authoring/​Configs/​MetricDimensionConfig.cs Supports default dimensions.
src/​Authoring/​Configs/​ManagedIdentityAuthenticationConfig.cs Restricts client ID expressions.
src/​Authoring/​Configs/​LlmContentSafetyConfig.cs Adds streaming options.
src/​Authoring/​Configs/​JsonToXmlConfig.cs Restricts parse-date expressions.
src/​Authoring/​Configs/​IpFilterConfig.cs Corrects action terminology.
src/​Authoring/​Configs/​HeaderConfig.cs Documents all actions.
src/​Authoring/​Configs/​EmitTokenMetricConfig.cs Corrects expression metadata.
src/​Authoring/​Configs/​EmitMetricConfig.cs Corrects default namespace.
src/​Authoring/​Configs/​CorsConfig.cs Corrects unmatched-request type.
src/​Authoring/​Configs/​ClaimConfig.cs Allows claim expressions.
src/​Authoring/​Configs/​CheckHeaderConfig.cs Makes values optional.
src/​Authoring/​Configs/​CertificateAuthenticationConfig.cs Corrects certificate constraints.
src/​Authoring/​Configs/​CacheRemoveValueConfig.cs Adds removal-error option.
src/​Authoring/​Configs/​CacheLookupValueConfig.cs Corrects caching defaults.
src/​Authoring/​Configs/​CacheLookupConfig.cs Corrects downstream cache semantics.
src/​Analyzers/​Extensions/​SyntaxExtensions.cs Analyzes document helpers.
src/​Analyzers/​Extensions/​SymbolExtensions.cs Adds generic-free symbol formatting.
src/​Analyzers/​ExpressionTypeUsed.Rules.cs Adds APIM003.
src/​Analyzers/​ExpressionDefinition.Analyzer.cs Expands return-type validation.
docs/​ExpressionHelpers.md Documents analyzer behavior.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Core/Compiling/DocumentCompiler.cs Outdated
Comment thread src/Core/Compiling/CompilerUtils.cs
Comment thread src/Core/Compiling/Policy/SetVariableCompiler.cs Outdated
Comment thread src/Core/Serialization/CustomXmlWriter.cs
Comment thread src/Core/Serialization/RawXmlContent.cs Outdated
Comment thread src/Testing/Emulator/Policies/CheckHeaderHandler.cs Outdated
Comment thread test/Test.Analyzers/ExpressionReturnTypeTests.cs
… bodies and check-header

- a method is only a section when it takes a section context, so an overload such as
  Inbound(int) no longer becomes another section
- a set-variable value written in place, such as an enum member, is reported when the
  gateway rejects its type, as a value returned by a helper already was
- a liquid set-body that is well-formed XML is written as markup also when writing it
  back changes its form, such as single-quoted attributes; it was written as escaped text
- the emulator passes an existence-only check-header for a header with several values

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Section detection, null handling, check-header collections, and decompiled LINQ imports still contain correctness gaps.

Review effort: Balanced
Findings: 3 High severity

Open (3)
Resolved since last review (7)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Handle all typed null constants as unset values

src/​Core/​Compiling/​CompilerUtils.cs:188

The new null handling only recognizes the untyped default literal. Typed null constants such as CachingType = default(string) or a const string whose value is null continue through ProcessExpression and are emitted as policy values instead of being treated as unset; the same form can also satisfy a required-property presence check incorrectly. Use the semantic constant value for every expression, not only DefaultLiteralExpressionSyntax.

Comment thread src/Core/Compiling/DocumentCompiler.cs Outdated
Comment thread src/Core/Compiling/Policy/CheckHeaderCompiler.cs
Comment thread src/Core/Decompiling/RequiredUsings.cs Outdated
…ons and query expressions

- a section context is recognised by its type in the authoring library, so an alias
  of it is a section and an unrelated type with the same name is not
- a collection of a policy configuration that is not written in place, such as a
  method call, is reported instead of being emitted without its items
- a decompiled query expression gets the System.Linq using directive

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Raw-expression round trips, long token quotas, and request-mode validation still have correctness defects.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (3)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Invalid literal modes bypass required-field validation

src/​Core/​Compiling/​Policy/​SendRequestCompiler.cs:90

This exempts every mode other than the exact literal "new", not just "copy". Consequently Mode = "typo" with no URL or method produces no required-field diagnostics and emits a policy the gateway rejects. Only a literal copy (or a genuinely nonconstant expression whose result cannot be known here) should bypass these checks; other literal modes should be rejected.

Medium severity Use long handler for TokenQuota named values and expressions

src/​Core/​Decompiling/​Policy/​TokenLimitDecompiler.cs:38

The non-expression branch still uses the int handler even though TokenQuota is now long. In particular, a valid named-value token such as token-quota="{{quota}}" generates an int-returning helper/cast, so values above Int32.MaxValue fail and the round trip no longer preserves the required type. Add a long-specific value handler and use it for literals, expressions, and named-value tokens.

Medium severity Restore placeholders in mixed-content XML text nodes

src/​Core/​Decompiling/​PolicyDecompiler.cs:266

Raw expressions in mixed content can remain as preprocessing placeholders. RestorePlaceholders only restores element.Value when !element.HasElements, so rawxml such as a liquid set-body with expression text followed by a child element keeps __APIM_EXPR_n__ and decompiles that marker instead of the expression. Restore placeholders in each XText node regardless of whether its parent also has elements.

Comment thread src/Core/Serialization/RawXmlContent.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Pending policy IDs, compile-time nulls, nested variable types, and decompiled long named values still have correctness gaps.

Review effort: Balanced
Findings: 1 High severity · 4 Medium severity

Open (5)
Resolved since last review (1)

Comment thread src/Core/Compiling/Policy/SetVariableCompiler.cs Outdated
Comment thread src/Core/Compiling/CompilerUtils.cs Outdated
Comment thread src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs
Comment thread src/Core/Decompiling/Policy/TokenLimitDecompiler.cs
Chris Dayne added 4 commits October 5, 2026 20:42
…ected policy's id

- a set-variable helper that returns the result of another helper is followed, so a
  value of a type the gateway rejects is reported however many helpers wrap it
- a null written as default(string), (string?)null or a null constant is treated as
  null: reported as a policy argument and skipped as a config property, instead of
  being emitted as an empty value
- the id of a policy rejected for its section no longer moves on to the next policy
The helper was typed int. It compiled back to the same token, but did not match the
long the property takes.
…d value types

- default and a cast of null are recognised as null for a nullable value type, so an
  optional argument such as the cacheResponse of cache-store is left out for them
  as it is for a literal null
- an implicit numeric conversion of a returned value is written as a cast also when
  the helper returns a nullable numeric type
- the section-context check of a document reuses the one of the expression compiler
- the emulator handlers of llm-semantic-cache-store and azure-openai-semantic-cache-store
  accept the optional cacheResponse argument; every call threw since it was added
- a score-threshold put together from a named value and text is kept as written instead
  of being decompiled to a helper for a named value with the wrong name
- an sbyte[] expression value is reported: only string[] and byte[] were accepted when
  saved to a gateway
- an id that is still waiting for its policy when another WithId is given is reported,
  instead of one of the two ids being dropped
- a config property that holds an emulator-only callback, such as the ConditionEvaluator
  the decompiler writes for a retry with an expression condition, is left out of the
  policy instead of being reported as an unsupported parameter
- a helper returning (object)new { a = 1 } hid the anonymous type from the value check,
  which saw only object; a cast to object and parentheses are now looked through, as a
  call to another helper already was
- the cast to object that expanding a helper declared to return object leaves around
  its result is dropped: the gateway types an expression by its code and rejects one
  typed object
- a helper whose numeric type C# converts to the type of the property it is assigned to,
  such as an int helper for the long TokenQuota, has that conversion written out: the
  gateway rejects an int expression for token-quota
- the unit-test compilation references System.Runtime, so the types of the authoring
  library resolve in it as they do in a real project

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Send-request validation and liquid set-body serialization still allow gateway-rejected or non-round-tripping output.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (5)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Preserve markup in root value elements during decompilation

src/​Core/​Decompiling/​PolicyDecompiler.cs:280

A direct <value> child is not always the toolkit's text wrapper; it can be the root element of a legitimate liquid XML body. For <set-body template="liquid"><value><a>1</a></value></set-body>, this path treats it as non-markup, and SetBodyDecompiler then takes the wrapper branch and flattens the content to 1, dropping both elements on round-trip. Distinguish a text-only wrapper from a <value> element that itself contains markup in both places.

Medium severity Reject mixed text and markup when selecting XML output

src/​Core/​Serialization/​RawXmlContent.cs:53

Wrapping the value makes mixed non-XML content parse successfully whenever it contains an element. For example, the liquid JSON body { "a": "<b />" } produces top-level text plus a <b> node, so this classifies it as XML markup even though non-XML bodies must stay text. Require the parsed top-level content to contain no non-whitespace text before selecting markup output.

Comment thread src/Core/Compiling/Policy/SendRequestCompiler.cs Outdated
…y or not known

Any mode other than new skipped the check, so a mistyped mode compiled without a URL
or method. Only the literal copy, an expression or a named value skip it now.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Liquid body formatting can alter literal payloads, and non-finite set-variable constants lose their numeric type.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Preserve typed NaN and infinity policy expressions

src/​Core/​Compiling/​Policy/​SetVariableCompiler.cs:68

Falling back to plain text changes these values to strings, contradicting the typed-value handling above. double.NaN and the infinity members are valid policy expressions, so SetVariable("v", double.NaN) should emit @(double.NaN) (and likewise for float/infinities) rather than value="NaN".

Comment thread src/Core/IO/FileUtils.cs
Chris Dayne added 2 commits October 5, 2026 22:34
The gateway treats an @(...) inside that markup as template text, not as a policy
expression.

- formatting expressions leaves the markup alone; reflowing it as C# changed the body
- the decompiler keeps the markup as XML instead of writing its @(...) spans raw, which
  left a string that was no longer well-formed and compiled back to an escaped body
…th < or &

A template such as {% if item.Count < 2 %}<p>...</p>{% endif %} is not well-formed XML,
so the whole body was written as escaped text, which the gateway returns still escaped.
A < or & that does not start a tag or an entity is now read as text. It is written
escaped, the form the gateway accepts inside a liquid tag: {% if 1 &lt; 2 %} was checked
with a request through a gateway.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Liquid-body serialization and duplicate-policy counting can alter valid templates or report false errors, and implicit char conversions lose their declared type.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Exclude liquid set-body markup from descendant policy counts

src/​Core/​Compiling/​DocumentCompiler.cs:120

This descendant count also includes XML markup inside a liquid set-body. A template such as <rate-limit /> plus one real context.RateLimit(...) is therefore reported as two policies even though the first element is body content, not an APIM policy. Exclude descendants of liquid set-body elements (and keep counting policies nested under choose/wrapper policies).

Medium severity Preserve char types in implicit numeric constant conversions

src/​Core/​Compiling/​PolicyExpressionCompiler.cs:577

char is also a target of implicit numeric constant conversions. For example, char Separator(IExpressionContext context) => 65; currently lowers to @(65), so JsonToXmlConfig.NamespaceSeparator reaches the gateway as an int rather than the helper's declared char type. Include System_Char so this path emits @((char)65); this also preserves char? helper returns.

Comment thread src/Core/Serialization/RawXmlContent.cs Outdated
Chris Dayne added 6 commits October 6, 2026 12:37
…s and the decompilers

Compiler
- every value a multi-statement helper returns is converted to the numeric type of the
  property it is assigned to, as a single-expression helper already was, also when the
  helper reads a raw named value; a conversion of a raw named value covers all of its text
- cors, quota and rate-limit used more than once are reported in a fragment too, and an
  element named like a policy in the markup of a liquid body is content, not a policy
- an id does not move past any statement that is not a policy
- the section-context check of a document reuses the one of the expression compiler

Liquid bodies
- a liquid set-body is code only when it is one whole expression; one that merely starts
  with @( is a template
- a CDATA section in a liquid body is markup and stays one; the markup is left alone when
  expressions are formatted, and a CDATA section or a comment in it keeps its text when a
  < or & written as text is escaped
- a body given through a value element is a policy value only when the value element is
  all there is in it; a template with one among other content is markup
- the decompiler keeps the markup of a liquid body as XML, with its whitespace, and a
  score-threshold put together from a named value and text as written

Decompiled C#
- the using directives cover the remaining types the analyzer allows, the certificate
  extension methods, a query expression, and Authorization next to a System.Net type
- a token-quota named value gets a helper that returns a long

Tests
- the unit-test compilation references System.Runtime, so the types of the authoring
  library resolve as in a real project; three tests that passed on any diagnostic check
  for the one they are about; two sample policies returned a Guid where a string is needed
- the TokenQuotaPeriod and CarbonIntensityCategory comments say what the code does
Every method of a document that returns a value was analysed as expression code, which
reported a ToString() override or a static factory that never becomes a policy
expression. A method is now analysed when it takes the expression context or is called,
directly or through other helpers, from a section, a configuration factory or such a
method of the same document or of a document derived from it, which is when the
compiler expands it. Calls are matched by name: Helper(), this.Helper(), base.Helper(),
a cast of this, and a receiver made of the document's namespace, its types, the classes
it is nested in or a using alias.

APIM003 does not report a using alias of Formatting: the compiler writes it out as the
name it stands for, which the gateway accepts. The helper guide says which methods are
analysed and which named value helpers compile to the bare token.
… compiler writes them

--policy-format (--pf) takes rawxml, the default, or xml, like the compiler, and so does
DecompileOptions.PolicyFormat. A rawxml value that begins with an expression is C# as it
is written, so an entity in it, such as the string "&amp;", is kept; everything else is
XML, an @(...) further into a value or in the markup of a liquid body included. As xml the
document has to be well-formed and is parsed as it is. Before, the decompiler read a
well-formed document as xml, which turned Method + "&amp;" into Method + "&", and guessed
at the rest by whether an expression looked escaped.

When a policy is read in the wrong format the decompiler says so: a rawxml policy read as
xml fails to parse, an xml policy read as rawxml leaves C# that does not parse, and both
messages name the option.

Also: expressions of a policy that is not well-formed XML are restored per text node, so
one next to child elements no longer keeps its placeholder and a CDATA section stays one;
a character reference is decoded, unless it is no character XML allows; the decompiler
tool counts only policy documents when it decides whether a fragment shares its
directory, and decompiles a file found through two options once; the round-trip tests
build the decompiled C# with full references.

BREAKING CHANGE: the decompiler reads a policy as rawxml by default. A policy whose
expressions are XML-escaped needs --policy-format xml (or DecompileOptions.PolicyFormat
= PolicyFormat.Xml), where it was read correctly without an option before.
…nd analyse every value helper

A void method of a document that takes no section context is not a section and nothing is
compiled from it, so a helper only it calls is not a policy expression and is left alone.
Reachability is worked out once per compilation over every document and expression helper
library: a configuration factory is a root wherever it is used from, and code of another
document or library reaches a helper it names, Shared.Helper(), directly, through its own
helpers or through a using alias. A helper is told from a section by its return type,
which approximates what the compiler does, so one that takes a section context is analysed
too; reading the ExpressionContext of an authoring section context is the gateway's context
and is allowed. A section that implements IDocument explicitly is a root, and a parameter
typed IHaveExpressionContext itself counts as a section context.
… compiles

A void method taking a section context was a root whatever its name and
however many parameters it had, so the helpers that only an uncompiled
method such as Audit(IInboundContext context) or
Inbound(IInboundContext context, int retries) calls were analysed. A void
root now has to be a section as DocumentCompiler finds it: a method of a
document named Inbound, Outbound, Backend or OnError, or of a fragment
named Fragment, taking one section context, implemented explicitly or
not. Value-returning helpers taking a section context or the expression
context stay roots.
…ly, as the compiler compiles

DocumentCompiler compiles the first Fragment method taking one section
context of the class declaration it compiles and leaves an overload taking
another, so the analyzer no longer roots such an overload: the helpers
only it calls were reported for code that is never compiled. The members
of a partial class come in file order, so the first Fragment method of
each part is taken rather than of the class.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Section validation, nested range checks, required request values, and liquid markup parsing still have correctness gaps.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Resolved since last review (1)

Comment thread src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs
Comment thread src/Core/Compiling/Policy/RateLimitCompiler.cs
Comment thread src/Core/Compiling/Policy/SendRequestCompiler.cs
Comment thread src/Core/Serialization/RawXmlContent.cs
…periods, empty send-request values and tag names

A policy's availability is checked against the context of the section
its method compiles to, OnError(IOutboundContext context) is on-error,
not against the context the method is written with. The renewal period
of an api or operation rate limit has the same 1-300 range as the
policy's own. An empty or blank send-request URL or method is reported
as missing. A tag in a liquid body may start with a letter outside ASCII
or a colon, as well as an underscore.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Section discovery still accepts a non-section base context, and content-safety window literals are not range-validated.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (4)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Validate WindowSize literals against the 200–1000 range

src/​Core/​Compiling/​Policy/​LlmContentSafetyCompiler.cs:39

WindowSize is documented as valid only from 200 through 1000, but this emits any literal unchanged, so values such as 199 or 1001 compile into policies the gateway rejects. Apply the existing literal range diagnostic before adding the attribute; expression values can remain unchecked.

Comment thread src/Core/Compiling/DocumentCompiler.cs Outdated
Chris Dayne added 3 commits October 7, 2026 11:12
A policy has no DTD, so the only entities are XML's five and character
references. The stray-character pass exempted any entity-shaped name, so
a liquid body with &nbsp; failed to parse as markup and was written as
escaped text, which the gateway returns escaped. It is now escaped to
&amp;nbsp; inside the markup, as a bare & already was.

The analyzer's configuration factory is a method returning a record of
the authoring namespace, not anything of that namespace. The docs note
that a factory may take IHaveExpressionContext.
…compiles them included

A section returns nothing and takes one of the five section contexts;
IHaveExpressionContext, which they share, isn't one, so a method taking
it is no longer compiled as a second section nor rooted by the analyzer,
and a value-returning method taking a section context is a helper, as
the analyzer already had it. A set-variable value of a nullable type is
as rejected as its value, and a helper declared with a rejected type
returns one whatever its body returns. The name of a named value has one
definition, shared by the decompiler's token patterns and the formatter,
and a token in an attribute is a named value on the same terms as one in
a string. [NamedValue] is recognised the same way wherever it is looked
for: by its type, or by its name alone when the type can't be resolved.
The decompiler keeps whitespace in a body written as markup, liquid or
not. The entities a policy may hold are defined once for the writer and
the decompiler.
…y or collection

The gateway's list of value types has byte[] and sbyte[] as its only
arrays and no collection. A Developer instance probed on 2026-10-07
rejects string[], List<string>, IEnumerable<string>, HashSet<string> and
byte?[] with "Expression return type ... is not allowed", where the
instance probed on 2026-10-05 had accepted the first three; the
documented list is what the stricter gateway enforces. The compiler
reports such a value, every type of System.Collections and System.Linq
included, and the analyzer such a return type. A collection declared
elsewhere, such as a user's own, isn't reported, as the gateway hasn't
been seen to reject it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Delete-form header and query policies can lose value children during decompiler round-tripping.

Review effort: Balanced
Findings: 2 High severity

Open (2)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Low severity Add encryption emulation tests for overloads and unsupported algorithms

src/​Authoring/​Expressions/​ByteArrayExtensions.cs:16

The new encryption/decryption emulation has no automated coverage, despite introducing six overloads and algorithm-name dispatch. Add tests for AES and TripleDES round trips through both the named-algorithm and SymmetricAlgorithm overloads, plus the unsupported-algorithm path, so gateway-expression emulation regressions are caught.

Comment thread src/Core/Decompiling/Policy/SetHeaderDecompiler.cs
Comment thread src/Core/Decompiling/Policy/SetQueryParameterDecompiler.cs
@cdayne

Copy link
Copy Markdown
Contributor Author

Rafał Mielowski (@Mielek) mind taking another look at this one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants