Address review: notification recall defect is a lost identity, not an unassigned or generated Id

- notification-recall-needs-known-id: the defect is a Recall() whose Id
  cannot be the sent one (fresh local Notification, new CreateGuid at
  recall time) while neither the sent instance nor its Id is kept. A
  retained global instance (CreateGuid once in OnOpenPage, or Id left for
  Send to assign), a generated Id saved after Send and reassigned before
  Recall, and correctly tracked per-record Ids are explicitly not findings.
  Findings require evidence that the recalled identity differs from or
  cannot recover the sent identity. Notification Lifecycle Mgt. is
  recommended for per-record tracking, not mandatory. Cites VAT Bus. Post.
  Grp. Part, Certificate, and Data Search Lines, and the lifecycle
  helper's Send-then-read-Id sequence.
- good sample: adds a page with a retained global Notification (CreateGuid
  in OnOpenPage, Send in an action, Recall in a later action and
  OnClosePage) and a pageextension that saves the Send-assigned Id and
  recalls it from a later action. Bad sample comments name the lost
  identity.
- al-ui-review: notification cue requires that evidence and lists the
  retained-instance, saved-Id, and direct per-record tracking controls as
  exclusions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael Dieringer 2026-10-05 16:05:59 +02:00
parent a37f45ada4
commit cff8917ecb
4 changed files with 144 additions and 12 deletions

View file

@ -52,7 +52,7 @@ Apply these high-signal mappings before fuzzy topic ranking:
- An editable page part affects a total, FlowField, or FactBox on the parent but does not set `UpdatePropagation = Both` — `updatepropagation-both-refreshes-main-page`.
- A page or pageextension `Enabled`, `Visible`, `Editable`, or `StyleExpr` value contains an `in [...]` list (compiler: "InListExpression is not valid for client expressions", AL0573 or AL0322), or replaces one with a procedure call — `page-client-expression-must-not-use-in-list`. Plain `=`/`<>` comparisons joined with `and`/`or`, and `in [...]` inside a trigger or procedure body, are valid; do not flag them.
- A `RoleCenter` page, or a pageextension whose target is a Role Center, gates a part, action, or field by binding `Visible` or `Enabled` to a procedure that tests a permission, or declares a procedure (AL0569 "A page of type Role Center cannot have procedures", AL0573) — `rolecenter-permission-gating-must-use-accessbypermission`. The target page name is not reliable evidence of its type; confirm it is a Role Center from its `PageType` or the AL0569 diagnostic. Setup- or feature-flag gating inside the part page is not this pattern.
- The same code path calls `Send()` and `Recall()` on a `Notification` (for example `Send()` when a condition holds, `Recall()` in the `else` branch or when it clears) but never assigns its `Id`, or assigns `CreateGuid()` and calls `Send()`/`Recall()` directly; or, when notifications for several records must be visible at the same time, they share one fixed `Id` sent and recalled directly — `notification-recall-needs-known-id`. A notification that is never recalled; one sent through `"Notification Lifecycle Mgt."` (`SendNotification`, `SendNotificationWithAdditionalContext`) without an `Id`; and a fixed `Id` shared across records for a one-at-a-time warning that is recalled before re-send (directly or through `SendNotification`, as in `Sales Line`'s blocked-item notification) are valid; do not flag them.
- Code sends and recalls a `Notification`, and the `Notification` passed to `Recall()` cannot carry the sent `Id`: for example `Send()` on a local variable when a condition holds and `Recall()` on a fresh local variable when it clears, with no `Id` assigned or a new `CreateGuid()` assigned on each call, while neither the sent instance nor its `Id` is kept in a global variable, a record, or `"Notification Lifecycle Mgt."`; or, when notifications for several records must be visible at the same time, they share one fixed `Id` sent and recalled directly — `notification-recall-needs-known-id`. Flag only with evidence that the recalled identity differs from, or cannot recover, the sent identity: trace the `Recall()` variable back to its `Id` assignment and the sent variable forward to where its instance or `Id` is kept. Valid, do not flag: a notification that is never recalled; a global or page-level `Notification` instance sent and later recalled, whether its `Id` came from `CreateGuid()` (for example once in `OnOpenPage`) or from `Send()`; a generated `Id` saved after `Send()` and reassigned before `Recall()`; per-record `Id`s the code tracks correctly itself; one sent through `"Notification Lifecycle Mgt."` (`SendNotification`, `SendNotificationWithAdditionalContext`) without an `Id`; and a fixed `Id` shared across records for a one-at-a-time warning that is recalled before re-send (directly or through `SendNotification`, as in `Sales Line`'s blocked-item notification). Recommend `"Notification Lifecycle Mgt."` for per-record tracking, but do not flag correct direct tracking for not using it.
- A page action opens a record of `Sales Header`, `Purchase Header`, their archives, or another table that codeunit `"Page Management"` routes (directly or after a `Get`) by switching on its `Document Type` or a similar type field and calling `Page.Run(Page::...)` per branch — `list-page-document-routing-uses-page-management`. Severity `minor`. Opening one known document type directly, a list with a fixed `CardPageID`, and a table that `"Page Management"` does not route (for example `Assembly Header`) are not this pattern.
Once the candidate worklist is known, resolve layer-precedence conflicts per READ and record suppressions.