Add StrongPoint custom knowledge layer

Eight organization rules in custom/knowledge, each overriding or
supplementing the Microsoft layer for StrongPoint repositories:

- style/spu-prefix-on-objects-and-extension-members
- style/cyclomatic-complexity-is-not-a-gate-in-sp-repos (LC0010 = Info)
- ui/keep-tooltips-on-sp-page-fields (AA0218 = Error, LC0064 off;
  overrides microsoft ui/bound-page-field-inherits-source-field-tooltip)
- data-modeling/sp-field-names-fit-30-chars-with-prefix (AL0468 = Error)
- events/change-ls-central-behaviour-through-its-events
- web-services/device-io-goes-through-sp-device-manager
- appsource/sp-id-ranges-are-not-checked-by-as0084 (AS0084 = None)
- upgrade/sp-released-ids-never-change

Sources: sp.ruleset.json across SP repositories, SPUFBUpgrade pattern in
SP-LSC-Fiscal-Printing, observed LS Central and Device Manager usage.
Verified: all eight are indexed by Build-KnowledgeIndex.ps1, and a review
of a probe app applied the ToolTip rule as major and suppressed the
Microsoft article by layer precedence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paulius Urbanas 2026-09-25 13:50:52 +03:00
parent 07e324ddbc
commit 5149da25ba
8 changed files with 208 additions and 0 deletions

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: appsource
keywords: [id-range, object-id, as0084, app-json, idranges, false-positive]
technologies: [al]
countries: [w1]
application-area: [all]
---
# StrongPoint ID ranges are governed by app.json, not by AS0084
## Description
StrongPoint's `sp.ruleset.json` disables AppSourceCop AS0084 ("We are using our custom range"). SP apps use StrongPoint-registered ranges such as `70760xxx` and, for some customer and test apps, other ranges; the rule that matters is that every object and extension field ID lies inside the app's own `app.json` `idRanges` and does not collide with another SP app.
## Best Practice
Check new IDs against the project's `app.json` `idRanges` (and the next free ID in that range). Report an ID outside `idRanges` as `major` — the compiler rejects it. Do not assess whether the range itself is an AppSource-assigned range.
## Anti Pattern
Reporting an SP app's ID range, or IDs inside it, as invalid because AS0084 or AppSource range rules would flag them; renumbering objects to move them into a different range (see the upgrade rule on ID changes after release).
## References
StrongPoint policy: `sp.ruleset.json` — AS0084 `None` ("Disable rule AS0084. We are using our custom range").

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: data-modeling
keywords: [field-name, length, al0468, 30-characters, prefix, tableextension]
technologies: [al]
countries: [w1]
application-area: [all]
---
# SP table field names, prefix included, stay within 30 characters
## Description
Table field names are limited to 30 characters (compiler rule AL0468), and StrongPoint's `sp.ruleset.json` raises AL0468 to an **error**. Because extension fields must start with `SPU ` plus the app code (for example `SPU FB `), only about 23–26 characters remain for the descriptive part. Generated names routinely exceed the limit and break the build.
## Best Practice
Count the full field name, prefix and spaces included, before proposing it; abbreviate the descriptive part (`Rcpt.`, `No.`, `Amt.`) the way existing SP fields do, and put the long wording in `Caption`. Report a field name over 30 characters as `major`.
## Anti Pattern
A field such as `"SPU FB Fiscal Receipt Printed Date Time"` (39 characters), or dropping the `SPU` prefix to make a name fit.
## References
StrongPoint policy: `sp.ruleset.json` — AL0468 `Error` ("Length of the table field name must not exceed 30 characters").

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: events
keywords: [ls-central, lsc, event-subscriber, pos, integration, dependency, false-positive]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Change LS Central behaviour through its events; calling its public codeunits is fine
## Description
StrongPoint extensions depend on LS Central. To change or extend what LS Central does — POS transaction flow, posting, printing, tender handling — SP code subscribes to LS Central events (`[EventSubscriber]` on `Codeunit::"LSC ..."`) instead of copying or re-implementing LS Central logic. Using LS Central's public codeunits as services is established SP practice and not a defect: `LSC POS Session`, `LSC POS Transaction`, `LSC POS Functions`, `LSC POS Print Utility`, `LSC WS Functions` and similar are called directly across all SP LSC apps.
## Best Practice
When the change alters LS Central's own behaviour, find the LS Central event that fires at that point and subscribe to it; state the event in the review if one exists. Call LS Central public procedures freely for session state, transaction data, printing and web-service helpers. LS Central source is not in Microsoft's Base App corpus — verify LS Central events against the project's `.alpackages` symbols.
## Anti Pattern
Duplicating an LS Central procedure body inside an SP codeunit to change one step of it, or modifying behaviour by re-running LS Central logic after the fact when an event exists. Equally wrong: flagging a direct call to a public LS Central codeunit as an architecture violation.
## References
StrongPoint practice observed in SP-LSC-Fiscal-Printing, SP-LSC-EMV-Integration, SP-LSC-SCO-Integration and SP-LSC-Discount-Management (event subscribers on LSC publishers alongside direct use of `LSC POS Session`, `LSC POS Transaction`, `LSC WS Functions`).

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: style
keywords: [cyclomatic-complexity, lc0010, lintercop, maintainability, false-positive]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Cyclomatic complexity (LC0010) is advisory in StrongPoint repositories
## Description
StrongPoint's `sp.ruleset.json` downgrades LinterCop LC0010 (cyclomatic complexity) to `Info`, and a few repositories disable it. Long procedures in POS, fiscal-printing and device flows mirror LS Central's own event and state handling; splitting them only to satisfy the metric is not required. Complexity is therefore never a merge gate in SP code.
## Best Practice
Mention high complexity at most as `info` or `minor`, and only together with a concrete readability or correctness problem you can point to (a duplicated branch, an unreachable path, a missing `else`). Judge new code on correctness first.
## Anti Pattern
Reporting a procedure as `major` or `blocker` because LC0010 fires or because it "has too many branches", or asking for a refactor of existing complex procedures that the change did not touch.
## References
StrongPoint policy: `sp.ruleset.json` — LC0010 `Info` ("Cyclomatic complexity warning changed to info").

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: style
keywords: [prefix, affix, spu, naming, object-name, tableextension, pageextension, as0011]
technologies: [al]
countries: [w1]
application-area: [all]
---
# StrongPoint objects and extension members carry the `SPU` prefix
## Description
In StrongPoint repositories every new AL object name, and every new field, control or action that a table extension or page extension adds to someone else's object, starts with the mandatory affix `SPU` — for example `codeunit 70760591 "SPU EMV Manager"` or the table-extension field `"SPU FB Receipt Printed"`. The AL-Go pipeline validates the prefix and fails the build without it; there is usually no `AppSourceCop.json` with `mandatoryAffixes` in the repository to warn earlier, so a missing prefix is only found at CI time.
## Best Practice
Name every new object `"SPU <App code> <Name>"` and every member added to a base-app or LS Central object `"SPU <App code> <Name>"`, following the app's existing code (for example `FB` for Fiscal Printing). Members of the extension's own `SPU` objects (fields of an `SPU` table, procedures of an `SPU` codeunit) do not need the prefix again. Report a missing prefix on a new object or extension member as `major`: the build will fail.
## Anti Pattern
A new object or an extension field without the prefix, a suffix instead of a prefix (`"Receipt Printed SPU"`), or another company's affix copied from sample code. Also wrong: demanding the prefix on members of an object that is already `SPU`-prefixed, or on existing objects that predate the rule.
## References
StrongPoint policy: AL-Go pipeline prefix validation in every SP repository (workspace `CLAUDE.md`, "AL Object Rules").

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: ui
keywords: [tooltip, page-field, aa0218, lc0064, inheritance, table-field-tooltip]
technologies: [al]
countries: [w1]
application-area: [all]
---
# StrongPoint page fields declare their own ToolTip
## Description
StrongPoint repositories configure CodeCop AA0218 as an **error** in `sp.ruleset.json` and disable LinterCop LC0064 (which suggests moving tooltips to the table field). The team observed tooltips defined only on the table field not showing in the client, so SP policy is a page-level `ToolTip` on every user-facing page field and action, regardless of runtime version. This overrides the general guidance to rely on table-field tooltip inheritance on runtime 13.0 and later.
## Best Practice
Declare `ToolTip` on each page field and action in SP pages and page extensions, even when the source table field also has one. Treat a user-facing page field without a page-level `ToolTip` as a `major` finding — AA0218 fails the SP build. Keep the text useful: say what the value is for, not "Specifies the <caption>".
## Anti Pattern
Recommending to remove page-level tooltips because the table field already has one, or to move them to the table (the LC0064 suggestion). Also wrong: accepting a page field with no page-level `ToolTip` on the grounds that it inherits one from the table.
## References
StrongPoint policy: `sp.ruleset.json` in SP repositories — AA0218 `Error` ("ToolTip property for page fields must be mandatory"), LC0064 `None` ("does not show in client if ToolTip missing in page"). Shared rule this overrides: `microsoft/knowledge/ui/bound-page-field-inherits-source-field-tooltip.md`.

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: upgrade
keywords: [object-id, field-id, renumber, breaking-change, upgrade-codeunit, upgrade-tag, pipeline]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Released object and field IDs never change in StrongPoint apps
## Description
Once any release of an SP app exists, the AL-Go pipeline rejects a change to an existing object ID or table field ID. Changing an ID is a schema break: installed tenants would lose the data in the old object or field. When data really has to move — a field replaced by a new one, a table split — the old element stays (marked obsolete), a new element gets a new ID, and an upgrade codeunit copies the data.
## Best Practice
Never renumber released objects or fields, even to tidy a range. For a data move, add the new object or field, mark the old one `ObsoleteState = Pending`, and migrate in a `Subtype = Upgrade` codeunit's `OnUpgradePerCompany`, one upgrade tag per step (`HasUpgradeTag` / `SetUpgradeTag`), with every tag registered in an `OnGetPerCompanyUpgradeTags` subscriber and matching install-code handling for new installs. `SPU FB Upgrade` in SP-LSC-Fiscal-Printing is the reference implementation. Report a changed released ID as `blocker`.
## Anti Pattern
Editing the number in `table 70760600` or `field(12; ...)` of a released object, deleting a released field instead of obsoleting it, or migrating data without an upgrade tag so it re-runs on every upgrade.
## References
StrongPoint policy: workspace `CLAUDE.md` ("Breaking change rule", "Upgrade Codeunits"); reference code `SP-LSC-Fiscal-Printing/.../SPUFBUpgrade.Codeunit.al`. Related shared rules: `microsoft/knowledge/upgrade/use-upgrade-tags-not-version-checks.md`, `microsoft/knowledge/upgrade/register-upgrade-tags-with-subscribers.md`, `microsoft/knowledge/breaking-changes/obsolete-table-fields-instead-of-deleting-them.md`.

