From 34c931e1c1a3b90e814a567dadd316aafb321d83 Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Mon, 13 Jul 2026 10:34:23 +0200 Subject: [PATCH] Fix knowledge corpus integrity issues (#87) Repair broken knowledge references and align review examples with canonical articles. Correct explicit version gates, restore a missing title, and recognize the plugin directory in the root guard. Co-authored-by: Jesper Schulz-Wedde Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/flag-new-top-level.yml | 2 +- ...grant-rights-beyond-a-users-entitlement.md | 2 - .../abouttitle-abouttext-teaching-tips.md | 2 +- .../style/this-keyword-in-codeunits.md | 2 +- .../ui/set-selection-filter-list-scope.md | 2 + .../upgrade/datatransfer-for-bulk-init.md | 2 +- ...transfer-skips-triggers-and-subscribers.md | 2 +- microsoft/skills/review/al-code-review.md | 53 +++++++++---------- .../skills/review/al-performance-review.md | 13 +++-- microsoft/skills/review/al-privacy-review.md | 9 ++-- microsoft/skills/review/al-security-review.md | 15 +++--- microsoft/skills/review/al-style-review.md | 5 +- microsoft/skills/review/al-ui-review.md | 7 ++- microsoft/skills/review/al-upgrade-review.md | 5 +- skills/read.md | 2 +- 15 files changed, 58 insertions(+), 65 deletions(-) diff --git a/.github/workflows/flag-new-top-level.yml b/.github/workflows/flag-new-top-level.yml index 4d5f1d7..c3a5c2d 100644 --- a/.github/workflows/flag-new-top-level.yml +++ b/.github/workflows/flag-new-top-level.yml @@ -40,7 +40,7 @@ jobs: // Known, intended repository root. Anything else added at the root // is flagged for a human to eyeball. const ALLOWED_DIRS = new Set([ - '.github', 'community', 'custom', 'microsoft', 'skills', 'tools', + '.claude-plugin', '.github', 'community', 'custom', 'microsoft', 'skills', 'tools', ]); const ALLOWED_FILES = new Set([ '.gitignore', 'CODEOWNERS', 'LICENSE', 'README.md', diff --git a/community/knowledge/security/do-not-grant-rights-beyond-a-users-entitlement.md b/community/knowledge/security/do-not-grant-rights-beyond-a-users-entitlement.md index 5334ab7..387c778 100644 --- a/community/knowledge/security/do-not-grant-rights-beyond-a-users-entitlement.md +++ b/community/knowledge/security/do-not-grant-rights-beyond-a-users-entitlement.md @@ -19,8 +19,6 @@ Entitlements are license-level caps on what a user can access, derived automatic When designing a permission set that ships with an extension, consult the entitlement model for the target user population before finalizing the grants. Every object and tabledata right the set expects to grant should be reachable within the intended entitlement tier; if it is not, the set needs to be scoped to licenses that permit it, or the feature needs a different access path. -See sample: `do-not-grant-rights-beyond-a-users-entitlement.good.al`. - ## Anti Pattern Authoring permission sets in a sandbox with full-license context and shipping them without verifying which entitlement tier customer users actually hold. The sets look complete in test; on a real customer they silently lose rights at runtime and the symptom is "the feature does not work for some users" with no obvious authorization error. diff --git a/microsoft/knowledge/style/abouttitle-abouttext-teaching-tips.md b/microsoft/knowledge/style/abouttitle-abouttext-teaching-tips.md index f72b959..edefcb6 100644 --- a/microsoft/knowledge/style/abouttitle-abouttext-teaching-tips.md +++ b/microsoft/knowledge/style/abouttitle-abouttext-teaching-tips.md @@ -1,5 +1,5 @@ --- -bc-version: [all] +bc-version: [21..] domain: style keywords: [abouttitle, abouttext, teaching-tip, onboarding, page] technologies: [al] diff --git a/microsoft/knowledge/style/this-keyword-in-codeunits.md b/microsoft/knowledge/style/this-keyword-in-codeunits.md index ffcf5a1..b38cc24 100644 --- a/microsoft/knowledge/style/this-keyword-in-codeunits.md +++ b/microsoft/knowledge/style/this-keyword-in-codeunits.md @@ -1,5 +1,5 @@ --- -bc-version: [all] +bc-version: [25..] domain: style keywords: [this, codeunit, self-reference, aa0248, scope] technologies: [al] diff --git a/microsoft/knowledge/ui/set-selection-filter-list-scope.md b/microsoft/knowledge/ui/set-selection-filter-list-scope.md index 59bde40..513590c 100644 --- a/microsoft/knowledge/ui/set-selection-filter-list-scope.md +++ b/microsoft/knowledge/ui/set-selection-filter-list-scope.md @@ -7,6 +7,8 @@ countries: [w1] application-area: [all] --- +# Preserve list scope after `SetSelectionFilter` + ## Description `CurrPage.SetSelectionFilter(Rec)` behaves differently depending on whether the user explicitly multi-selected rows. When no rows are marked — the cursor is simply positioned on a row — the method writes a primary key filter for that single row and leaves `MarkedOnly` as false. When the user explicitly selected multiple rows, the method marks those records and sets `MarkedOnly` to true. A batch action that calls `SetSelectionFilter` and then passes the record directly to a processing codeunit will therefore silently restrict to one row whenever the user has not made an explicit selection, which is almost never the intended behaviour for an action labelled "Verify All" or "Post All". diff --git a/microsoft/knowledge/upgrade/datatransfer-for-bulk-init.md b/microsoft/knowledge/upgrade/datatransfer-for-bulk-init.md index 3eeaa46..988dfa4 100644 --- a/microsoft/knowledge/upgrade/datatransfer-for-bulk-init.md +++ b/microsoft/knowledge/upgrade/datatransfer-for-bulk-init.md @@ -1,5 +1,5 @@ --- -bc-version: [all] +bc-version: [21..] domain: upgrade keywords: [datatransfer, large-dataset, bulk-update, modifyall, copyfields, new-field] technologies: [al] diff --git a/microsoft/knowledge/upgrade/datatransfer-skips-triggers-and-subscribers.md b/microsoft/knowledge/upgrade/datatransfer-skips-triggers-and-subscribers.md index 785684f..49d9ab1 100644 --- a/microsoft/knowledge/upgrade/datatransfer-skips-triggers-and-subscribers.md +++ b/microsoft/knowledge/upgrade/datatransfer-skips-triggers-and-subscribers.md @@ -1,5 +1,5 @@ --- -bc-version: [all] +bc-version: [21..] domain: upgrade keywords: [datatransfer, validate-trigger, event-subscriber, side-effects, business-logic] technologies: [al] diff --git a/microsoft/skills/review/al-code-review.md b/microsoft/skills/review/al-code-review.md index ba58d70..3ea0356 100644 --- a/microsoft/skills/review/al-code-review.md +++ b/microsoft/skills/review/al-code-review.md @@ -127,36 +127,36 @@ Output conforms to the DO output contract, extended with `sub-results` and `skip }, "findings": [ { - "id": "microsoft/knowledge/performance/filter-before-find.md", + "id": "microsoft/knowledge/performance/apply-filters-before-iterating.md", "severity": "major", - "message": "FindSet is called on a record variable without any prior SetRange/SetFilter. This forces a full-table scan.", + "message": "The Country/Region Code predicate is evaluated inside the loop instead of with SetRange before FindSet, so every row crosses the database boundary.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 140, "range": { "start-line": 140, "end-line": 144 } }, "references": [ - { "path": "microsoft/knowledge/performance/filter-before-find.md" } + { "path": "microsoft/knowledge/performance/apply-filters-before-iterating.md" } ], "confidence": "high", "from-sub-skill": "al-performance-review" }, { - "id": "community/knowledge/performance/call-setloadfields-before-filters.md", + "id": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md", "severity": "minor", - "message": "SetLoadFields is called after SetRange. Per the referenced guidance the call must come before filters to be folded into the query plan.", + "message": "The loop reads an unlisted field after SetLoadFields, triggering a hidden JIT load for each record passed by value.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 152 }, "references": [ - { "path": "community/knowledge/performance/call-setloadfields-before-filters.md" } + { "path": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md" } ], "confidence": "high", "from-sub-skill": "al-performance-review" }, { - "id": "microsoft/knowledge/security/use-secrettext-for-credentials.md", + "id": "microsoft/knowledge/security/secrettext-for-credentials.md", "severity": "blocker", "message": "A bearer token is declared as a Text parameter and passed through the HTTP request path as plain text. The referenced guidance requires credentials to flow as SecretText end-to-end.", "location": { @@ -165,21 +165,21 @@ Output conforms to the DO output contract, extended with `sub-results` and `skip "range": { "start-line": 85, "end-line": 89 } }, "references": [ - { "path": "microsoft/knowledge/security/use-secrettext-for-credentials.md" } + { "path": "microsoft/knowledge/security/secrettext-for-credentials.md" } ], "confidence": "high", "from-sub-skill": "al-security-review" }, { - "id": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md", + "id": "community/knowledge/security/secrets-isolated-storage.md", "severity": "minor", - "message": "An API key is assigned from a string literal rather than retrieved from IsolatedStorage or Key Vault at runtime.", + "message": "A setup table stores an API key in an ordinary Text field, exposing it through table reads and exports. Persist it in IsolatedStorage instead.", "location": { - "file": "src/Integration/ApiClient.Codeunit.al", - "line": 201 + "file": "src/Integration/ExternalServiceSetup.Table.al", + "line": 12 }, "references": [ - { "path": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md" } + { "path": "community/knowledge/security/secrets-isolated-storage.md" } ], "confidence": "medium", "from-sub-skill": "al-security-review" @@ -209,29 +209,29 @@ Output conforms to the DO output contract, extended with `sub-results` and `skip }, "findings": [ { - "id": "microsoft/knowledge/performance/filter-before-find.md", + "id": "microsoft/knowledge/performance/apply-filters-before-iterating.md", "severity": "major", - "message": "FindSet is called on a record variable without any prior SetRange/SetFilter. This forces a full-table scan.", + "message": "The Country/Region Code predicate is evaluated inside the loop instead of with SetRange before FindSet, so every row crosses the database boundary.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 140, "range": { "start-line": 140, "end-line": 144 } }, "references": [ - { "path": "microsoft/knowledge/performance/filter-before-find.md" } + { "path": "microsoft/knowledge/performance/apply-filters-before-iterating.md" } ], "confidence": "high" }, { - "id": "community/knowledge/performance/call-setloadfields-before-filters.md", + "id": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md", "severity": "minor", - "message": "SetLoadFields is called after SetRange. Per the referenced guidance the call must come before filters to be folded into the query plan.", + "message": "The loop reads an unlisted field after SetLoadFields, triggering a hidden JIT load for each record passed by value.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 152 }, "references": [ - { "path": "community/knowledge/performance/call-setloadfields-before-filters.md" } + { "path": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md" } ], "confidence": "high" } @@ -247,7 +247,7 @@ Output conforms to the DO output contract, extended with `sub-results` and `skip }, "findings": [ { - "id": "microsoft/knowledge/security/use-secrettext-for-credentials.md", + "id": "microsoft/knowledge/security/secrettext-for-credentials.md", "severity": "blocker", "message": "A bearer token is declared as a Text parameter and passed through the HTTP request path as plain text. The referenced guidance requires credentials to flow as SecretText end-to-end.", "location": { @@ -256,20 +256,20 @@ Output conforms to the DO output contract, extended with `sub-results` and `skip "range": { "start-line": 85, "end-line": 89 } }, "references": [ - { "path": "microsoft/knowledge/security/use-secrettext-for-credentials.md" } + { "path": "microsoft/knowledge/security/secrettext-for-credentials.md" } ], "confidence": "high" }, { - "id": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md", + "id": "community/knowledge/security/secrets-isolated-storage.md", "severity": "minor", - "message": "An API key is assigned from a string literal rather than retrieved from IsolatedStorage or Key Vault at runtime.", + "message": "A setup table stores an API key in an ordinary Text field, exposing it through table reads and exports. Persist it in IsolatedStorage instead.", "location": { - "file": "src/Integration/ApiClient.Codeunit.al", - "line": 201 + "file": "src/Integration/ExternalServiceSetup.Table.al", + "line": 12 }, "references": [ - { "path": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md" } + { "path": "community/knowledge/security/secrets-isolated-storage.md" } ], "confidence": "medium" } @@ -310,4 +310,3 @@ The empty-corpus case — BCQuality's state until knowledge files land — rolls ] } ``` - diff --git a/microsoft/skills/review/al-performance-review.md b/microsoft/skills/review/al-performance-review.md index 09bef27..5b92d2b 100644 --- a/microsoft/skills/review/al-performance-review.md +++ b/microsoft/skills/review/al-performance-review.md @@ -89,29 +89,29 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/performance/filter-before-find.md", + "id": "microsoft/knowledge/performance/apply-filters-before-iterating.md", "severity": "major", - "message": "FindSet is called on a record variable without any prior SetRange/SetFilter. This forces a full-table scan.", + "message": "The Country/Region Code predicate is evaluated inside the loop instead of with SetRange before FindSet, so every row crosses the database boundary.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 140, "range": { "start-line": 140, "end-line": 144 } }, "references": [ - { "path": "microsoft/knowledge/performance/filter-before-find.md" } + { "path": "microsoft/knowledge/performance/apply-filters-before-iterating.md" } ], "confidence": "high" }, { - "id": "community/knowledge/performance/call-setloadfields-before-filters.md", + "id": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md", "severity": "minor", - "message": "SetLoadFields is called after SetRange. Per the referenced guidance the call must come before filters to be folded into the query plan.", + "message": "The loop reads an unlisted field after SetLoadFields, triggering a hidden JIT load for each record passed by value.", "location": { "file": "src/Sales/PostingRoutines.Codeunit.al", "line": 152 }, "references": [ - { "path": "community/knowledge/performance/call-setloadfields-before-filters.md" } + { "path": "community/knowledge/performance/setloadfields-unlisted-field-triggers-jit-load.md" } ], "confidence": "high" } @@ -134,4 +134,3 @@ The empty-corpus case — BCQuality's state until performance knowledge files la "suppressed": [] } ``` - diff --git a/microsoft/skills/review/al-privacy-review.md b/microsoft/skills/review/al-privacy-review.md index 4ef87e3..bf60f5d 100644 --- a/microsoft/skills/review/al-privacy-review.md +++ b/microsoft/skills/review/al-privacy-review.md @@ -89,16 +89,16 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/privacy/strsubstno-prebuild-breaks-error-telemetry-classification.md", + "id": "microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md", "severity": "major", - "message": "Error receives a pre-built Text produced by StrSubstNo with customer name and email as arguments. Per the referenced guidance the platform cannot classify or strip PII from an opaque Text and will export the full message to telemetry.", + "message": "The new Customer E-Mail table field has no DataClassification property, leaving personal data unclassified.", "location": { - "file": "src/Sales/CustomerValidation.Codeunit.al", + "file": "src/Sales/Customer.TableExt.al", "line": 64, "range": { "start-line": 60, "end-line": 64 } }, "references": [ - { "path": "microsoft/knowledge/privacy/strsubstno-prebuild-breaks-error-telemetry-classification.md" } + { "path": "microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md" } ], "confidence": "high" } @@ -106,4 +106,3 @@ Output conforms to the DO output contract. A populated example: "suppressed": [] } ``` - diff --git a/microsoft/skills/review/al-security-review.md b/microsoft/skills/review/al-security-review.md index 1e72447..1986934 100644 --- a/microsoft/skills/review/al-security-review.md +++ b/microsoft/skills/review/al-security-review.md @@ -89,7 +89,7 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/security/use-secrettext-for-credentials.md", + "id": "microsoft/knowledge/security/secrettext-for-credentials.md", "severity": "blocker", "message": "A bearer token is declared as a Text parameter and passed through the HTTP request path as plain text. The referenced guidance requires credentials to flow as SecretText end-to-end.", "location": { @@ -98,20 +98,20 @@ Output conforms to the DO output contract. A populated example: "range": { "start-line": 85, "end-line": 89 } }, "references": [ - { "path": "microsoft/knowledge/security/use-secrettext-for-credentials.md" } + { "path": "microsoft/knowledge/security/secrettext-for-credentials.md" } ], "confidence": "high" }, { - "id": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md", + "id": "community/knowledge/security/secrets-isolated-storage.md", "severity": "minor", - "message": "An API key is assigned from a string literal rather than retrieved from IsolatedStorage or Key Vault at runtime.", + "message": "A setup table stores an API key in an ordinary Text field, exposing it through table reads and exports. Persist it in IsolatedStorage instead.", "location": { - "file": "src/Integration/ApiClient.Codeunit.al", - "line": 201 + "file": "src/Integration/ExternalServiceSetup.Table.al", + "line": 12 }, "references": [ - { "path": "microsoft/knowledge/security/never-hardcode-secrets-in-al.md" } + { "path": "community/knowledge/security/secrets-isolated-storage.md" } ], "confidence": "medium" } @@ -134,4 +134,3 @@ The empty-corpus case — BCQuality's state until security knowledge files land "suppressed": [] } ``` - diff --git a/microsoft/skills/review/al-style-review.md b/microsoft/skills/review/al-style-review.md index d926df3..fb15dba 100644 --- a/microsoft/skills/review/al-style-review.md +++ b/microsoft/skills/review/al-style-review.md @@ -87,7 +87,7 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/style/apply-approved-label-suffixes.md", + "id": "microsoft/knowledge/style/label-suffix-approved-list.md", "severity": "minor", "message": "A Label named Text000 has no approved suffix (Msg/Err/Qst/Tok/Lbl/Txt). Per the referenced CodeCop AA0074 guidance, every Label and TextConst carries a suffix indicating its consuming call.", "location": { @@ -95,7 +95,7 @@ Output conforms to the DO output contract. A populated example: "line": 42 }, "references": [ - { "path": "microsoft/knowledge/style/apply-approved-label-suffixes.md" } + { "path": "microsoft/knowledge/style/label-suffix-approved-list.md" } ], "confidence": "high" } @@ -103,4 +103,3 @@ Output conforms to the DO output contract. A populated example: "suppressed": [] } ``` - diff --git a/microsoft/skills/review/al-ui-review.md b/microsoft/skills/review/al-ui-review.md index ef2e94d..2f99c12 100644 --- a/microsoft/skills/review/al-ui-review.md +++ b/microsoft/skills/review/al-ui-review.md @@ -87,15 +87,15 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/ui/field-tooltips-start-with-specifies-and-end-with-period.md", + "id": "microsoft/knowledge/ui/show-caption-on-editable-fields.md", "severity": "minor", - "message": "Field ToolTip is a fragment ('Customer name') — missing the 'Specifies' opener and the terminating period the house-style guidance requires.", + "message": "An editable page field sets ShowCaption = false, removing the visible and accessible label. Leave ShowCaption enabled or use a documented exception pattern.", "location": { "file": "src/Sales/CustomerCard.Page.al", "line": 58 }, "references": [ - { "path": "microsoft/knowledge/ui/field-tooltips-start-with-specifies-and-end-with-period.md" } + { "path": "microsoft/knowledge/ui/show-caption-on-editable-fields.md" } ], "confidence": "high" } @@ -103,4 +103,3 @@ Output conforms to the DO output contract. A populated example: "suppressed": [] } ``` - diff --git a/microsoft/skills/review/al-upgrade-review.md b/microsoft/skills/review/al-upgrade-review.md index 7ccfa4a..3b91d67 100644 --- a/microsoft/skills/review/al-upgrade-review.md +++ b/microsoft/skills/review/al-upgrade-review.md @@ -89,7 +89,7 @@ Output conforms to the DO output contract. A populated example: }, "findings": [ { - "id": "microsoft/knowledge/upgrade/enum-changes-must-be-additive-at-the-end.md", + "id": "microsoft/knowledge/upgrade/enum-values-additive-at-end.md", "severity": "blocker", "message": "A new enum value was inserted at ordinal 1, shifting every subsequent value by one. Rows that store the old ordinal 1 will silently resolve to the new value. Per the referenced guidance, enum values must be appended at the end.", "location": { @@ -97,7 +97,7 @@ Output conforms to the DO output contract. A populated example: "line": 7 }, "references": [ - { "path": "microsoft/knowledge/upgrade/enum-changes-must-be-additive-at-the-end.md" } + { "path": "microsoft/knowledge/upgrade/enum-values-additive-at-end.md" } ], "confidence": "high" } @@ -105,4 +105,3 @@ Output conforms to the DO output contract. A populated example: "suppressed": [] } ``` - diff --git a/skills/read.md b/skills/read.md index 8badb97..6a2080d 100644 --- a/skills/read.md +++ b/skills/read.md @@ -115,7 +115,7 @@ Consumers MUST NOT silently treat missing context as a match. ## Citing a knowledge file -A consumer that produces output referencing a knowledge file MUST cite it by its repo-relative path (for example, `microsoft/knowledge/performance/filter-before-find.md`). Line numbers are not stable references; use the file path only. If a commit SHA is available to the consumer, it SHOULD be included alongside the path. +A consumer that produces output referencing a knowledge file MUST cite it by its repo-relative path (for example, `microsoft/knowledge/performance/apply-filters-before-iterating.md`). Line numbers are not stable references; use the file path only. If a commit SHA is available to the consumer, it SHOULD be included alongside the path. ## Sample files