mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 09:26:52 +01:00
Complete AL review knowledge readiness (#108)
* Complete AL review knowledge readiness Fill telemetry and Query coverage, strengthen thin review domains, correct audited content defects, and add deterministic cheap-model evaluation and reference-integrity safeguards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Generalize review fixture discovery Derive smoke cases from the leaf, domain, and paired-sample conventions so new leaves require no scoring-contract changes. Keep only exceptional selection/context overrides and fail when retrieval metadata cannot rank the selected article. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Preserve published field IDs in sample Keep the existing Email and Contact Email field IDs unchanged, clarify that the sample represents an independent baseline, and use a local breaking-change rule for the generic smoke evaluation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Clarify published field identity rules State explicitly that a published field keeps its ID, name, and type while a replacement is added as a separate field under an unused ID. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Align field obsoletion sample baselines Use Email field ID 3 as the shared baseline so the bad example demonstrates a same-ID rename while the good example retains the original field and adds a separate replacement. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 --------- Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com>
This commit is contained in:
parent
ae04938c03
commit
186d8a1314
105 changed files with 2229 additions and 212 deletions
|
|
@ -1,13 +1,24 @@
|
|||
table 50253 "Sample Caption Bad"
|
||||
page 50253 "Sample Caption Bad"
|
||||
{
|
||||
fields
|
||||
PageType = Card;
|
||||
SourceTable = Customer;
|
||||
|
||||
layout
|
||||
{
|
||||
field(1; "Customer No."; Code[20])
|
||||
area(Content)
|
||||
{
|
||||
}
|
||||
field(2; "Is Active"; Boolean)
|
||||
{
|
||||
Caption = '';
|
||||
group(General)
|
||||
{
|
||||
field("Customer No."; Rec."No.")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
}
|
||||
field("Customer Name"; Rec.Name)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
Caption = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,28 @@
|
|||
table 50252 "Sample Caption Good"
|
||||
page 50252 "Sample Caption Good"
|
||||
{
|
||||
fields
|
||||
PageType = Card;
|
||||
SourceTable = Customer;
|
||||
|
||||
layout
|
||||
{
|
||||
field(1; "Customer No."; Code[20])
|
||||
area(Content)
|
||||
{
|
||||
Caption = 'Customer No.';
|
||||
}
|
||||
field(2; "Enabled"; Boolean)
|
||||
{
|
||||
}
|
||||
field(3; Amount; Decimal)
|
||||
{
|
||||
CaptionClass = '3,5,' + 'USD';
|
||||
group(General)
|
||||
{
|
||||
Caption = 'General';
|
||||
field("Customer No."; Rec."No.")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
Caption = 'Customer No.';
|
||||
ToolTip = 'Specifies the customer number.';
|
||||
}
|
||||
field("Customer Name"; Rec.Name)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
Caption = 'Customer Name';
|
||||
ToolTip = 'Specifies the customer name.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
codeunit 50260 "Sample Telemetry Id Bad"
|
||||
{
|
||||
procedure LogCustomerProcessed(var Customer: Record Customer)
|
||||
begin
|
||||
Session.LogMessage(
|
||||
'0000',
|
||||
'Customer record processed',
|
||||
Verbosity::Normal,
|
||||
DataClassification::SystemMetadata,
|
||||
TelemetryScope::All,
|
||||
'Category', 'QualitySamples');
|
||||
end;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
codeunit 50261 "Sample Telemetry Id Good"
|
||||
{
|
||||
procedure LogCustomerProcessed(var Customer: Record Customer)
|
||||
begin
|
||||
Session.LogMessage(
|
||||
'QS0001',
|
||||
'Customer record processed',
|
||||
Verbosity::Normal,
|
||||
DataClassification::SystemMetadata,
|
||||
TelemetryScope::All,
|
||||
'Category', 'QualitySamples');
|
||||
end;
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: style
|
||||
keywords: [telemetry, logmessage, event-id, sessionlogmessage, observability]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Telemetry event IDs must be stable, unique, and non-placeholder
|
||||
|
||||
## Description
|
||||
|
||||
The first parameter of `Session.LogMessage` is the **event ID**. Telemetry consumers — Application Insights queries, KQL dashboards, alert rules, support runbooks — pivot on this ID to filter and aggregate events. The contract works only when the ID is:
|
||||
|
||||
- **Stable** across releases: the same logical event keeps the same ID, so existing queries continue to match it.
|
||||
- **Unique** within the extension's telemetry catalogue: two different events MUST NOT share an ID, or downstream consumers cannot distinguish them.
|
||||
- **Non-placeholder**: literal IDs like `'0000'`, `'1234'`, `'TODO'`, or `'XX0000'` are placeholders that collide with other placeholder-using extensions, are unsearchable, and indicate the catalogue entry was never registered.
|
||||
|
||||
The convention used by Microsoft first-party AL code is a short prefix identifying the publisher or feature followed by a numeric suffix — for example `'AL0001'`, `'CUST0042'`, `'SHPFY-0007'`. The exact format is up to the extension; the requirements are stability, uniqueness, and that the chosen ID is registered in whatever catalogue or wiki the extension's telemetry consumers reference.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Assign each `Session.LogMessage` call a real, registered event ID drawn from the extension's catalogue. Treat the ID as part of the public contract of the event — renaming it is a breaking change for consumers. Keep IDs short, deterministic, and free of personal or environment-specific tokens.
|
||||
|
||||
See sample: `telemetry-event-id-stable-unique.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Calling `Session.LogMessage('0000', ...)` (or `'1234'`, `'TODO'`, an empty string, a GUID generated at runtime, or any other placeholder) leaves the event unsearchable and indistinguishable from every other event using the same placeholder. The catalogue entry never gets created because the developer "will fix it later", and the placeholder ships.
|
||||
|
||||
See sample: `telemetry-event-id-stable-unique.bad.al`.
|
||||
|
|
@ -15,12 +15,12 @@ A `Record` variable declared with the `temporary` modifier behaves nothing like
|
|||
|
||||
## Best Practice
|
||||
|
||||
Every variable of type `Record X temporary` must start with `Temp`. The same applies to parameters: a procedure that receives a temporary record as a buffer names the parameter `TempBuffer`, `TempSalesLine`, and so on. The convention extends naturally to derived names — `TempJobWIPBufferCopy`, `TempSourceSalesLine` — anything that starts with `Temp` is in-memory.
|
||||
Every local or global variable of type `Record X temporary` must start with `Temp`. Ordinary procedure parameters follow the same convention. Event publisher parameters are owned by the events-domain rule `prefix-temporary-record-event-parameters-with-temp.md`; the style leaf must not emit a second finding for the same event parameter.
|
||||
|
||||
See sample: `temporary-variable-temp-prefix.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`WIPBuffer: Record "Job WIP Buffer" temporary;` reads at the call site as if it were a database operation: `WIPBuffer.Insert()` looks identical to a write to the underlying table. The reader has to scroll back to the declaration to discover that this is in-memory, every time.
|
||||
`WIPBuffer: Record "Job WIP Buffer" temporary;` as a local, global, or ordinary procedure parameter reads at the call site as if it were a database operation. Exclude event publisher parameters here so the events leaf remains their single owner.
|
||||
|
||||
See sample: `temporary-variable-temp-prefix.bad.al`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue