Skip to content

Add retention policy knowledge to the privacy domain - #177

Open
Jeremy Vyska (JeremyVyska) wants to merge 1 commit into
mainfrom
jeremy-retention-policies
Open

Add retention policy knowledge to the privacy domain#177
Jeremy Vyska (JeremyVyska) wants to merge 1 commit into
mainfrom
jeremy-retention-policies

Conversation

@JeremyVyska

Copy link
Copy Markdown
Collaborator

Business Central's retention policy feature had no coverage in BCQuality — the only mention anywhere in the corpus was a passing clause in the data-classification article. Extension-owned tables that accumulate rows (activity logs, integration logs, archives) routinely ship with no policy at all, and the two mistakes behind that are both LLM-invisible: registration that only runs in install code, and registration mistaken for a policy.

Two articles in microsoft/knowledge/privacy/, each with .good.al / .bad.al companions:

register-owned-log-tables-for-retention-policies.md — the engine only deletes from tables in the allowed-tables list, and an extension may register only tables it owns. Register from install and upgrade code, because install code does not run when an existing installation moves to a new version; guard with IsAllowedTable plus an upgrade tag; use MandatoryMinRetenDays where audit needs a floor, and locked filters via AddTableFilterToJsonArray. Anti-pattern: a hand-rolled DeleteAll against a hard-coded window, invisible to the Retention Policy Log and unadjustable by the administrator.

ship-a-default-retention-policy-setup.mdAddAllowedTable only makes a table selectable. Nothing is deleted until a Retention Policy Setup record exists, names a period, and is enabled, so ship one pre-created and disabled, gated by an upgrade tag — the shape codeunit 3907 "Retention Policy Installer" uses for Retention Policy Log Entry. Anti-pattern in both directions: registering and stopping, or inserting the setup enabled so the app deletes tenant data on a schedule nobody approved.

Platform claims are cited in ## References against the BC admin documentation and the Retention Policy module in microsoft/BCApps. Layer choice follows the current skills/write.md rule: privacy is owned by al-privacy-review, so its corpus lives in the Microsoft layer regardless of authorship.

Checks: validate_frontmatter.py reports 0 errors / 0 warnings; Test-ReviewFixtures.ps1 passes at 34 cases across 17 leaf domains (the convention-selected privacy fixture is unchanged).

Deliberately out of scope, noted for a later PR: Apply Retention Policy deletes at most 10,000 records per run shared across all policies, so a table gaining more than that per day never converges. That is a throughput concern and belongs in performance, not here.

🤖 Generated with Claude Code

https://claude.ai/code/session_011gvTjm746MtJEVWeRTbG46

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are valuable additions: the distinction between allowed-table registration and an actual policy is important, the opt-in default is responsible, and the main platform claims align with the current retention-policy module. The repository validators and all PR checks pass.

I found three correctness/integration gaps that need addressing before these rules become authoritative agent guidance. Most importantly, the privacy review skill currently extracts no retention-policy signals and has no targeted cues for either rule. The new article keywords (retention-policy, AddAllowedTable, Retention Policy Setup, install/upgrade codeunits, log/archive tables) do not intersect its current token list, so a reviewer can miss both the absent registration and the “registered but no setup” anti-pattern. Please update al-privacy-review.md with relevance tokens and deterministic candidate-selection cues for both articles—for example, accumulating extension-owned log/archive tables or hand-rolled cleanup for the first, and AddAllowedTable without a corresponding setup for the second.

The inline findings cover refresh behavior, version scope, and the default-period implementation. With those corrections, the overall contribution should be in good shape.

@@ -0,0 +1,33 @@
---
bc-version: [all]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retention policies and these System Application APIs were introduced with Business Central 2020 release wave 2 (v17), so [all] makes both articles applicable to versions where the objects do not exist. Please scope both articles to bc-version: [17..], consistent with how this corpus versions other platform features.


## Best Practice

Register every table the extension owns that accumulates rows over time: activity and audit logs, integration and API request logs, archived documents. Call a shared routine from both the install codeunit (`OnInstallAppPerCompany`) and an upgrade codeunit (`OnUpgradePerCompany`), because install code does not run when an existing installation moves to a new version — registration added only to install code never reaches tenants that already have the app. Guard the routine with `IsAllowedTable` and an upgrade tag so repeated runs are idempotent. Pass `MandatoryMinRetenDays` when the data must survive a minimum period for audit or support reasons; the platform then rejects any shorter period an administrator configures. When only a subset of rows should ever expire, build the filter with `AddTableFilterToJsonArray` and pass it to the `AddAllowedTable` overload that takes a `JsonArray` — a filter added as locked cannot be removed later by the administrator.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Install and upgrade coverage is necessary, but not sufficient. The Retention Policies pages raise Reten. Pol. Allowed Tables.OnRefreshAllowedTables; current System Application, Base Application, Shopify, Email, and Performance Profiler installers all subscribe and re-run registration with a force path. This sample's upgrade-tag guard makes it exit after the first run, so it cannot participate in that refresh path. Please add the event subscriber and separate “refresh registration” from “one-time setup/tag” behavior, as the platform installers do.

var
RetentionPeriod: Record "Retention Period";
begin
RetentionPeriod.SetRange("Retention Period", RetentionPeriod."Retention Period"::"6 Months");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the public Codeunit "Retention Policy Setup".FindOrCreateRetentionPeriod(...) helper here instead of reimplementing it. This implementation can fail if a Retention Period record already uses code SIX MONTHS for a different enum value: the filtered FindFirst finds nothing, then Insert collides on the existing code. The public helper is specifically intended to find or safely create the requested period and is what current Shopify, Performance Profiler, and Financial Report code uses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants