mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Some checks are pending
Validate knowledge index / validate-index (push) Waiting to run
Validate AL review fixtures / validate-review-fixtures (push) Waiting to run
Validate skill index and report schemas / validate-contract (push) Waiting to run
Validate frontmatter and structure / validate (push) Waiting to run
* Add retention policy knowledge to the privacy domain Two articles covering retention policies for extension-owned tables, the gap that lets high-volume log tables grow unbounded: - register-owned-log-tables-for-retention-policies: an extension's own log tables must be added to the allowed-tables list from install AND upgrade code, guarded by IsAllowedTable plus an upgrade tag, with a mandatory minimum retention where audit needs one. - ship-a-default-retention-policy-setup: registration only makes a table selectable; nothing is deleted until a Retention Policy Setup record exists, so ship one (disabled by default) as the platform's own Retention Policy Installer does. Each ships good/bad AL samples. Claims verified against the BC admin docs and the Retention Policy module in microsoft/BCApps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gvTjm746MtJEVWeRTbG46 * Address review feedback on retention policy knowledge - Scope both articles to bc-version [17..] (retention policies shipped in v17). - Allowed-tables sample: add OnRefreshAllowedTables subscriber with a ForceUpdate path; the upgrade tag now gates one-time setup only. - Default-policy sample: use Retention Policy Setup.FindOrCreateRetentionPeriod instead of a hand-rolled lookup-then-insert that can collide on code. - Anti-pattern now keys on append-only tables rather than table names. - al-privacy-review: add retention-policy tokens and deterministic routing for both articles, with a bounded per-table text search for delete and registration paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address second review round on retention policy knowledge - Scope both articles to bc-version [22..]: FindOrCreateRetentionPeriod first appears in the 21.1 System Application and OnRefreshAllowedTables in 22. - Reframe the default-setup article as optional guidance; registration without a setup is valid. The anti-pattern and review routing now cover only false claims that registration alone cleans up data. - Make all four samples self-contained: declare Contoso Activity Log in each, add the Retention Policy Setup permission, and guard the default setup on IsAllowedTable. - Let evaluation overrides list additionalArticles and register both retention pairs as extra privacy cases (38 cases, existing IDs unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Revert evaluation harness change for multiple articles per domain The harness intentionally evaluates one paired article per domain. Keep it as designed; how the retention pairs join privacy evaluation is left to the maintainers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Register retention policy pairs in the privacy evaluation override Use main's articles override so both retention article pairs get positive and clean cases alongside no-pii-in-telemetry-message-string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Jeremy Vyska <jeremy@sparebrained.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
200 lines
7.9 KiB
JSON
200 lines
7.9 KiB
JSON
{
|
|
"version": 2,
|
|
"selection": "first-paired-al-article",
|
|
"minimumExpectedRecall": 1.0,
|
|
"minimumCleanRate": 1.0,
|
|
"coverageWaivers": [],
|
|
"overrides": {
|
|
"agents": {
|
|
"article": "wire-all-three-agent-interfaces"
|
|
},
|
|
"breaking-changes": {
|
|
"article": "do-not-expose-sensitive-data-through-public-api"
|
|
},
|
|
"data-modeling": {
|
|
"articles": [
|
|
"activate-new-price-calculation-handler-via-onfindsupportedsetup",
|
|
"check-blocked-in-referencing-code-not-in-master",
|
|
"code-must-not-change-workdate",
|
|
"custom-document-dispatch-must-not-bypass-report-selections",
|
|
"delete-master-data-with-trigger",
|
|
"document-line-prices-follow-prices-including-vat",
|
|
"document-print-and-email-actions-call-report-selections-directly",
|
|
"extend-find-entries-navigate-for-new-document-types",
|
|
"extend-price-source-type-must-sync-document-subset-enum",
|
|
"extend-report-selection-usage-for-new-document-types",
|
|
"master-data-must-be-inserted-with-trigger",
|
|
"new-price-source-must-add-candidate-and-trigger-recalculation",
|
|
"pictures-must-use-media-not-blob",
|
|
"report-barcodes-must-use-barcode-module-and-production-font-name",
|
|
"table-design-must-match-bc-table-type-conventions",
|
|
"transferfields-mirrored-fields-must-match-type-and-length"
|
|
]
|
|
},
|
|
"error-handling": {
|
|
"articles": [
|
|
"collect-validation-errors-with-errorbehavior",
|
|
"declined-confirm-must-abort-not-partially-apply",
|
|
"defensive-vs-offensive-code-must-match-blast-radius",
|
|
"log-writes-must-survive-rollback"
|
|
]
|
|
},
|
|
"events": {
|
|
"articles": [
|
|
"reset-ishandled-only-when-the-value-can-carry-over",
|
|
"changecompany-runs-triggers-in-the-calling-company",
|
|
"database-trigger-setup-flags-may-only-be-set-to-true"
|
|
]
|
|
},
|
|
"finance": {
|
|
"articles": [
|
|
"apply-ledger-entries-through-application-codeunits",
|
|
"change-ledger-due-dates-through-entry-edit",
|
|
"do-not-edit-shared-dimension-sets",
|
|
"do-not-modify-or-delete-posted-ledger-entries",
|
|
"normal-vat-journal-amount-includes-vat",
|
|
"post-ledger-entries-through-posting-codeunits",
|
|
"preserve-journal-batch-document-balance",
|
|
"reverse-transactions-by-transaction-number",
|
|
"write-dimensions-as-dimension-set-entries"
|
|
],
|
|
"context": "Target Business Central 28, worldwide standard application (w1). Review each file as an independent source input."
|
|
},
|
|
"interfaces": {
|
|
"article": "set-defaultimplementation-on-enum"
|
|
},
|
|
"performance": {
|
|
"articles": [
|
|
"use-isempty-for-existence-check",
|
|
"use-get-instead-of-findfirst-on-full-primary-key",
|
|
"job-queue-category-code-serializes-conflicting-jobs",
|
|
"job-queue-external-effects-must-be-idempotent",
|
|
"job-queue-handlers-must-not-require-ui",
|
|
"job-queue-handlers-must-propagate-failures",
|
|
"job-queue-on-hold-does-not-stop-running-work",
|
|
"store-scheduled-task-id-to-avoid-duplicate-tasks",
|
|
"design-covering-keys-from-read-pattern",
|
|
"review-overlapping-keys-before-adding-an-index",
|
|
"setcurrentkey-sets-sort-order-not-index-hint",
|
|
"preserve-buffered-inserts-by-separating-target-reads",
|
|
"aggregate-before-persisting-intermediate-results",
|
|
"cache-repeated-filtered-results-with-explicit-scope",
|
|
"avoid-repeating-unchanged-validation",
|
|
"avoid-get-inside-loop-on-large-table",
|
|
"isempty-before-findset-is-extra-round-trip",
|
|
"query-results-bypass-primary-key-cache",
|
|
"calcsums-instead-of-calcfields-in-loop",
|
|
"use-setautocalcfields-for-per-row-flowfields",
|
|
"temporary-tables-have-no-database-cost",
|
|
"use-setloadfields-for-partial-records",
|
|
"prefer-modifyall-over-per-row-modify",
|
|
"al-methods-limited-during-write-transactions",
|
|
"avoid-user-prompts-inside-transactions",
|
|
"use-grouped-query-for-distinct-values-and-duplicates"
|
|
]
|
|
},
|
|
"privacy": {
|
|
"articles": [
|
|
"no-pii-in-telemetry-message-string",
|
|
"register-owned-log-tables-for-retention-policies",
|
|
"ship-a-default-retention-policy-setup"
|
|
]
|
|
},
|
|
"query": {
|
|
"articles": [
|
|
"dataitemtablefilter-cannot-be-overwritten-at-runtime",
|
|
"reopening-query-resets-cursor-but-keeps-filters",
|
|
"set-query-filters-before-open",
|
|
"setfilter-overwrites-query-columnfilter"
|
|
]
|
|
},
|
|
"reporting": {
|
|
"articles": [
|
|
"clear-report-variable-before-independent-runmodal",
|
|
"currreport-break-ends-the-current-trigger",
|
|
"currreport-quit-rolls-back-and-skips-onpostreport",
|
|
"currreport-skip-does-not-stop-trigger-code",
|
|
"report-output-in-a-loop-needs-one-client-download",
|
|
"reportextension-dataitem-trigger-order-is-explicit",
|
|
"reportextension-report-triggers-run-after-base-triggers",
|
|
"settableview-cannot-broaden-dataitemtableview",
|
|
"stop-when-runrequestpage-returns-empty-parameters"
|
|
]
|
|
},
|
|
"scm": {
|
|
"articles": [
|
|
"post-item-ledger-changes-through-item-journals",
|
|
"post-revaluation-through-the-item-journal-batch",
|
|
"change-item-applications-through-posting-routines",
|
|
"cancel-reservations-through-reservation-management",
|
|
"transfer-item-tracking-through-source-reservation-codeunits",
|
|
"reconcile-warehouse-adjustments-with-the-item-ledger",
|
|
"post-transfers-through-shipment-and-receipt-codeunits",
|
|
"use-date-aware-availability-for-promising",
|
|
"carry-out-requisition-actions-through-the-standard-workflow",
|
|
"derive-base-quantities-through-the-line-unit-of-measure"
|
|
]
|
|
},
|
|
"security": {
|
|
"articles": [
|
|
"al-has-no-built-in-htmlencode",
|
|
"do-not-concatenate-external-text-into-setfilter",
|
|
"exposed-objects-must-be-in-a-permission-set"
|
|
]
|
|
},
|
|
"style": {
|
|
"articles": [
|
|
"label-comment-explains-placeholders",
|
|
"dateformula-evaluate-needs-language-independent-literals",
|
|
"al-comments-must-not-restate-what-code-already-shows",
|
|
"pages-must-not-contain-business-logic",
|
|
"mutating-procedure-for-a-page-caller-takes-var-record"
|
|
]
|
|
},
|
|
"telemetry": {
|
|
"article": "telemetry-event-id-stable-unique"
|
|
},
|
|
"testing": {
|
|
"articles": [
|
|
"ui-handlers-in-tests",
|
|
"reset-per-test-state-before-the-isinitialized-guard",
|
|
"asserterror-needs-expectederror-and-code",
|
|
"bcpt-scenarios-must-be-app-specific",
|
|
"commit-shared-test-fixture-inside-lazy-initialize",
|
|
"given-blocks-must-cover-full-precondition-chain",
|
|
"table-relation-test-exclude-known-invalid-relations-via-event",
|
|
"test-feature-scenario-tags",
|
|
"test-one-when-per-test",
|
|
"transactionmodel-attribute-governs-test-transactions",
|
|
"ui-test-codeunit-naming",
|
|
"use-assert-isfalse-not-asserterror-for-boolean-checks"
|
|
]
|
|
},
|
|
"ui": {
|
|
"articles": [
|
|
"default-descending-sort-on-historical-pages",
|
|
"page-design-must-match-bc-page-type-conventions",
|
|
"page-client-expression-must-not-use-in-list",
|
|
"rolecenter-permission-gating-must-use-accessbypermission"
|
|
]
|
|
},
|
|
"upgrade": {
|
|
"articles": [
|
|
"initvalue-does-not-update-existing-rows",
|
|
"upgrade-tag-logic-must-not-nest-deeply",
|
|
"no-changecompany-in-upgrade"
|
|
],
|
|
"context": "The extended table existed in the previous app version and already contains rows."
|
|
},
|
|
"web-services": {
|
|
"articles": [
|
|
"expose-systemid-as-the-api-key",
|
|
"handle-httpclient-platform-failure-before-response-access",
|
|
"check-http-status-before-consuming-response-body",
|
|
"check-json-null-before-converting-values",
|
|
"format-exchanged-values-with-standard-format-9",
|
|
"api-page-least-privilege-write-access"
|
|
]
|
|
}
|
|
}
|
|
}
|