From 23184480d06e9fc9e029b69d685d5717d7ef03bb Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Thu, 23 Apr 2026 15:47:01 +0200 Subject: [PATCH 1/2] Triage seed knowledge and document admission test for preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove seven knowledge files whose content is generic software-engineering guidance that a capable LLM already applies without BCQuality present (HTTPS-only, secret-leakage-in-errors, no-credentials-in-URLs, silent security-error swallowing, short transaction scope, HTTP timeouts, StrSubstNo-vs-concatenation). These fail the remedial-knowledge premise and dilute the signal of the preview corpus. Strip the "Seed article — domain stewards should expand" banner from ten files that are ready to showcase (AA0232/AA0233 rules, FindSet read-only semantics, SetLoadFields ordering and usage, CalcFields-in-loops, SecretText end-to-end, DataClassification). The banner remains on files that still need domain-steward refinement. Add a "What belongs here" section to the README stating the admission test: a file exists only if a modern LLM would get something wrong or miss something without it. Gives contributors a concrete yes/no filter before they open a PR. --- README.md | 14 +++++++++ .../call-setloadfields-before-filters.md | 2 -- ...ify-every-field-with-dataclassification.md | 2 -- .../add-sift-keys-for-flowfields.md | 2 -- .../performance/avoid-calcfields-in-loops.md | 2 -- .../performance/avoid-findfirst-with-next.md | 2 -- .../performance/filter-before-find.md | 2 -- .../keep-transaction-scope-short.bad.al | 18 ------------ .../keep-transaction-scope-short.good.al | 22 -------------- .../keep-transaction-scope-short.md | 29 ------------------- .../use-findset-readonly-by-default.md | 2 -- .../use-setloadfields-for-partial-records.md | 2 -- ...e-strsubstno-for-message-formatting.bad.al | 7 ----- ...-strsubstno-for-message-formatting.good.al | 9 ------ .../use-strsubstno-for-message-formatting.md | 29 ------------------- ...id-sensitive-data-in-error-messages.bad.al | 14 --------- ...d-sensitive-data-in-error-messages.good.al | 24 --------------- .../avoid-sensitive-data-in-error-messages.md | 29 ------------------- .../do-not-put-credentials-in-urls.bad.al | 10 ------- .../do-not-put-credentials-in-urls.good.al | 13 --------- .../do-not-put-credentials-in-urls.md | 29 ------------------- ...ot-swallow-security-errors-silently.bad.al | 15 ---------- ...t-swallow-security-errors-silently.good.al | 24 --------------- ...do-not-swallow-security-errors-silently.md | 29 ------------------- .../require-https-for-external-calls.bad.al | 10 ------- .../require-https-for-external-calls.good.al | 12 -------- .../require-https-for-external-calls.md | 29 ------------------- .../set-timeouts-for-external-calls.bad.al | 11 ------- .../set-timeouts-for-external-calls.good.al | 12 -------- .../set-timeouts-for-external-calls.md | 29 ------------------- .../use-secrettext-for-credentials.md | 2 -- .../use-secrettext-with-httpclient.md | 2 -- 32 files changed, 14 insertions(+), 424 deletions(-) delete mode 100644 microsoft/knowledge/performance/keep-transaction-scope-short.bad.al delete mode 100644 microsoft/knowledge/performance/keep-transaction-scope-short.good.al delete mode 100644 microsoft/knowledge/performance/keep-transaction-scope-short.md delete mode 100644 microsoft/knowledge/performance/use-strsubstno-for-message-formatting.bad.al delete mode 100644 microsoft/knowledge/performance/use-strsubstno-for-message-formatting.good.al delete mode 100644 microsoft/knowledge/performance/use-strsubstno-for-message-formatting.md delete mode 100644 microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.bad.al delete mode 100644 microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.good.al delete mode 100644 microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.md delete mode 100644 microsoft/knowledge/security/do-not-put-credentials-in-urls.bad.al delete mode 100644 microsoft/knowledge/security/do-not-put-credentials-in-urls.good.al delete mode 100644 microsoft/knowledge/security/do-not-put-credentials-in-urls.md delete mode 100644 microsoft/knowledge/security/do-not-swallow-security-errors-silently.bad.al delete mode 100644 microsoft/knowledge/security/do-not-swallow-security-errors-silently.good.al delete mode 100644 microsoft/knowledge/security/do-not-swallow-security-errors-silently.md delete mode 100644 microsoft/knowledge/security/require-https-for-external-calls.bad.al delete mode 100644 microsoft/knowledge/security/require-https-for-external-calls.good.al delete mode 100644 microsoft/knowledge/security/require-https-for-external-calls.md delete mode 100644 microsoft/knowledge/security/set-timeouts-for-external-calls.bad.al delete mode 100644 microsoft/knowledge/security/set-timeouts-for-external-calls.good.al delete mode 100644 microsoft/knowledge/security/set-timeouts-for-external-calls.md diff --git a/README.md b/README.md index 882e1d1..ebed31b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,20 @@ Quality skills and knowledge for Business Central development. BCQuality is a curated knowledge base and skills library for Business Central. It provides structured, machine-readable guidance that development agents and tools can consume — establishing a consistent quality bar across tooling and teams. +## What belongs here + +BCQuality is a remedial knowledge base. A file exists because a capable LLM **would get something wrong, or miss something, without it** — not because the topic is important. The admission test for a knowledge file is one question: + +> If this file did not exist, would a modern LLM reviewing or generating BC code make a mistake this file would have prevented? + +If the answer is no — the advice is generic software-engineering guidance, or the LLM already knows the BC mechanic in question — the file does not belong here, regardless of how sound the content is. A file earns its place by encoding something BC-specific that LLMs demonstrably get wrong: a CodeCop rule number, a platform API whose semantics the training data gets backwards, a non-obvious ordering rule, a BC property whose default is a footgun. + +Good fit: "`SetLoadFields` must be called before filters, not after" (non-obvious ordering rule). "`FindSet(true)` takes a LockTable and the two-parameter signature is obsolete" (subtle platform behaviour + outdated training data). "CodeCop AA0233 flags `FindFirst … Next` loops" (rule-specific). + +Poor fit: "Use HTTPS instead of HTTP." "Don't hardcode secrets." "Keep transactions short." These are true but any capable LLM already applies them without prompting. + +The practical consequence: when a code-review agent flags something it shouldn't have, or misses something it should have caught, the remedy is a new knowledge file. When it already behaves correctly on a topic, no file is needed. + ## What's in this repo BCQuality contains **knowledge** and **skills**. It does not contain agents. Agents that consume BCQuality ship with [AL-Go](https://github.com/microsoft/AL-Go) and other orchestrators. diff --git a/community/knowledge/performance/call-setloadfields-before-filters.md b/community/knowledge/performance/call-setloadfields-before-filters.md index 90d95e8..78b455b 100644 --- a/community/knowledge/performance/call-setloadfields-before-filters.md +++ b/community/knowledge/performance/call-setloadfields-before-filters.md @@ -9,8 +9,6 @@ application-area: [all] # Call SetLoadFields before filters -> **Seed article.** Ported from BC Code Intelligence to seed the community corpus. Community contributors are invited to expand or refine. - ## Description `SetLoadFields` is folded into the database query that the subsequent `Find`, `FindSet`, or `FindFirst` executes. When it is called after filters have already been applied, the platform either ignores the specification or is forced into an extra round-trip to reload the narrower column set — negating the optimization. The placement rule is simple and absolute: `SetLoadFields` must come first. diff --git a/community/knowledge/security/classify-every-field-with-dataclassification.md b/community/knowledge/security/classify-every-field-with-dataclassification.md index 540f64b..79c27b1 100644 --- a/community/knowledge/security/classify-every-field-with-dataclassification.md +++ b/community/knowledge/security/classify-every-field-with-dataclassification.md @@ -9,8 +9,6 @@ application-area: [all] # Classify every field with DataClassification -> **Seed article.** Ported from BC Code Intelligence to seed the community corpus. Community contributors are invited to expand or refine. - ## Description Every field on every AL table and table extension must carry an explicit `DataClassification` property. The value drives GDPR tooling, data-subject requests, retention policies, and audit reporting — all of which rely on the field metadata to know what data to include, anonymize, or delete. A field with no `DataClassification` defaults to `ToBeClassified`, which is a compliance gap, not a neutral state. diff --git a/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md b/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md index e72eb2a..b2dce78 100644 --- a/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md +++ b/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md @@ -9,8 +9,6 @@ application-area: [all] # Add SIFT keys for FlowField aggregations -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description CodeCop rule AA0232 checks that FlowFields backed by CalcSums or aggregation CalcFormula are supported by a key whose SumIndexFields include the summed field and whose key prefix matches the formula's filter fields. Without a SIFT key the platform falls back to a full aggregation on every read — typically invisible in development and catastrophic in production. diff --git a/microsoft/knowledge/performance/avoid-calcfields-in-loops.md b/microsoft/knowledge/performance/avoid-calcfields-in-loops.md index 1be8cfa..c89509a 100644 --- a/microsoft/knowledge/performance/avoid-calcfields-in-loops.md +++ b/microsoft/knowledge/performance/avoid-calcfields-in-loops.md @@ -9,8 +9,6 @@ application-area: [all] # Do not call CalcFields inside loops -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description CalcFields evaluates one or more FlowFields for the current record by issuing a separate SQL aggregation. Called inside a loop over a record set, it becomes an N+1 problem: one aggregate per row. For any non-trivial set on a ledger-entry-backed FlowField this is orders of magnitude slower than the equivalent batched query. diff --git a/microsoft/knowledge/performance/avoid-findfirst-with-next.md b/microsoft/knowledge/performance/avoid-findfirst-with-next.md index bdff419..92fe53f 100644 --- a/microsoft/knowledge/performance/avoid-findfirst-with-next.md +++ b/microsoft/knowledge/performance/avoid-findfirst-with-next.md @@ -9,8 +9,6 @@ application-area: [all] # Do not pair FindFirst, FindLast, or Get with Next -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description CodeCop rule AA0233 flags loops that start with FindFirst, FindLast, or Get and then call Next. FindFirst and FindLast retrieve a single row and reposition the cursor; calling Next after them forces the platform to re-seek and stream the rest of the set, which is slower than the correct FindSet pattern and signals intent incorrectly to reviewers and the optimizer. diff --git a/microsoft/knowledge/performance/filter-before-find.md b/microsoft/knowledge/performance/filter-before-find.md index 94389b4..271a544 100644 --- a/microsoft/knowledge/performance/filter-before-find.md +++ b/microsoft/knowledge/performance/filter-before-find.md @@ -9,8 +9,6 @@ application-area: [all] # Filter before you find -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description Every call to FindSet, Find, or FindFirst on an unfiltered record variable scans the entire table. On hot tables (ledger entries, value entries, sales invoice lines) a production dataset can easily be millions of rows, so the cost of forgetting a filter is orders of magnitude worse than the cost of applying one. diff --git a/microsoft/knowledge/performance/keep-transaction-scope-short.bad.al b/microsoft/knowledge/performance/keep-transaction-scope-short.bad.al deleted file mode 100644 index 76c7972..0000000 --- a/microsoft/knowledge/performance/keep-transaction-scope-short.bad.al +++ /dev/null @@ -1,18 +0,0 @@ -codeunit 50128 "Perf Sample TxnScope Bad" -{ - procedure ImportCustomers(var Source: List of [Text]) - var - Customer: Record Customer; - HttpClient: HttpClient; - HttpResponse: HttpResponseMessage; - Row: Text; - begin - foreach Row in Source do begin - // external call inside the write transaction - HttpClient.Get('https://example.com/validate?row=' + Row, HttpResponse); - Customer.Init(); - // ... populate from Row ... - Customer.Insert(true); - end; - end; -} diff --git a/microsoft/knowledge/performance/keep-transaction-scope-short.good.al b/microsoft/knowledge/performance/keep-transaction-scope-short.good.al deleted file mode 100644 index c875f39..0000000 --- a/microsoft/knowledge/performance/keep-transaction-scope-short.good.al +++ /dev/null @@ -1,22 +0,0 @@ -codeunit 50123 "Perf Sample TxnScope Good" -{ - procedure ImportCustomers(var Source: List of [Text]) - var - Prepared: Record Customer temporary; - Customer: Record Customer; - begin - // read, validate, and shape outside the transaction - PrepareRows(Source, Prepared); - - // transaction starts here: only Insert/Modify calls - if Prepared.FindSet() then - repeat - Customer := Prepared; - Customer.Insert(true); - until Prepared.Next() = 0; - end; - - local procedure PrepareRows(var Source: List of [Text]; var Prepared: Record Customer temporary) - begin - end; -} diff --git a/microsoft/knowledge/performance/keep-transaction-scope-short.md b/microsoft/knowledge/performance/keep-transaction-scope-short.md deleted file mode 100644 index 704cffc..0000000 --- a/microsoft/knowledge/performance/keep-transaction-scope-short.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: performance -keywords: [transaction, lock, scope, contention] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Keep transaction scope short - -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -Every write operation runs inside a transaction that holds locks until the transaction ends. Long transactions increase blocking, deadlocks, and timeouts for other sessions. The same work split across narrower transactions typically completes faster under load because it holds locks for less time. - -## Best Practice - -Perform data reads, calculations, and external integrations outside the transaction whenever possible. Enter the writing phase with all inputs computed, execute the minimum set of Insert, Modify, and Delete calls, and exit. If you have a long-running batch, split it into checkpoints at safe boundaries (see avoid-commit-inside-loops). - -See sample: `keep-transaction-scope-short.good.al`. - -## Anti Pattern - -Opening a transaction, then performing external web-service calls, heavy report runs, or user-facing dialogs while the locks are held, suspends every other session that needs the same rows for as long as the external operation takes. - -See sample: `keep-transaction-scope-short.bad.al`. - diff --git a/microsoft/knowledge/performance/use-findset-readonly-by-default.md b/microsoft/knowledge/performance/use-findset-readonly-by-default.md index 64cbe72..e3d3bba 100644 --- a/microsoft/knowledge/performance/use-findset-readonly-by-default.md +++ b/microsoft/knowledge/performance/use-findset-readonly-by-default.md @@ -9,8 +9,6 @@ application-area: [all] # Use FindSet in read-only mode by default -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description FindSet has two modes: FindSet() and FindSet(false) are read-only and take no write lock; FindSet(true) calls LockTable before fetching. Write locks are expensive and hold for the remainder of the transaction, so passing `true` when you do not intend to modify the records increases contention under load. diff --git a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md index e112fc7..bcbf064 100644 --- a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md +++ b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md @@ -9,8 +9,6 @@ application-area: [all] # Use SetLoadFields for partial records -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description SetLoadFields instructs the platform to hydrate only the listed fields on a record variable. On wide tables, or tables with BLOB or media fields, the difference is substantial: a Sales Invoice Line has dozens of fields and loading all of them for every row of a large set is wasted bandwidth. Primary key fields, SystemId, and system audit fields are always loaded automatically. SetLoadFields works only with FieldClass = Normal; FlowFields and FlowFilters cannot be partial-loaded. diff --git a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.bad.al b/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.bad.al deleted file mode 100644 index bcfa7d0..0000000 --- a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.bad.al +++ /dev/null @@ -1,7 +0,0 @@ -codeunit 50137 "Perf Sample StrSubstNo Bad" -{ - procedure CustomerGreeting(var Customer: Record Customer): Text - begin - exit('Hello, ' + Customer.Name + ' (' + Customer."No." + ')'); - end; -} diff --git a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.good.al b/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.good.al deleted file mode 100644 index 87883e8..0000000 --- a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.good.al +++ /dev/null @@ -1,9 +0,0 @@ -codeunit 50136 "Perf Sample StrSubstNo Good" -{ - procedure CustomerGreeting(var Customer: Record Customer): Text - var - GreetingLbl: Label 'Hello, %1 (%2)'; - begin - exit(StrSubstNo(GreetingLbl, Customer.Name, Customer."No.")); - end; -} diff --git a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.md b/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.md deleted file mode 100644 index 6e2b3f6..0000000 --- a/microsoft/knowledge/performance/use-strsubstno-for-message-formatting.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: performance -keywords: [strsubstno, string, concatenation, format] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Use StrSubstNo for message formatting - -> **Seed article.** Converted from an existing performance-review prompt to bootstrap the BCQuality performance corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -StrSubstNo formats values into a placeholder template in a single call. Manual concatenation with `+` produces a chain of intermediate strings, each allocated and discarded, and mixes formatting rules inconsistently across locales. The performance difference per call is small; repeated inside a tight loop it is noticeable. - -## Best Practice - -Declare the template as a Label (so it can be localized) and format with StrSubstNo. Pass values in the order the placeholders expect; StrSubstNo handles locale-sensitive conversions consistently. - -See sample: `use-strsubstno-for-message-formatting.good.al`. - -## Anti Pattern - -Building a user-facing string by concatenating record field values with string literals ignores locale rules and allocates more than necessary. - -See sample: `use-strsubstno-for-message-formatting.bad.al`. - diff --git a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.bad.al b/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.bad.al deleted file mode 100644 index 0a78e75..0000000 --- a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.bad.al +++ /dev/null @@ -1,14 +0,0 @@ -codeunit 50225 "Sec Sample ErrorDisclosure Bad" -{ - procedure Connect() - begin - if not TryConnect() then - Error('Failed to connect to Server=PROD-SQL01;Database=NAV;User=svc_admin: %1', GetLastErrorText()); - end; - - [TryFunction] - local procedure TryConnect() - begin - // ... - end; -} diff --git a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.good.al b/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.good.al deleted file mode 100644 index 6fb6267..0000000 --- a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.good.al +++ /dev/null @@ -1,24 +0,0 @@ -codeunit 50224 "Sec Sample ErrorDisclosure Good" -{ - var - ConnectionFailedErr: Label 'Connection to the external service failed. Contact your administrator.'; - - procedure Connect() - begin - if not TryConnect() then begin - LogConnectionFailure(GetLastErrorText()); - Error(ConnectionFailedErr); - end; - end; - - [TryFunction] - local procedure TryConnect() - begin - // ... - end; - - local procedure LogConnectionFailure(Detail: Text) - begin - // Route to controlled logging (Session.LogMessage, activity log, etc.). - end; -} diff --git a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.md b/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.md deleted file mode 100644 index 75d78cb..0000000 --- a/microsoft/knowledge/security/avoid-sensitive-data-in-error-messages.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: security -keywords: [error, disclosure, logging, label] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Avoid sensitive data in error messages - -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -Errors surfaced to end users are routinely forwarded to support systems, captured in bug reports, and exported to telemetry. Server names, database names, usernames, connection strings, file paths, and stack excerpts in an end-user error message leak infrastructure detail to untrusted consumers and help an attacker map the environment. - -## Best Practice - -Raise end-user errors using localized Labels that describe the condition without naming infrastructure. Emit the actual detail (exception text, endpoint, correlation id) through the application's internal logging channel, where audience and retention are controlled. - -See sample: `avoid-sensitive-data-in-error-messages.good.al`. - -## Anti Pattern - -Error('Failed to connect to Server=PROD-SQL01;Database=NAV;User=admin: %1', Ex.Message); — every support ticket now carries the server name, database name, and service account. - -See sample: `avoid-sensitive-data-in-error-messages.bad.al`. - diff --git a/microsoft/knowledge/security/do-not-put-credentials-in-urls.bad.al b/microsoft/knowledge/security/do-not-put-credentials-in-urls.bad.al deleted file mode 100644 index 12476bb..0000000 --- a/microsoft/knowledge/security/do-not-put-credentials-in-urls.bad.al +++ /dev/null @@ -1,10 +0,0 @@ -codeunit 50223 "Sec Sample UrlCreds Bad" -{ - procedure Call(ApiKey: Text) - var - Client: HttpClient; - Response: HttpResponseMessage; - begin - Client.Get('https://api.example.com/v1/items?api_key=' + ApiKey, Response); - end; -} diff --git a/microsoft/knowledge/security/do-not-put-credentials-in-urls.good.al b/microsoft/knowledge/security/do-not-put-credentials-in-urls.good.al deleted file mode 100644 index c26dd29..0000000 --- a/microsoft/knowledge/security/do-not-put-credentials-in-urls.good.al +++ /dev/null @@ -1,13 +0,0 @@ -codeunit 50222 "Sec Sample UrlCreds Good" -{ - procedure Call(ApiKey: SecretText) - var - Client: HttpClient; - Response: HttpResponseMessage; - AuthHeader: SecretText; - begin - AuthHeader := SecretStrSubstNo('Bearer %1', ApiKey); - Client.DefaultRequestHeaders.Add('Authorization', AuthHeader); - Client.Get('https://api.example.com/v1/items', Response); - end; -} diff --git a/microsoft/knowledge/security/do-not-put-credentials-in-urls.md b/microsoft/knowledge/security/do-not-put-credentials-in-urls.md deleted file mode 100644 index dc3843e..0000000 --- a/microsoft/knowledge/security/do-not-put-credentials-in-urls.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: security -keywords: [url, query-string, credentials, logging] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Do not put credentials in URLs - -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -URL query strings and path segments are routinely captured in web-server access logs, browser history, proxy logs, platform telemetry, and exception traces. A credential placed anywhere in the URL therefore persists across systems the extension does not control, and is typically retained far longer than the secret's intended lifetime. - -## Best Practice - -Transport credentials in Authorization headers, carried as SecretText end-to-end (see use-secrettext-with-httpclient). Where the URI itself must carry a secret (for example, a pre-signed URL), build it with SecretStrSubstNo and pass it via SetSecretRequestUri so it is never materialized as Text. - -See sample: `do-not-put-credentials-in-urls.good.al`. - -## Anti Pattern - -Appending '?api_key=' + Key to a request URL, or embedding a token in a path segment, then calling HttpClient.Get with the resulting Text URL. - -See sample: `do-not-put-credentials-in-urls.bad.al`. - diff --git a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.bad.al b/microsoft/knowledge/security/do-not-swallow-security-errors-silently.bad.al deleted file mode 100644 index 923df57..0000000 --- a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.bad.al +++ /dev/null @@ -1,15 +0,0 @@ -codeunit 50227 "Sec Sample SwallowErr Bad" -{ - procedure Authenticate(): Boolean - begin - if not TryAuthenticate() then - exit(false); - exit(true); - end; - - [TryFunction] - local procedure TryAuthenticate() - begin - // ... - end; -} diff --git a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.good.al b/microsoft/knowledge/security/do-not-swallow-security-errors-silently.good.al deleted file mode 100644 index 2887312..0000000 --- a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.good.al +++ /dev/null @@ -1,24 +0,0 @@ -codeunit 50226 "Sec Sample SwallowErr Good" -{ - procedure Authenticate(): Boolean - begin - if TryAuthenticate() then - exit(true); - - LogAuthFailure(GetLastErrorText()); - exit(false); - end; - - [TryFunction] - local procedure TryAuthenticate() - begin - // ... - end; - - local procedure LogAuthFailure(Detail: Text) - begin - Session.LogMessage('SEC0001', 'Authentication failed', Verbosity::Warning, - DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, - 'Detail', Detail); - end; -} diff --git a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.md b/microsoft/knowledge/security/do-not-swallow-security-errors-silently.md deleted file mode 100644 index fa4af42..0000000 --- a/microsoft/knowledge/security/do-not-swallow-security-errors-silently.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: security -keywords: [tryfunction, logging, audit, error] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Do not swallow security errors silently - -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -Authentication failures, permission denials, and unexpected error paths in security-relevant code are the signals a reviewer or incident responder needs to see. A TryFunction whose failure is ignored without logging turns an attack or a misconfiguration into silent bad behaviour: the call returns false, the caller moves on, and no record of the event survives. - -## Best Practice - -Use TryFunctions to contain errors around security-relevant work, but always log the failure (category, GetLastErrorText, and enough context to identify the operation) before deciding whether to surface a user-facing error. Never discard a caught security error without a trace. - -See sample: `do-not-swallow-security-errors-silently.good.al`. - -## Anti Pattern - -`if not TryAuthenticate() then exit;` with no logging and no user-facing error. An authentication-bypass attempt, a revoked credential, and a transient network glitch are now indistinguishable. - -See sample: `do-not-swallow-security-errors-silently.bad.al`. - diff --git a/microsoft/knowledge/security/require-https-for-external-calls.bad.al b/microsoft/knowledge/security/require-https-for-external-calls.bad.al deleted file mode 100644 index 4edf291..0000000 --- a/microsoft/knowledge/security/require-https-for-external-calls.bad.al +++ /dev/null @@ -1,10 +0,0 @@ -codeunit 50219 "Sec Sample Https Bad" -{ - procedure CallExternal() - var - Client: HttpClient; - Response: HttpResponseMessage; - begin - Client.Get('http://api.example.com/data', Response); - end; -} diff --git a/microsoft/knowledge/security/require-https-for-external-calls.good.al b/microsoft/knowledge/security/require-https-for-external-calls.good.al deleted file mode 100644 index 6ae7639..0000000 --- a/microsoft/knowledge/security/require-https-for-external-calls.good.al +++ /dev/null @@ -1,12 +0,0 @@ -codeunit 50218 "Sec Sample Https Good" -{ - procedure CallExternal(Endpoint: Text) - var - Client: HttpClient; - Response: HttpResponseMessage; - begin - if not Endpoint.StartsWith('https://') then - Error('Only HTTPS endpoints are allowed.'); - Client.Get(Endpoint, Response); - end; -} diff --git a/microsoft/knowledge/security/require-https-for-external-calls.md b/microsoft/knowledge/security/require-https-for-external-calls.md deleted file mode 100644 index a026015..0000000 --- a/microsoft/knowledge/security/require-https-for-external-calls.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: security -keywords: [https, httpclient, tls, plaintext] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Require HTTPS for external calls - -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -HttpClient can issue requests over plaintext HTTP as easily as over HTTPS. A request sent over http:// is transmitted unencrypted, exposing the full URL (including query string), the request headers (including Authorization), and the bodies of both request and response to any on-path observer. This holds even when the payload itself is not marked sensitive — request signatures and session tokens are routinely captured and replayed. - -## Best Practice - -Call external services exclusively over https://. When the destination is configurable, validate at runtime that the scheme is https before issuing the request, and fail closed with a clear (non-disclosing) error otherwise. - -See sample: `require-https-for-external-calls.good.al`. - -## Anti Pattern - -Issuing HttpClient.Get('http://...'), or accepting an arbitrary user-supplied URL and passing it straight to HttpClient without scheme validation. - -See sample: `require-https-for-external-calls.bad.al`. - diff --git a/microsoft/knowledge/security/set-timeouts-for-external-calls.bad.al b/microsoft/knowledge/security/set-timeouts-for-external-calls.bad.al deleted file mode 100644 index 2068c1f..0000000 --- a/microsoft/knowledge/security/set-timeouts-for-external-calls.bad.al +++ /dev/null @@ -1,11 +0,0 @@ -codeunit 50221 "Sec Sample Timeout Bad" -{ - procedure CallExternal() - var - Client: HttpClient; - Response: HttpResponseMessage; - begin - // No Timeout set; a hung endpoint stalls the caller. - Client.Get('https://api.example.com/data', Response); - end; -} diff --git a/microsoft/knowledge/security/set-timeouts-for-external-calls.good.al b/microsoft/knowledge/security/set-timeouts-for-external-calls.good.al deleted file mode 100644 index ad9910c..0000000 --- a/microsoft/knowledge/security/set-timeouts-for-external-calls.good.al +++ /dev/null @@ -1,12 +0,0 @@ -codeunit 50220 "Sec Sample Timeout Good" -{ - procedure CallExternal() - var - Client: HttpClient; - Response: HttpResponseMessage; - begin - Client.Timeout := 10000; // 10 seconds - if not Client.Get('https://api.example.com/data', Response) then - Error('External service is unavailable.'); - end; -} diff --git a/microsoft/knowledge/security/set-timeouts-for-external-calls.md b/microsoft/knowledge/security/set-timeouts-for-external-calls.md deleted file mode 100644 index 28d83b0..0000000 --- a/microsoft/knowledge/security/set-timeouts-for-external-calls.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -bc-version: [26..28] -domain: security -keywords: [timeout, httpclient, availability, dos] -technologies: [al] -countries: [w1] -application-area: [all] ---- - -# Set timeouts for external calls - -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - -## Description - -An HttpClient with no explicit timeout relies on defaults that may be long enough for a hung or slow endpoint to block a user session or a background task for minutes. A dependency that degrades therefore degrades the caller, and an intentionally slow endpoint is a cheap denial-of-service vector against the extension. - -## Best Practice - -Set HttpClient.Timeout to a bounded value (seconds, not minutes) that reflects the SLA of the dependency. Handle the timeout error without leaking endpoint details to end users (see avoid-sensitive-data-in-error-messages). - -See sample: `set-timeouts-for-external-calls.good.al`. - -## Anti Pattern - -Issuing HttpClient requests without setting Timeout and without a timeout-handling branch. A slow dependency now has an unbounded blast radius inside the extension. - -See sample: `set-timeouts-for-external-calls.bad.al`. - diff --git a/microsoft/knowledge/security/use-secrettext-for-credentials.md b/microsoft/knowledge/security/use-secrettext-for-credentials.md index 991f2d0..0401d9a 100644 --- a/microsoft/knowledge/security/use-secrettext-for-credentials.md +++ b/microsoft/knowledge/security/use-secrettext-for-credentials.md @@ -9,8 +9,6 @@ application-area: [all] # Use SecretText for credentials -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description SecretText is a compile-time-checked AL type for credentials, API keys, tokens, and similar sensitive values. The compiler rejects literal assignments to SecretText and blocks implicit conversion back to Text or Code, which prevents many accidental disclosures via logs, errors, and the debugger (regular and snapshot). A SecretText value remains opaque throughout its lifetime. diff --git a/microsoft/knowledge/security/use-secrettext-with-httpclient.md b/microsoft/knowledge/security/use-secrettext-with-httpclient.md index 2acb268..6a0ed9a 100644 --- a/microsoft/knowledge/security/use-secrettext-with-httpclient.md +++ b/microsoft/knowledge/security/use-secrettext-with-httpclient.md @@ -9,8 +9,6 @@ application-area: [all] # Use SecretText with HttpClient -> **Seed article.** Converted from an existing security-review prompt to bootstrap the BCQuality security corpus. Domain stewards should expand, restructure, and refine as needed. - ## Description HttpRequestMessage, HttpHeaders, and HttpContent expose SecretText overloads so credentials never have to be converted back to Text to be sent. Key APIs: HttpRequestMessage.SetSecretRequestUri (for URIs containing secrets), HttpHeaders.Add(name, SecretText) for authorization headers, HttpHeaders.ContainsSecret to probe secret-valued headers, HttpContent.WriteFrom(SecretText) for request bodies, and HttpContent.ReadAs(SecretText) to pull response bodies into a secret destination. From 9a4198eb28a6f7cbb5b7db326acbe29fe391723a Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Thu, 23 Apr 2026 16:00:03 +0200 Subject: [PATCH 2/2] Add [all] sentinel to bc-version; apply to version-agnostic knowledge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the corpus — FindSet/SetLoadFields/CalcFields patterns, permission sets, SingleInstance codeunits, DataClassification, IsolatedStorage, transaction scope, SecretText — describes BC platform behaviour that is identical across supported versions. The seed [26..28] range on every file implied a version-specificity the content does not actually have, and there was no way to express "applies to every version" in the schema the way [w1] and [all] already do for countries and application-area. Extend the v1 schema with a universal sentinel for bc-version, parallel to the sentinels already defined for the other dimensions: bc-version: [all] # applies to every BC version [all] is mutually exclusive with explicit versions. Range shorthand ([26..28]) and explicit lists ([26, 27, 28]) continue to work for files genuinely tied to a version-gated API or deprecation. Update read.md (field definition, matching semantics, partial-context rule), write.md (default to [all], use ranges only with a concrete reason), README.md (frontmatter example), and the CI validator. All forty existing knowledge files and the three action skills convert to [all]; none of the current content is version-gated. Validator passes. --- .github/scripts/validate_frontmatter.py | 15 ++++++++++++--- README.md | 2 +- ...owing-globals-in-singleinstance-subscribers.md | 2 +- .../call-setloadfields-before-filters.md | 2 +- ...hoose-maintainsiftindex-by-read-write-ratio.md | 2 +- ...load-common-fields-before-branching-on-case.md | 2 +- ...-only-primary-key-fields-for-reference-work.md | 2 +- .../omit-filter-only-fields-from-setloadfields.md | 2 +- .../order-case-branches-by-frequency.md | 2 +- .../use-deleteall-for-filtered-bulk-deletion.md | 2 +- ...lassify-every-field-with-dataclassification.md | 2 +- .../compose-permission-sets-with-included-sets.md | 2 +- ...not-grant-rights-beyond-a-users-entitlement.md | 2 +- .../guard-bulk-operations-with-istemporary.md | 2 +- ...auth2-over-api-keys-for-external-http-calls.md | 2 +- .../protect-sensitive-data-in-temporary-tables.md | 2 +- .../performance/add-sift-keys-for-flowfields.md | 2 +- .../performance/avoid-calcfields-in-loops.md | 2 +- .../performance/avoid-commit-inside-loops.md | 2 +- .../performance/avoid-findfirst-with-next.md | 2 +- .../avoid-user-interaction-in-transactions.md | 2 +- .../knowledge/performance/filter-before-find.md | 2 +- .../keep-event-subscribers-lightweight.md | 2 +- .../performance/only-fetch-records-you-use.md | 2 +- .../prefer-direct-record-over-recordref.md | 2 +- .../prefer-get-for-primary-key-lookups.md | 2 +- .../set-current-key-to-match-filters.md | 2 +- .../use-addloadfields-in-report-layouts.md | 2 +- .../use-calcsums-for-flowfield-totals.md | 2 +- .../use-findset-readonly-by-default.md | 2 +- .../performance/use-findset-with-next.md | 2 +- .../use-insert-false-when-skipping-triggers.md | 2 +- .../use-isempty-for-existence-checks.md | 2 +- .../use-setloadfields-for-partial-records.md | 2 +- .../use-single-instance-codeunits-for-caching.md | 2 +- .../use-temporary-tables-for-intermediate-data.md | 2 +- .../compose-secrets-with-secretstrsubstno.md | 2 +- ...t-expose-sensitive-data-in-event-publishers.md | 2 +- .../follow-least-privilege-in-permission-sets.md | 2 +- .../security/never-hardcode-secrets-in-al.md | 2 +- ...efer-azure-key-vault-for-production-secrets.md | 2 +- ...se-indirect-permissions-for-elevated-access.md | 2 +- ...nherent-permissions-to-grant-minimal-access.md | 2 +- ...ated-storage-for-module-and-company-secrets.md | 2 +- .../use-nondebuggable-when-parsing-secrets.md | 2 +- .../security/use-secrettext-for-credentials.md | 2 +- .../security/use-secrettext-with-httpclient.md | 2 +- microsoft/skills/al-code-review.md | 2 +- microsoft/skills/al-performance-review.md | 2 +- microsoft/skills/al-security-review.md | 2 +- skills/read.md | 11 ++++++----- skills/write.md | 2 +- 52 files changed, 68 insertions(+), 58 deletions(-) diff --git a/.github/scripts/validate_frontmatter.py b/.github/scripts/validate_frontmatter.py index df579e2..d969cef 100644 --- a/.github/scripts/validate_frontmatter.py +++ b/.github/scripts/validate_frontmatter.py @@ -145,10 +145,19 @@ def is_non_empty_list_of_str(value: Any) -> bool: return isinstance(value, list) and len(value) > 0 and all(isinstance(v, str) and v for v in value) -def expand_bc_version(value: Any) -> tuple[list[int] | None, str | None]: - """Return (expanded-list, error-message). One of the two is None.""" +def expand_bc_version(value: Any) -> tuple[list[int] | str | None, str | None]: + """Return (expanded, error-message). One of the two is None. + + For the universal sentinel ["all"], `expanded` is the string "all". + Otherwise it is the expanded list of version integers. + """ if not isinstance(value, list) or not value: return None, "must be a non-empty list" + # Case 0: universal sentinel + if len(value) == 1 and value[0] == "all": + return "all", None + if "all" in value: + return None, "'all' is mutually exclusive with explicit versions" # Case 1: all integers if all(isinstance(v, int) and not isinstance(v, bool) for v in value): if any(v <= 0 for v in value): @@ -162,7 +171,7 @@ def expand_bc_version(value: Any) -> tuple[list[int] | None, str | None]: if start > end: return None, f"range '{value[0]}' is not ascending" return list(range(start, end + 1)), None - return None, "must be a list of integers or a single-element range shorthand like [26..28]" + return None, "must be [all], a list of integers, or a single-element range shorthand like [26..28]" def headings_in_order(body: str) -> list[tuple[str, int]]: diff --git a/README.md b/README.md index ebed31b..b75a49d 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Every knowledge file is a markdown file with mandatory YAML frontmatter. Files t ```yaml --- -bc-version: [26..28] # BC versions this applies to +bc-version: [all] # or [26..28] for version-gated guidance domain: performance # security | performance | ux | telemetry | ... keywords: [query, filtering, partial] # free-text tags for retrieval technologies: [al] # al | javascript | powershell | ... diff --git a/community/knowledge/performance/avoid-growing-globals-in-singleinstance-subscribers.md b/community/knowledge/performance/avoid-growing-globals-in-singleinstance-subscribers.md index d2de14e..ed95723 100644 --- a/community/knowledge/performance/avoid-growing-globals-in-singleinstance-subscribers.md +++ b/community/knowledge/performance/avoid-growing-globals-in-singleinstance-subscribers.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [singleinstance, subscriber, event, memory, session] technologies: [al] diff --git a/community/knowledge/performance/call-setloadfields-before-filters.md b/community/knowledge/performance/call-setloadfields-before-filters.md index 78b455b..01cc1cc 100644 --- a/community/knowledge/performance/call-setloadfields-before-filters.md +++ b/community/knowledge/performance/call-setloadfields-before-filters.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setloadfields, placement, filter, setrange, query-plan] technologies: [al] diff --git a/community/knowledge/performance/choose-maintainsiftindex-by-read-write-ratio.md b/community/knowledge/performance/choose-maintainsiftindex-by-read-write-ratio.md index 787c344..e0bc686 100644 --- a/community/knowledge/performance/choose-maintainsiftindex-by-read-write-ratio.md +++ b/community/knowledge/performance/choose-maintainsiftindex-by-read-write-ratio.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [maintainsiftindex, sift, calcsums, flowfield, write-cost] technologies: [al] diff --git a/community/knowledge/performance/load-common-fields-before-branching-on-case.md b/community/knowledge/performance/load-common-fields-before-branching-on-case.md index 1cc8492..bee6065 100644 --- a/community/knowledge/performance/load-common-fields-before-branching-on-case.md +++ b/community/knowledge/performance/load-common-fields-before-branching-on-case.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setloadfields, case, conditional, branch, field-loading] technologies: [al] diff --git a/community/knowledge/performance/load-only-primary-key-fields-for-reference-work.md b/community/knowledge/performance/load-only-primary-key-fields-for-reference-work.md index f13f444..3b4b8f9 100644 --- a/community/knowledge/performance/load-only-primary-key-fields-for-reference-work.md +++ b/community/knowledge/performance/load-only-primary-key-fields-for-reference-work.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setloadfields, primary-key, reference, existence-check, memory] technologies: [al] diff --git a/community/knowledge/performance/omit-filter-only-fields-from-setloadfields.md b/community/knowledge/performance/omit-filter-only-fields-from-setloadfields.md index 3674836..017b567 100644 --- a/community/knowledge/performance/omit-filter-only-fields-from-setloadfields.md +++ b/community/knowledge/performance/omit-filter-only-fields-from-setloadfields.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setloadfields, filter, field-exclusion, index] technologies: [al] diff --git a/community/knowledge/performance/order-case-branches-by-frequency.md b/community/knowledge/performance/order-case-branches-by-frequency.md index 90518f6..9f78100 100644 --- a/community/knowledge/performance/order-case-branches-by-frequency.md +++ b/community/knowledge/performance/order-case-branches-by-frequency.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [case, branch, frequency, control-flow, hot-path] technologies: [al] diff --git a/community/knowledge/performance/use-deleteall-for-filtered-bulk-deletion.md b/community/knowledge/performance/use-deleteall-for-filtered-bulk-deletion.md index 194dae4..101672a 100644 --- a/community/knowledge/performance/use-deleteall-for-filtered-bulk-deletion.md +++ b/community/knowledge/performance/use-deleteall-for-filtered-bulk-deletion.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [deleteall, bulk-delete, sql, ondelete, trigger-bypass] technologies: [al] diff --git a/community/knowledge/security/classify-every-field-with-dataclassification.md b/community/knowledge/security/classify-every-field-with-dataclassification.md index 79c27b1..bca3219 100644 --- a/community/knowledge/security/classify-every-field-with-dataclassification.md +++ b/community/knowledge/security/classify-every-field-with-dataclassification.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [dataclassification, gdpr, privacy, euii, compliance] technologies: [al] diff --git a/community/knowledge/security/compose-permission-sets-with-included-sets.md b/community/knowledge/security/compose-permission-sets-with-included-sets.md index 3de55d0..b072a67 100644 --- a/community/knowledge/security/compose-permission-sets-with-included-sets.md +++ b/community/knowledge/security/compose-permission-sets-with-included-sets.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [permissionset, includedpermissionsets, assignable, composition, role] technologies: [al] 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 dfc2666..fe68d58 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 @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [entitlement, permissionset, license, clipping, sandbox-drift] technologies: [al] diff --git a/community/knowledge/security/guard-bulk-operations-with-istemporary.md b/community/knowledge/security/guard-bulk-operations-with-istemporary.md index baf2bd8..b3559a6 100644 --- a/community/knowledge/security/guard-bulk-operations-with-istemporary.md +++ b/community/knowledge/security/guard-bulk-operations-with-istemporary.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [istemporary, deleteall, modifyall, safeguard, precondition] technologies: [al] diff --git a/community/knowledge/security/prefer-oauth2-over-api-keys-for-external-http-calls.md b/community/knowledge/security/prefer-oauth2-over-api-keys-for-external-http-calls.md index ab30675..7ae5e24 100644 --- a/community/knowledge/security/prefer-oauth2-over-api-keys-for-external-http-calls.md +++ b/community/knowledge/security/prefer-oauth2-over-api-keys-for-external-http-calls.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [oauth2, api-key, authentication, httpclient, token-refresh] technologies: [al] diff --git a/community/knowledge/security/protect-sensitive-data-in-temporary-tables.md b/community/knowledge/security/protect-sensitive-data-in-temporary-tables.md index e6d475d..37ce915 100644 --- a/community/knowledge/security/protect-sensitive-data-in-temporary-tables.md +++ b/community/knowledge/security/protect-sensitive-data-in-temporary-tables.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [temporary-table, data-protection, permission, cleanup] technologies: [al] diff --git a/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md b/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md index b2dce78..1a1843f 100644 --- a/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md +++ b/microsoft/knowledge/performance/add-sift-keys-for-flowfields.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [sift, sumindexfields, flowfield, key, aa0232] technologies: [al] diff --git a/microsoft/knowledge/performance/avoid-calcfields-in-loops.md b/microsoft/knowledge/performance/avoid-calcfields-in-loops.md index c89509a..4a94f47 100644 --- a/microsoft/knowledge/performance/avoid-calcfields-in-loops.md +++ b/microsoft/knowledge/performance/avoid-calcfields-in-loops.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [calcfields, flowfield, loop, n-plus-one] technologies: [al] diff --git a/microsoft/knowledge/performance/avoid-commit-inside-loops.md b/microsoft/knowledge/performance/avoid-commit-inside-loops.md index f8e3943..fefd4f3 100644 --- a/microsoft/knowledge/performance/avoid-commit-inside-loops.md +++ b/microsoft/knowledge/performance/avoid-commit-inside-loops.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [commit, loop, transaction, lock] technologies: [al] diff --git a/microsoft/knowledge/performance/avoid-findfirst-with-next.md b/microsoft/knowledge/performance/avoid-findfirst-with-next.md index 92fe53f..267aaac 100644 --- a/microsoft/knowledge/performance/avoid-findfirst-with-next.md +++ b/microsoft/knowledge/performance/avoid-findfirst-with-next.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [findfirst, findlast, get, next, aa0233] technologies: [al] diff --git a/microsoft/knowledge/performance/avoid-user-interaction-in-transactions.md b/microsoft/knowledge/performance/avoid-user-interaction-in-transactions.md index a3a9041..bf0895f 100644 --- a/microsoft/knowledge/performance/avoid-user-interaction-in-transactions.md +++ b/microsoft/knowledge/performance/avoid-user-interaction-in-transactions.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [confirm, strmenu, message, transaction, dialog] technologies: [al] diff --git a/microsoft/knowledge/performance/filter-before-find.md b/microsoft/knowledge/performance/filter-before-find.md index 271a544..f267761 100644 --- a/microsoft/knowledge/performance/filter-before-find.md +++ b/microsoft/knowledge/performance/filter-before-find.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [filter, setrange, setfilter, findset, scan] technologies: [al] diff --git a/microsoft/knowledge/performance/keep-event-subscribers-lightweight.md b/microsoft/knowledge/performance/keep-event-subscribers-lightweight.md index 25e2ceb..5079bf2 100644 --- a/microsoft/knowledge/performance/keep-event-subscribers-lightweight.md +++ b/microsoft/knowledge/performance/keep-event-subscribers-lightweight.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [event, subscriber, publisher, extension] technologies: [al] diff --git a/microsoft/knowledge/performance/only-fetch-records-you-use.md b/microsoft/knowledge/performance/only-fetch-records-you-use.md index 8c45801..b0d7a61 100644 --- a/microsoft/knowledge/performance/only-fetch-records-you-use.md +++ b/microsoft/knowledge/performance/only-fetch-records-you-use.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [findset, get, aa0175, wasted-fetch, read] technologies: [al] diff --git a/microsoft/knowledge/performance/prefer-direct-record-over-recordref.md b/microsoft/knowledge/performance/prefer-direct-record-over-recordref.md index 9916ef8..dce8733 100644 --- a/microsoft/knowledge/performance/prefer-direct-record-over-recordref.md +++ b/microsoft/knowledge/performance/prefer-direct-record-over-recordref.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [recordref, fieldref, dynamic, reflection] technologies: [al] diff --git a/microsoft/knowledge/performance/prefer-get-for-primary-key-lookups.md b/microsoft/knowledge/performance/prefer-get-for-primary-key-lookups.md index 6102d47..8dfa92b 100644 --- a/microsoft/knowledge/performance/prefer-get-for-primary-key-lookups.md +++ b/microsoft/knowledge/performance/prefer-get-for-primary-key-lookups.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [get, findfirst, primary-key, lookup] technologies: [al] diff --git a/microsoft/knowledge/performance/set-current-key-to-match-filters.md b/microsoft/knowledge/performance/set-current-key-to-match-filters.md index 87b4bf9..d3525aa 100644 --- a/microsoft/knowledge/performance/set-current-key-to-match-filters.md +++ b/microsoft/knowledge/performance/set-current-key-to-match-filters.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setcurrentkey, key, index, sort, filter] technologies: [al] diff --git a/microsoft/knowledge/performance/use-addloadfields-in-report-layouts.md b/microsoft/knowledge/performance/use-addloadfields-in-report-layouts.md index 4331947..0073b07 100644 --- a/microsoft/knowledge/performance/use-addloadfields-in-report-layouts.md +++ b/microsoft/knowledge/performance/use-addloadfields-in-report-layouts.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [report, addloadfields, ondatapreitem, layout, partial-record] technologies: [al] diff --git a/microsoft/knowledge/performance/use-calcsums-for-flowfield-totals.md b/microsoft/knowledge/performance/use-calcsums-for-flowfield-totals.md index 19a2e2c..f934cae 100644 --- a/microsoft/knowledge/performance/use-calcsums-for-flowfield-totals.md +++ b/microsoft/knowledge/performance/use-calcsums-for-flowfield-totals.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [calcsums, sift, sum, aggregate, totals] technologies: [al] diff --git a/microsoft/knowledge/performance/use-findset-readonly-by-default.md b/microsoft/knowledge/performance/use-findset-readonly-by-default.md index e3d3bba..602dba6 100644 --- a/microsoft/knowledge/performance/use-findset-readonly-by-default.md +++ b/microsoft/knowledge/performance/use-findset-readonly-by-default.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [findset, lock, locktable, readonly, update] technologies: [al] diff --git a/microsoft/knowledge/performance/use-findset-with-next.md b/microsoft/knowledge/performance/use-findset-with-next.md index f15912f..2232a05 100644 --- a/microsoft/knowledge/performance/use-findset-with-next.md +++ b/microsoft/knowledge/performance/use-findset-with-next.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [findset, next, repeat, iteration, aa0181] technologies: [al] diff --git a/microsoft/knowledge/performance/use-insert-false-when-skipping-triggers.md b/microsoft/knowledge/performance/use-insert-false-when-skipping-triggers.md index 748c6a2..a1391ba 100644 --- a/microsoft/knowledge/performance/use-insert-false-when-skipping-triggers.md +++ b/microsoft/knowledge/performance/use-insert-false-when-skipping-triggers.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [insert, modify, delete, triggers, parameters] technologies: [al] diff --git a/microsoft/knowledge/performance/use-isempty-for-existence-checks.md b/microsoft/knowledge/performance/use-isempty-for-existence-checks.md index 7199a74..58b483a 100644 --- a/microsoft/knowledge/performance/use-isempty-for-existence-checks.md +++ b/microsoft/knowledge/performance/use-isempty-for-existence-checks.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [isempty, count, findfirst, existence] technologies: [al] diff --git a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md index bcbf064..3ee35d4 100644 --- a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md +++ b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [setloadfields, partial-record, blob, bandwidth] technologies: [al] diff --git a/microsoft/knowledge/performance/use-single-instance-codeunits-for-caching.md b/microsoft/knowledge/performance/use-single-instance-codeunits-for-caching.md index ca98596..d8303ff 100644 --- a/microsoft/knowledge/performance/use-single-instance-codeunits-for-caching.md +++ b/microsoft/knowledge/performance/use-single-instance-codeunits-for-caching.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [singleinstance, cache, codeunit, session] technologies: [al] diff --git a/microsoft/knowledge/performance/use-temporary-tables-for-intermediate-data.md b/microsoft/knowledge/performance/use-temporary-tables-for-intermediate-data.md index 3c12938..c5ca053 100644 --- a/microsoft/knowledge/performance/use-temporary-tables-for-intermediate-data.md +++ b/microsoft/knowledge/performance/use-temporary-tables-for-intermediate-data.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: performance keywords: [temporary-table, in-memory, intermediate, working-set] technologies: [al] diff --git a/microsoft/knowledge/security/compose-secrets-with-secretstrsubstno.md b/microsoft/knowledge/security/compose-secrets-with-secretstrsubstno.md index 0d88c52..3f87594 100644 --- a/microsoft/knowledge/security/compose-secrets-with-secretstrsubstno.md +++ b/microsoft/knowledge/security/compose-secrets-with-secretstrsubstno.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [secretstrsubstno, secrettext, composition] technologies: [al] diff --git a/microsoft/knowledge/security/do-not-expose-sensitive-data-in-event-publishers.md b/microsoft/knowledge/security/do-not-expose-sensitive-data-in-event-publishers.md index b8907a8..7ad3ad3 100644 --- a/microsoft/knowledge/security/do-not-expose-sensitive-data-in-event-publishers.md +++ b/microsoft/knowledge/security/do-not-expose-sensitive-data-in-event-publishers.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [event, publisher, extensibility, var-parameter] technologies: [al] diff --git a/microsoft/knowledge/security/follow-least-privilege-in-permission-sets.md b/microsoft/knowledge/security/follow-least-privilege-in-permission-sets.md index d33c7be..09290f8 100644 --- a/microsoft/knowledge/security/follow-least-privilege-in-permission-sets.md +++ b/microsoft/knowledge/security/follow-least-privilege-in-permission-sets.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [permissionset, least-privilege, rimd, tabledata] technologies: [al] diff --git a/microsoft/knowledge/security/never-hardcode-secrets-in-al.md b/microsoft/knowledge/security/never-hardcode-secrets-in-al.md index 4be50aa..0d333c2 100644 --- a/microsoft/knowledge/security/never-hardcode-secrets-in-al.md +++ b/microsoft/knowledge/security/never-hardcode-secrets-in-al.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [secrets, credentials, hardcoded, label, apikey] technologies: [al] diff --git a/microsoft/knowledge/security/prefer-azure-key-vault-for-production-secrets.md b/microsoft/knowledge/security/prefer-azure-key-vault-for-production-secrets.md index bffe8c5..7a35168 100644 --- a/microsoft/knowledge/security/prefer-azure-key-vault-for-production-secrets.md +++ b/microsoft/knowledge/security/prefer-azure-key-vault-for-production-secrets.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [keyvault, azure, secrets, rotation, audit] technologies: [al] diff --git a/microsoft/knowledge/security/use-indirect-permissions-for-elevated-access.md b/microsoft/knowledge/security/use-indirect-permissions-for-elevated-access.md index ecf648b..d93efea 100644 --- a/microsoft/knowledge/security/use-indirect-permissions-for-elevated-access.md +++ b/microsoft/knowledge/security/use-indirect-permissions-for-elevated-access.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [indirect-permission, elevation, permissionset] technologies: [al] diff --git a/microsoft/knowledge/security/use-inherent-permissions-to-grant-minimal-access.md b/microsoft/knowledge/security/use-inherent-permissions-to-grant-minimal-access.md index d305791..d5b33f4 100644 --- a/microsoft/knowledge/security/use-inherent-permissions-to-grant-minimal-access.md +++ b/microsoft/knowledge/security/use-inherent-permissions-to-grant-minimal-access.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [inherentpermissions, attribute, least-privilege] technologies: [al] diff --git a/microsoft/knowledge/security/use-isolated-storage-for-module-and-company-secrets.md b/microsoft/knowledge/security/use-isolated-storage-for-module-and-company-secrets.md index 78b46dc..a5e00f0 100644 --- a/microsoft/knowledge/security/use-isolated-storage-for-module-and-company-secrets.md +++ b/microsoft/knowledge/security/use-isolated-storage-for-module-and-company-secrets.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [isolatedstorage, encryption, datascope, secrets] technologies: [al] diff --git a/microsoft/knowledge/security/use-nondebuggable-when-parsing-secrets.md b/microsoft/knowledge/security/use-nondebuggable-when-parsing-secrets.md index 4389394..f2b0d5c 100644 --- a/microsoft/knowledge/security/use-nondebuggable-when-parsing-secrets.md +++ b/microsoft/knowledge/security/use-nondebuggable-when-parsing-secrets.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [nondebuggable, secrettext, attribute, parse] technologies: [al] diff --git a/microsoft/knowledge/security/use-secrettext-for-credentials.md b/microsoft/knowledge/security/use-secrettext-for-credentials.md index 0401d9a..505d695 100644 --- a/microsoft/knowledge/security/use-secrettext-for-credentials.md +++ b/microsoft/knowledge/security/use-secrettext-for-credentials.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [secrettext, credentials, debugger, type] technologies: [al] diff --git a/microsoft/knowledge/security/use-secrettext-with-httpclient.md b/microsoft/knowledge/security/use-secrettext-with-httpclient.md index 6a0ed9a..9b73fec 100644 --- a/microsoft/knowledge/security/use-secrettext-with-httpclient.md +++ b/microsoft/knowledge/security/use-secrettext-with-httpclient.md @@ -1,5 +1,5 @@ --- -bc-version: [26..28] +bc-version: [all] domain: security keywords: [httpclient, secrettext, headers, uri] technologies: [al] diff --git a/microsoft/skills/al-code-review.md b/microsoft/skills/al-code-review.md index 4f27782..d60e78b 100644 --- a/microsoft/skills/al-code-review.md +++ b/microsoft/skills/al-code-review.md @@ -6,7 +6,7 @@ title: AL code review description: Reviews AL source changes by composing the AL review leaf skills (performance, security, ...). inputs: [pr-diff, file-path] outputs: [findings-report] -bc-version: [26..28] +bc-version: [all] technologies: [al] countries: [w1] application-area: [all] diff --git a/microsoft/skills/al-performance-review.md b/microsoft/skills/al-performance-review.md index 4b52d21..5762391 100644 --- a/microsoft/skills/al-performance-review.md +++ b/microsoft/skills/al-performance-review.md @@ -6,7 +6,7 @@ title: AL performance review description: Reviews AL source changes against performance guidance from BCQuality. inputs: [pr-diff, file-path] outputs: [findings-report] -bc-version: [26..28] +bc-version: [all] technologies: [al] countries: [w1] application-area: [all] diff --git a/microsoft/skills/al-security-review.md b/microsoft/skills/al-security-review.md index cecfbd5..1389785 100644 --- a/microsoft/skills/al-security-review.md +++ b/microsoft/skills/al-security-review.md @@ -6,7 +6,7 @@ title: AL security review description: Reviews AL source changes against security guidance from BCQuality. inputs: [pr-diff, file-path] outputs: [findings-report] -bc-version: [26..28] +bc-version: [all] technologies: [al] countries: [w1] application-area: [all] diff --git a/skills/read.md b/skills/read.md index b9a1562..08bce2c 100644 --- a/skills/read.md +++ b/skills/read.md @@ -26,7 +26,7 @@ A file that violates any of these rules is invalid and MUST be skipped by consum ```yaml --- -bc-version: [26, 27, 28] # or the range shorthand [26..28] +bc-version: [all] # or [26, 27, 28] or the range shorthand [26..28] domain: performance keywords: [query, filtering, partial] technologies: [al] @@ -39,12 +39,13 @@ All six fields are required. Missing or empty fields invalidate the file. ### Fields -**`bc-version`** — Array. The Business Central major versions this file applies to. Two forms are accepted: +**`bc-version`** — Array. The Business Central major versions this file applies to. Three forms are accepted: +- Universal sentinel: `[all]` means the guidance applies to every BC version and matches any target. - Explicit list: `[26, 27, 28]`. - Range shorthand: `[26..28]` means every integer from 26 through 28 inclusive. -Consumers MUST expand ranges to the full set before comparison. +`[all]` is mutually exclusive with explicit versions; do not combine. Consumers MUST expand ranges to the full set before comparison. **`domain`** — String. A single domain tag that places the file within a broader area of concern. Standard values include `performance`, `security`, `ux`, `telemetry`, `testing`, `api`, `pipelines`, `finance`, `supply-chain`, `manufacturing`, `jobs`. New domains may be introduced by contributors; no closed enumeration is enforced at the schema level. Consumers MUST treat unknown domains as valid. @@ -93,7 +94,7 @@ Conflict detection is the consumer's responsibility; BCQuality does not enforce When a consumer filters or matches files against a task context, these rules apply: -- **`bc-version`** — the target BC version MUST be an element of the file's expanded `bc-version` set. Range shorthand (`[26..28]`) MUST be expanded before comparison. +- **`bc-version`** — the file matches if its set is `[all]`, or if the target BC version is an element of the file's expanded `bc-version` set. Range shorthand (`[26..28]`) MUST be expanded before comparison. - **`technologies`** — non-empty intersection between the task's technologies and the file's technologies. There is no sentinel for this field. - **`countries`** — the file matches if its set contains `w1`, or if there is a non-empty intersection with the task's countries. - **`application-area`** — the file matches if its set contains `all`, or if there is a non-empty intersection with the task's application areas. @@ -104,7 +105,7 @@ A file is **applicable** to a task when all four rules match. Applicability is a A task context may omit one or more dimensions (for example, a skill invoked against a raw file path with no known target BC version). For any omitted dimension: -- If the file's value for that dimension is a universal sentinel (`w1` for countries, `all` for application-area), the rule matches. +- If the file's value for that dimension is a universal sentinel (`all` for bc-version, `w1` for countries, `all` for application-area), the rule matches. - Otherwise the rule is treated as **unknown**, not as a match and not as a failure. A file with any `unknown` rule is **conditionally applicable**. A consumer MAY include conditionally applicable files in the worklist; if it does, every finding derived from such a file MUST have `confidence` no higher than `medium` and MUST record the unknown dimensions in the finding's `message`. A consumer MAY be configured to exclude conditionally applicable files entirely. diff --git a/skills/write.md b/skills/write.md index 22af773..6fe2eee 100644 --- a/skills/write.md +++ b/skills/write.md @@ -43,7 +43,7 @@ Knowledge files do not contain code. Samples live as **sibling files** next to t ## Choosing frontmatter values -**`bc-version`.** Claim only the versions you have evidence for. If the guidance is known to apply from BC 24 onward and you have tested against 26–28, write `[26..28]`, not `[24..28]`. Under-claim; a future contributor can widen the range. +**`bc-version`.** Default to `[all]` when the guidance is universal — a BC language pattern, a property on a long-standing platform type, a CodeCop rule, or a platform behaviour that has not changed across versions. Use an explicit list or range (`[26, 27, 28]`, `[26..28]`) only when the guidance is tied to a version-gated API, a deprecation, or platform behaviour that genuinely differs across versions. Most knowledge files should be `[all]`; reach for a range only with a concrete reason. **`domain`.** Pick one. If two fit, the file is probably two concerns. If no existing domain fits, introduce a new one — domains are open. Prefer existing domains when they are a reasonable fit, to keep retrieval predictable.