Ruleset hardening: promote 5 rules from Warning to Error - #10251
Ruleset hardening: promote 5 rules from Warning to Error#10251Jesper Schulz-Wedde (JesperSchulz) wants to merge 9 commits into
Conversation
AD0001 and AL0640 are overridden to Error in base.ruleset.json, which is already the effective severity: base.ruleset.json sets generalAction to Error, and neither rule is downgraded by the included ruleset.json. Removing the two entries is therefore a no-op for analysis behaviour and simply reduces the override list from 105 to 103. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
AA0471, AA0472 and AA0474 have no violations anywhere in the repository, so their overrides are removed outright. AA0473 and AS0112 each have a handful of violations. Rather than keeping the rules disabled for the whole repository, the individual sites are marked with #pragma warning disable so they become accepted violations, and the overrides are removed as well. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
CI showed that the earlier scope was based on incomplete local coverage: AA0473 and AS0112 have several hundred violations across the repository, so their overrides are restored and they are left for dedicated remediation. AA0471 and AA0474 turned out to have only six sites in total, all in the Withholding Tax app, and all of them are genuine omissions rather than acceptable exceptions. They are fixed by supplying the missing AutoFormatExpression, matching what neighbouring fields in the same tables already do. The accumulator table has no currency, so it formats in local currency. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
Removes 14 overrides from src/rulesets/base.ruleset.json so the rules inherit Error from ./ruleset.json. Selected from CI-derived violation counts (all estimated at 10 or fewer distinct sites): AW0003, AW0004, AW0014, AL0520, AL0523, AL0547, AL0589, AL0679, AL0717, AL0719, AL0749, AS0052, AS0058, PTE0007 Override count: 100 -> 86. CodeCop (AA*) rules are deliberately excluded here because removing CodeCop entries perturbs AA0021 reporting. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
Reduces the promotion set from 14 rules to the 7 whose violations are
fully resolved here. Override count: 100 -> 93.
Fixed in code:
AL0679 Email Logging Install codeunit was missing from the
'Email Logging - Obj.' permission set.
AW0014 The hidden 'F&unctions' group on the ES Account Schedule Names
page contained the target of a promoted actionref, which made
the promoted action unreachable.
AS0052 Two app.json 'help' URLs had a leading space.
Accepted with #pragma warning disable (documented in place):
AL0547 Four published integration events declare GlobalVarAccess.
Turning it off is a breaking change for existing subscribers.
AL0523 Three 'Posted Deposit Line' methods are part of the released
public surface; renaming them would be breaking.
AS0058 / PTE0007
26 asserterror statements in 11 test library codeunits that
intentionally wrap asserterror for use by test codeunits.
Deferred (overrides retained, need real remediation or owner input):
AL0520 obsolete table references, AL0589 duplicate report column names
(breaks RDLC layouts), AL0717 FlowFields with no CalcFormula (broken
fields, needs domain input), AL0719, AL0749 internal types on public
methods, AW0003, AW0004 Blob page fields.
Related to AB#640773
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
…cationTestLibrary manifests AL0523 is reported without a source location (project-level symbol comparison), so #pragma warning disable cannot suppress it. Restore its Warning override and revert the pragma in PostedDepositLine.Table.al. AS0052 fired on the DemoTool and Application Test Library views because their layer manifests do not declare 'url'. Add the standard Microsoft link used by the other 786 app.json files in the repository. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
The Verify App Changes propagation check requires that a change to a W1 layer manifest is mirrored into the localized layers that override it. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
| "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", | ||
| "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", | ||
| "help": " https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview", | ||
| "help": "https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview", |
There was a problem hiding this comment.
The corrected help URL (https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview) is still more than two path levels deep. Per AppSource Copilot grounding guidance, Copilot truncates such URLs to the first two path segments (.../dynamics365), silently grounding on unrelated sibling content instead of this app's documentation.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", | ||
| "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", | ||
| "help": " https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview", | ||
| "help": "https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview", |
There was a problem hiding this comment.
The corrected help URL (https://learn.microsoft.com/en-us/dynamics365/business-central/finance-edocuments-overview) is still more than two path levels deep. Per AppSource Copilot grounding guidance, Copilot truncates such URLs to the first two path segments (.../dynamics365), silently grounding on unrelated sibling content instead of this app's documentation.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
|
The touched Normal fields "Accumulated Base Amount" and "Accumulated WHT Amount" rely on the table's DataClassification and do not declare field-level DataClassification. Per AppSourceCop AS0016, a table-level DataClassification does not cascade to Normal fields, so these fields remain non-compliant with the field-level classification requirement even after this PR touches them. Suggested fix (apply manually — could not be anchored as a one-click suggestion): field(8; "Accumulated Base Amount"; Decimal)
{
Caption = 'Accumulated Base Amount';
AutoFormatType = 1;
// The accumulator only ever holds local currency amounts.
AutoFormatExpression = '';
DataClassification = CustomerContent;
ToolTip = 'Specifies the accumulated base amount for the withholding tax threshold accumulator.';
}
field(9; "Accumulated WHT Amount"; Decimal)
{
Caption = 'Accumulated Withholding Tax Amount';
AutoFormatType = 1;
// The accumulator only ever holds local currency amounts.
AutoFormatExpression = '';
DataClassification = CustomerContent;
ToolTip = 'Specifies the accumulated withholding tax amount for the withholding tax threshold accumulator.';
}Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
|
The touched Normal field "Taxable Base Amount" does not declare field-level DataClassification, even though other fields in this same table (e.g. lines 95 and 272) already do. This leaves the field non-compliant with AppSourceCop AS0016 and inconsistent with the table's own established pattern. Suggested fix (apply manually — could not be anchored as a one-click suggestion): field(112; "Taxable Base Amount"; Decimal)
{
Caption = 'Taxable Base Amount';
AutoFormatType = 1;
AutoFormatExpression = Rec."Currency Code";
Editable = false;
DataClassification = CustomerContent;
}Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
The build halts at the first project that reports an error, so CI only ever surfaced two AS0052 sites at a time. Enumerating the repository shows 61 app.json manifests with a missing or empty 'url', 44 of them under src/Layers/W1 and each requiring propagation to the localized layers. That is far too large to carry here, so AS0052 keeps its Warning override and the manifest changes are reverted. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
| { | ||
| Caption = 'Accumulated Base Amount'; | ||
| AutoFormatType = 1; | ||
| // The accumulator only ever holds local currency amounts. |
There was a problem hiding this comment.
The new LCY-only AutoFormatExpression ('') on "WHT Threshold Accumulator" fields "Accumulated Base Amount" and "Accumulated WHT Amount" hard-codes these totals as local-currency values, but the accumulator is populated from "Withholding Tax Entry"."Taxable Base Amount", and that source table itself stores a "Currency Code" and uses Rec."Currency Code" for its own AutoFormatExpression in this same PR. This makes the added comment's LCY-only claim architecturally inconsistent with the actual data flow, and risks mislabeling accumulated foreign-currency amounts as local currency in the UI. Either confirm/enforce that only LCY amounts ever flow into the accumulator (e.g. via validation or a comment referencing where that invariant is enforced), or source the AutoFormatExpression from a persisted currency-code field consistent with the source entries.
Agent judgement — not directly backed by a BCQuality knowledge article.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
SnapTestRunner and TestRunner are Subtype = TestRunner, not Subtype = Test, so both rules fire on their asserterror statements. They use asserterror deliberately: to probe whether a lock file could be acquired, and to surface permission errors as a test failure. A repository-wide scan confirms these were the last three uncovered sites; all 13 files that use asserterror outside a Subtype = Test codeunit are now pragma-covered. Related to AB#640773 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7e6fd312-fe92-4215-8333-f98ba0a2e25d
Summary
Continues the ruleset hardening work from #10088 and #10209. Promotes 5 rules back to
Errorby removing their overrides fromsrc/rulesets/base.ruleset.json, and resolves every violation those rules report.Override count: 100 → 95.
Promoted:
AL0547,AL0679,AW0014,AS0058,PTE0007.Fixed in code
AL0679Email Logging Installcodeunit was missing from theEmail Logging - Obj.permission set, so it was not covered by any entitlement. Every other object in the app was already listed.AW0014F&unctionsgroup on the ESAccount Schedule Namespage containedExport Schedules to ASC format, which is the target of a promotedactionref. Hiding the group made the promoted action unreachable, so theVisible = falseis removed.Accepted with
#pragma warning disableEach suppression is documented in place with the reason.
AL0547GlobalVarAccess. Turning it off would break existing external subscribers.AS0058/PTE0007asserterrorstatements in 11 test library codeunits. These areSubtype = Normalhelpers that deliberately wrapasserterroron behalf of test codeunits, which is exactly what these rules flag.Deferred
These were in the original scope but need real remediation, a dedicated PR, or domain-owner input, so their overrides are retained:
AS0052— needs its own PR. 61app.jsonmanifests have a missing or emptyurl, 44 of them undersrc/Layers/W1, each requiring propagation into the localized layers. CI only ever revealed two sites at a time because the build halts at the first project that errors, so the true scope was not visible up front.AL0523— cannot be suppressed. ThePosted Deposit Lineduplicate-method diagnostic is emitted without a source location (it is a project-level comparison against the Base Application symbol package), so#pragma warning disablehas nothing to attach to. Resolving it means renaming released public methods.AL0717— FlowFields with noCalcFormula(Script Editor Line."Has Errors",Statutory Report Data Header."Requisites Quantity"/."Set Requisites Quantity"). These are activelyCalcFields'd, so they silently always return 0/false. Genuine latent bugs; suppressing them would enshrine the bug.AL0589— duplicate report column/data item names across ~27 reports. Renaming columns breaks RDLC and Word layouts.AL0520— references to removed tables (Intrastat,Invoice Post. Buffer,Payment Buffer,VAT Code). Real obsolescence debt.AL0749— public method parameters withInternaltypes. Resolving means changing accessibility, i.e. an API change.AL0719,AW0003,AW0004— small counts but the sites could not be pinpointed from CI logs alone (CI reports no file/line).Notes
AA*) rules are deliberately excluded from this PR. Removing CodeCop entries perturbsAA0021reporting (observed in Restore Error severity for 13 dead base app ruleset entries #10138), so those get their own PR.AL1430was considered but is defined in the sharedruleset.json, not in the base override list, so it is out of scope here.Related to AB#640773