knowledge(events): database trigger setup flags may only be set to true (#213)

* knowledge(events): database trigger setup flags may only be set to true

Add an events article with compiled good/bad samples:
GetDatabaseTableTriggerSetup (Global Triggers) and
OnAfterGetDatabaseTableTriggerSetup (GlobalTriggerManagement) share four
var Booleans across all subscribers, which run in no particular order.
Assigning false, or a lookup result without or-ing in the current value,
clears flags other features set (Dataverse sync, API webhooks, data
archive, and, for direct Global Triggers subscribers, the change log).
Recommends the codeunit 49 integration events per Learn's guidance on
system codeunits 2000000001..2000000010 and handler-side table filtering.

Wired into al-events-review tokens and an event-design check, with
carve-outs for conditional := true, Flag := Flag or ..., and the no-op
"if not Flag then Flag := false" found in BCApps. Registered in the
events review-fixtures override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* knowledge(events): address review of database trigger setup flags article

- Name the BCApps code that clears flags (demo data tool W1/CZ/IN,
  Backup Management test library) as the mechanism in demo/test-only
  sessions; carve such code out of the al-events-review check.
- Label "events are raised only when the flag is true" as inferred and
  cite the supporting sources (Learn integration-record refactoring on
  disabled bulk SQL operations, ChangeLog test cache comment,
  No Transactions Subscriber); fold the bulk-SQL cost into Best Practice.
- Drop the "handler without opt-in" signal from article and check.
- Qualify change log protection as normal execution context only.
- Drop the unverifiable codeunit ID; describe Global Triggers by its
  2000000001..2000000010 range and mark the transition page as v14-era.
- Order-dependent wording for flag overwrites; add GP and
  No Transactions as further direct subscribers; link the references.

Samples rechecked with alc 30.0 against Base Application 28.4 symbols.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael Dieringer 2026-10-05 14:59:53 +02:00 • committed by GitHub
parent 4405c97c57
commit 4cd53eac64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 177 additions and 2 deletions

View file

@ -39,7 +39,7 @@ Narrow the relevant files to the subset that applies to the changes under review
- The changed AL object names and types — especially codeunits that publish events or host event subscribers, posting/release/validation routines that should expose extension points, and test codeunits that bind subscribers.
- The changed procedures and triggers, weighted toward event publisher methods, methods carrying the `[EventSubscriber(...)]` attribute, routines that raise `OnBefore`/`OnAfter` events, and any procedure that calls `BindSubscription`/`UnbindSubscription`.
- Tokens extracted from the diff that relate to events and the publish/subscribe model (`IntegrationEvent`, `BusinessEvent`, `InternalEvent`, `EventSubscriber`, `IsHandled`, `BindSubscription`, `UnbindSubscription`, `EventSubscriberInstance`, `OnBefore`, `OnAfter`, `Manual`, `IncludeSender`, `GlobalVarAccess`, `Isolated`, `local`, `internal`, `Sender`, `this`, `RecordRef`, `xRec`, `temporary`, `Temp`, `repeat`, `ChangeCompany`, `StartSession`, `RunTrigger`).
- Tokens extracted from the diff that relate to events and the publish/subscribe model (`IntegrationEvent`, `BusinessEvent`, `InternalEvent`, `EventSubscriber`, `IsHandled`, `BindSubscription`, `UnbindSubscription`, `EventSubscriberInstance`, `OnBefore`, `OnAfter`, `Manual`, `IncludeSender`, `GlobalVarAccess`, `Isolated`, `local`, `internal`, `Sender`, `this`, `RecordRef`, `xRec`, `temporary`, `Temp`, `repeat`, `ChangeCompany`, `StartSession`, `RunTrigger`, `GetDatabaseTableTriggerSetup`, `OnAfterGetDatabaseTableTriggerSetup`, `GlobalTriggerManagement`, `Global Triggers`, `OnDatabaseInsert`, `OnDatabaseModify`, `OnDatabaseDelete`, `OnDatabaseRename`).
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed object type. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone.
@ -66,6 +66,7 @@ The following targeted checks map diff signals to specific `events` articles. Tr
- A `var IsHandled` added to a pre-existing event rather than introduced through a new `OnBefore` publisher — `do-not-add-ishandled-to-an-existing-event`.
- An `if IsHandled then exit;` whose skipped body performs posting, ledger-entry creation, number-series consumption, or integrity/permission validation — `do-not-bypass-critical-operations-with-ishandled`.
- A record variable that had `ChangeCompany(<name>)` called on it and is later used with `Insert`, `Modify`, `Delete`, or `Validate`, where the table is not owned by the extension, has triggers that read company data, or has trigger-event subscribers that do not exit on `RunTrigger = false` — `changecompany-runs-triggers-in-the-calling-company`. Do not match a read-only use after `ChangeCompany`, a write with `RunTrigger = false` into an extension-owned table whose triggers do not read company data and whose trigger-event subscribers exit on `RunTrigger = false`, or the parameterless `ChangeCompany()` reset.
- A subscriber to `GetDatabaseTableTriggerSetup` (`Global Triggers`) or `OnAfterGetDatabaseTableTriggerSetup` (`GlobalTriggerManagement`) that assigns one of its `var` flags (`OnDatabaseInsert`, `OnDatabaseModify`, `OnDatabaseDelete`, `OnDatabaseRename`) a literal `false` or a lookup/Boolean expression that does not `or` in the flag's current value, or that calls `Clear` on one — `database-trigger-setup-flags-may-only-be-set-to-true`. Do not match `Flag := true` under a condition, `Flag := Flag or <condition>`, or `if not Flag then Flag := false;`, none of which can clear a flag. Do not match code that runs only in demo-data generation or test-library sessions (for example the base application's demo data tool or a test library's backup/restore subscriber), where clearing other features' flags is accepted.
## Action