View file

@ -0,0 +1,26 @@
---
bc-version: [all]
domain: web-services
keywords: [device, hardware, httpclient, sp-device-manager, payment-terminal, fiscal-printer, sco]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Hardware devices are reached through SP Device Manager, not HttpClient
## Description
StrongPoint extensions talk to physical devices — payment terminals (EMV), fiscal printers, self-checkout terminals — only through the `SP Device Manager` app: its `SPU Device Manager` record (per store / POS terminal, holding device settings such as the fiscal protocol version) and the `SPU Device Manager` / `SPU Device Communication` codeunits, which exchange JSON with the Device Manager Windows service. Keeping device I/O in one place is what lets device settings, timeouts, logging and the service endpoint be managed per terminal.
## Best Practice
For new device interaction, read the device setup from the `SPU Device Manager` record and send requests through the Device Manager codeunits, following how the existing EMV and Fiscal Printing apps do it. Report a new direct `HttpClient` call to a device endpoint as `major`.
## Anti Pattern
A device integration that builds its own `HttpClient` request to a terminal or printer URL, stores device endpoints in its own setup table, or bypasses the per-terminal Device Manager record. Not a violation: `HttpClient` used for ordinary web services that are not devices (for example calls to external business APIs).
## References
StrongPoint architecture: `SP-Device-Manager` is the declared dependency for device I/O in SP-LSC-EMV-Integration, SP-LSC-Fiscal-Printing and SP-LSC-SCO-Integration (workspace `CLAUDE.md`, "SP-Device-Manager (Foundation)").