mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 06:36:55 +01:00
Improve partner onboarding and documentation navigation (#174)
Lead with a complete plugin quick start and add task-oriented usage, troubleshooting, customization, and contribution guides. Preserve the broader plugin framing, correct conflicting contract guidance, support Agents folder reviews, and align repository validation. Convert existing sample references to clickable links without changing knowledge rules. Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
a21edfec46
commit
2b5550c346
276 changed files with 1287 additions and 756 deletions
|
|
@ -17,13 +17,13 @@ An agent is a user, but it cannot configure users or other agents, and it cannot
|
|||
|
||||
Document that intersection. Give the agent only the table and page rights its tasks need. Do not add user-setup or permission-assignment pages to the agent profile or permission sets; those operations will fail by design.
|
||||
|
||||
See sample: `agent-permissions-intersect-with-assigner.good.al`.
|
||||
See sample: [`agent-permissions-intersect-with-assigner.good.al`](agent-permissions-intersect-with-assigner.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Permission sets or profiles that include User card, Permission Set Assignment, or agent-admin pages, or comments that the agent runs as SUPER regardless of who assigned it. Detection signal: default access controls or profile including user-administration objects.
|
||||
|
||||
See sample: `agent-permissions-intersect-with-assigner.bad.al`.
|
||||
See sample: [`agent-permissions-intersect-with-assigner.bad.al`](agent-permissions-intersect-with-assigner.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ The agent only sees what its profile shows. Extra actions, views, and Role Cente
|
|||
|
||||
Ship an agent-specific profile and page customizations: hide unrelated actions, keep descriptive tooltips, add Role Center links to the few pages the agent should open. Prefer fewer navigation hops.
|
||||
|
||||
See sample: `agent-profile-narrows-visible-ui.good.al`.
|
||||
See sample: [`agent-profile-narrows-visible-ui.good.al`](agent-profile-narrows-visible-ui.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Assigning `BUSINESS MANAGER` or `ORDER PROCESSOR` as `GetDefaultProfile` so the agent can do anything. Detection signal: default profile equal to a full-user role with no agent page customizations.
|
||||
|
||||
See sample: `agent-profile-narrows-visible-ui.bad.al`.
|
||||
See sample: [`agent-profile-narrows-visible-ui.bad.al`](agent-profile-narrows-visible-ui.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Instance setup is not a Card or StandardDialog. The toolkit expects `PageType =
|
|||
|
||||
Declare `PageType = ConfigurationDialog`, host `part(...; "Agent Setup Part")`, and put agent-specific fields in another group. Keep system OK/Cancel. Use a temporary source record and defer persistence until Update, as described in `agent-setup-source-table-is-temporary.md`. Following Microsoft's agent setup samples, set `Extensible = false`.
|
||||
|
||||
See sample: `agent-setup-page-is-configuration-dialog.good.al`.
|
||||
See sample: [`agent-setup-page-is-configuration-dialog.good.al`](agent-setup-page-is-configuration-dialog.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A Card or StandardDialog setup page with no `Agent Setup Part`. Detection signal: setup page ID from `IAgentFactory` / `IAgentMetadata` whose page is not `ConfigurationDialog` or has no `Agent Setup Part`.
|
||||
|
||||
See sample: `agent-setup-page-is-configuration-dialog.bad.al`.
|
||||
See sample: [`agent-setup-page-is-configuration-dialog.bad.al`](agent-setup-page-is-configuration-dialog.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ ConfigurationDialog setup is a draft: the user can Cancel without writing. That
|
|||
|
||||
Mark the page `SourceTableTemporary = true`. Copy into the temp record on open. Persist the Agent Setup buffer and custom fields only from the close path when the action is not Cancel, using `Agent Setup.GetChangesMade` / `SaveChanges`.
|
||||
|
||||
See sample: `agent-setup-source-table-is-temporary.good.al`.
|
||||
See sample: [`agent-setup-source-table-is-temporary.good.al`](agent-setup-source-table-is-temporary.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A non-temporary source table, or `Insert`/`Modify` on the persisted setup row from field OnValidate. Detection signal: agent `ConfigurationDialog` without `SourceTableTemporary = true`, or database writes before Update.
|
||||
|
||||
See sample: `agent-setup-source-table-is-temporary.bad.al`.
|
||||
See sample: [`agent-setup-source-table-is-temporary.bad.al`](agent-setup-source-table-is-temporary.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Each agent instance is a user. Instance-specific setup is keyed by that user's `
|
|||
|
||||
Give the setup table a Guid field `User Security ID` as the clustered primary key. Other settings are attributes of that key. When the page opens, `Get` or insert by the Guid the Agent Setup part already holds.
|
||||
|
||||
See sample: `agent-setup-table-keyed-by-user-security-id.good.al`.
|
||||
See sample: [`agent-setup-table-keyed-by-user-security-id.good.al`](agent-setup-table-keyed-by-user-security-id.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A setup table keyed by Code, Integer, or with no Guid user key, then mapping one row to every instance. Detection signal: source table of the agent setup page whose primary key is not `User Security ID`.
|
||||
|
||||
See sample: `agent-setup-table-keyed-by-user-security-id.bad.al`.
|
||||
See sample: [`agent-setup-table-keyed-by-user-security-id.bad.al`](agent-setup-table-keyed-by-user-security-id.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Validate inbound payloads in analysis: Error when the task must not run; Warning when a human must confirm. For outbound messages, adjust text in this method rather than in a later subscriber. Do not rely on skip-review to bypass warnings.
|
||||
|
||||
See sample: `analyze-message-error-stops-warning-forces-review.good.al`.
|
||||
See sample: [`analyze-message-error-stops-warning-forces-review.good.al`](analyze-message-error-stops-warning-forces-review.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Ignoring analysis entirely, or emitting Warning while documenting that `SetRequiresReview(false)` means unattended run. Detection signal: empty `AnalyzeAgentTaskMessage` plus skip-review on external input.
|
||||
|
||||
See sample: `analyze-message-error-stops-warning-forces-review.bad.al`.
|
||||
See sample: [`analyze-message-error-stops-warning-forces-review.bad.al`](analyze-message-error-stops-warning-forces-review.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Page-filter tweaks, extra validation, and prompt dialogs for the agent should no
|
|||
|
||||
On `OnAfterInitialization`, exit unless `Agent Session.IsAgentSession`. Then `BindSubscription` a single-instance codeunit that holds the current task id. Keep those subscribers internal.
|
||||
|
||||
See sample: `bind-agent-subscribers-only-in-agent-session.good.al`.
|
||||
See sample: [`bind-agent-subscribers-only-in-agent-session.good.al`](bind-agent-subscribers-only-in-agent-session.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Event subscribers on `Sales Header` OnAfterInsert that always `Message` the agent, with no `IsAgentSession` guard. Detection signal: agent-only behaviour in a static subscriber that is not bind-gated.
|
||||
|
||||
See sample: `bind-agent-subscribers-only-in-agent-session.bad.al`.
|
||||
See sample: [`bind-agent-subscribers-only-in-agent-session.bad.al`](bind-agent-subscribers-only-in-agent-session.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ For isolation, `Agent`, `Agent Task Builder`, and related toolkit codeunits erro
|
|||
|
||||
Expose a public codeunit in the agent app (`Access = Public`) whose procedures take `User Security ID` and forward to `Agent` / `Agent Task Builder`. Document that surface as the integration contract. Keep toolkit calls inside that app.
|
||||
|
||||
See sample: `cross-app-agent-calls-need-your-public-api.good.al`.
|
||||
See sample: [`cross-app-agent-calls-need-your-public-api.good.al`](cross-app-agent-calls-need-your-public-api.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
From app B, calling `Agent.SetDisplayName` or `Agent.Create` with app A's metadata provider. Detection signal: toolkit agent APIs used with an `Agent Metadata Provider` value not declared in the same app.
|
||||
|
||||
See sample: `cross-app-agent-calls-need-your-public-api.bad.al`.
|
||||
See sample: [`cross-app-agent-calls-need-your-public-api.bad.al`](cross-app-agent-calls-need-your-public-api.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Create instances from a setup page, a wizard, or another UI-driven path after the user is in a client session. Apply instructions and `Activate` there. For existing companies after an upgrade, document that an admin must open setup; do not create from the upgrade codeunit.
|
||||
|
||||
See sample: `do-not-create-agents-in-install-upgrade-or-background.good.al`.
|
||||
See sample: [`do-not-create-agents-in-install-upgrade-or-background.good.al`](do-not-create-agents-in-install-upgrade-or-background.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Agent.Create` inside `OnInstallAppPerCompany`, `OnUpgradePerCompany`, or a job-queue codeunit. The call fails at runtime even if it compiles. Detection signal: `Agent.Create` in `Subtype = Install`, `Subtype = Upgrade`, or a non-UI session.
|
||||
|
||||
See sample: `do-not-create-agents-in-install-upgrade-or-background.bad.al`.
|
||||
See sample: [`do-not-create-agents-in-install-upgrade-or-background.bad.al`](do-not-create-agents-in-install-upgrade-or-background.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ application-area: [all]
|
|||
|
||||
Insert only the permission sets the agent needs. For an AL `permissionset` object, use `Scope::System` and the ID of the app that defines it. Recreate permission sets that exist only as user-defined configuration in Business Central as AL objects first. Prefer a dedicated permission set over a full-user role.
|
||||
|
||||
See sample: `get-default-access-controls-least-privilege.good.al`.
|
||||
See sample: [`get-default-access-controls-least-privilege.good.al`](get-default-access-controls-least-privilege.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Empty `GetDefaultAccessControls`, or inserting `SUPER` / `D365 BUS FULL ACCESS` because it made the demo work. Detection signal: Role ID on the default buffer that is a full-user role, or a set that is not in the app.
|
||||
|
||||
See sample: `get-default-access-controls-least-privilege.bad.al`.
|
||||
See sample: [`get-default-access-controls-least-privilege.bad.al`](get-default-access-controls-least-privilege.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ application-area: [all]
|
|||
|
||||
Ship a `profile` object (and page customizations) in the app. In `GetDefaultProfile`, call `Agent.PopulateDefaultProfile` with that profile ID and `NavApp.GetCurrentModuleInfo`. Include UI-exported customizations as AL.
|
||||
|
||||
See sample: `get-default-profile-lives-in-the-app.good.al`.
|
||||
See sample: [`get-default-profile-lives-in-the-app.good.al`](get-default-profile-lives-in-the-app.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Setting `TempAllProfile."Profile ID"` to a client-only profile, or skipping `GetDefaultProfile`. Detection signal: factory default profile ID with no matching `profile` object in the app.
|
||||
|
||||
See sample: `get-default-profile-lives-in-the-app.bad.al`.
|
||||
See sample: [`get-default-profile-lives-in-the-app.bad.al`](get-default-profile-lives-in-the-app.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ The runtime treats instructions as the agent's standing prompt. A one-line goal
|
|||
|
||||
Store a document that states responsibilities, then non-negotiable guidelines (when to request a review, when not to post), then numbered steps for each task. Keep that text in the resource you pass to `SetInstructions`.
|
||||
|
||||
See sample: `instruction-structure-is-role-rules-steps.good.al`.
|
||||
See sample: [`instruction-structure-is-role-rules-steps.good.al`](instruction-structure-is-role-rules-steps.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A single sentence such as Check customer credit for the sales order. Detection signal: instruction resource or `SetInstructions` payload with no responsibilities / guidelines / steps sections.
|
||||
|
||||
See sample: `instruction-structure-is-role-rules-steps.bad.al`.
|
||||
See sample: [`instruction-structure-is-role-rules-steps.bad.al`](instruction-structure-is-role-rules-steps.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ Agent tools are the UI the profile exposes. Action names and tool ids change acr
|
|||
|
||||
Write steps as business outcomes (release the order, set the hold reason). Tell the agent to memorize identifiers it must reuse. Do not hard-code action captions or tool ids.
|
||||
|
||||
See sample: `instructions-describe-work-not-tool-ids.good.al`.
|
||||
See sample: [`instructions-describe-work-not-tool-ids.good.al`](instructions-describe-work-not-tool-ids.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Instructions that say invoke SalesOrder.Post_Promoted or use tool page-42-action-3. Detection signal: instruction text containing Promoted action names or tool identifiers.
|
||||
|
||||
See sample: `instructions-describe-work-not-tool-ids.bad.al`.
|
||||
See sample: [`instructions-describe-work-not-tool-ids.bad.al`](instructions-describe-work-not-tool-ids.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Static instructions stored as an app resource are copied onto an instance only w
|
|||
|
||||
In the upgrade codeunit, find existing instances of your metadata provider and call `SetInstructions` again with `NavApp.GetResourceAsText`. Guard with an upgrade tag so the rewrite runs once per version that changes the file.
|
||||
|
||||
See sample: `reapply-resource-instructions-on-upgrade.good.al`.
|
||||
See sample: [`reapply-resource-instructions-on-upgrade.good.al`](reapply-resource-instructions-on-upgrade.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Editing only the resource file, or calling `SetInstructions` solely from the first-time setup path. Detection signal: instruction resource in `resourceFolders` with no upgrade procedure that re-applies it.
|
||||
|
||||
See sample: `reapply-resource-instructions-on-upgrade.bad.al`.
|
||||
See sample: [`reapply-resource-instructions-on-upgrade.bad.al`](reapply-resource-instructions-on-upgrade.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ Each agent type needs a `Copilot Capability` enum value that the factory links a
|
|||
|
||||
Extend `Copilot Capability` with a unique value. In `OnInstallAppPerDatabase`, call `Copilot Capability.IsCapabilityRegistered` and, if false, `RegisterCapability` with availability, billing type, and a learn-more URL. Point `IAgentFactory` at that capability.
|
||||
|
||||
See sample: `register-copilot-capability-for-the-agent.good.al`.
|
||||
See sample: [`register-copilot-capability-for-the-agent.good.al`](register-copilot-capability-for-the-agent.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Shipping the agent enum without a `Copilot Capability` value, or adding the enum but never calling `RegisterCapability`. Duplicate ordinals across extensions also collide. Detection signal: agent metadata provider with no matching capability registration in an install codeunit.
|
||||
|
||||
See sample: `register-copilot-capability-for-the-agent.bad.al`.
|
||||
See sample: [`register-copilot-capability-for-the-agent.bad.al`](register-copilot-capability-for-the-agent.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Instructions are instance data, not an enum caption. `Agent.SetInstructions` tak
|
|||
|
||||
Load instruction text from a resource or builder into a `SecretText` variable and call `Agent.SetInstructions(AgentUserSecurityId, Instructions)` after `Create`. Keep one instruction document per instance.
|
||||
|
||||
See sample: `set-instructions-as-secrettext.good.al`.
|
||||
See sample: [`set-instructions-as-secrettext.good.al`](set-instructions-as-secrettext.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Passing a `Label` or `Text` to `SetInstructions`, storing instructions in a setup Text field without wrapping as `SecretText`, or putting the prompt only in a code comment. Detection signal: `SetInstructions` with a non-`SecretText` argument, or no `SetInstructions` after `Create`.
|
||||
|
||||
See sample: `set-instructions-as-secrettext.bad.al`.
|
||||
See sample: [`set-instructions-as-secrettext.bad.al`](set-instructions-as-secrettext.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Use `ShowCanCreateAgent` to decide discovery. If only agent administrators should see the type, return `Agent System Permissions.CurrentUserHasCanManageAllAgentsPermission`. Enforce extra policy inside your own create API. Never assume UI hiding blocks code.
|
||||
|
||||
See sample: `show-can-create-agent-does-not-block-code-create.good.al`.
|
||||
See sample: [`show-can-create-agent-does-not-block-code-create.good.al`](show-can-create-agent-does-not-block-code-create.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Returning `exit(false)` from `ShowCanCreateAgent` and then documenting that instances cannot be created, while page actions or other apps still call `Agent.Create`. Detection signal: `ShowCanCreateAgent` always false with no matching guard on programmatic create.
|
||||
|
||||
See sample: `show-can-create-agent-does-not-block-code-create.bad.al`.
|
||||
See sample: [`show-can-create-agent-does-not-block-code-create.bad.al`](show-can-create-agent-does-not-block-code-create.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Incoming task messages default to requiring user approval before the agent runs.
|
|||
|
||||
Leave the default review-on for anything that originated outside your extension. Call `SetRequiresReview(false)` only on messages you constructed from already-authorized BC data.
|
||||
|
||||
See sample: `skip-incoming-review-only-for-trusted-input.good.al`.
|
||||
See sample: [`skip-incoming-review-only-for-trusted-input.good.al`](skip-incoming-review-only-for-trusted-input.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`SetRequiresReview(false)` on simulated email, incoming webhooks, or user-free text. Detection signal: `SetRequiresReview(false)` next to external content with no prior validation.
|
||||
|
||||
See sample: `skip-incoming-review-only-for-trusted-input.bad.al`.
|
||||
See sample: [`skip-incoming-review-only-for-trusted-input.bad.al`](skip-incoming-review-only-for-trusted-input.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ The agent runtime looks for specific phrases: ask for assistance, request a revi
|
|||
|
||||
In the instruction resource, use those keywords at the decision points: request a review before posting; write an email only after stating that outbound mail is reviewed; memorize values the later steps need. Pair `Reply` / `Write an email` with an explicit review sentence.
|
||||
|
||||
See sample: `use-documented-instruction-keywords.good.al`.
|
||||
See sample: [`use-documented-instruction-keywords.good.al`](use-documented-instruction-keywords.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Inventing tool-like verbs (call Copilot, click Post_Promoted) or omitting request a review before posting. Detection signal: instruction text that says email the customer with no review keyword.
|
||||
|
||||
See sample: `use-documented-instruction-keywords.bad.al`.
|
||||
See sample: [`use-documented-instruction-keywords.bad.al`](use-documented-instruction-keywords.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ An AL agent type is registered by extending `Agent Metadata Provider`. The platf
|
|||
|
||||
On the enum value, set `Implementation` for all three interfaces, each pointing at a dedicated codeunit. Keep factory (create, defaults, first-time setup), metadata (setup page, summary, annotations), and task execution (message analysis, intervention suggestions) in separate objects.
|
||||
|
||||
See sample: `wire-all-three-agent-interfaces.good.al`.
|
||||
See sample: [`wire-all-three-agent-interfaces.good.al`](wire-all-three-agent-interfaces.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
An `Agent Metadata Provider` value with no `Implementation`, only one interface mapped, or all three interfaces pointing at one catch-all codeunit that cannot satisfy the contracts. Detection signal: enumextension of `Agent Metadata Provider` whose value does not list `IAgentFactory`, `IAgentMetadata`, and `IAgentTaskExecution`.
|
||||
|
||||
See sample: `wire-all-three-agent-interfaces.bad.al`.
|
||||
See sample: [`wire-all-three-agent-interfaces.bad.al`](wire-all-three-agent-interfaces.bad.al).
|
||||
|
||||
## See also
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ id: al-agents-review
|
|||
version: 1
|
||||
title: AL agents review
|
||||
description: Reviews AL source changes against agent guidance from BCQuality.
|
||||
inputs: [pr-diff, file-path]
|
||||
inputs: [pr-diff, file-path, folder-path]
|
||||
outputs: [findings-report]
|
||||
bc-version: [all]
|
||||
technologies: [al]
|
||||
|
|
@ -18,7 +18,10 @@ Reviews AL source changes against the `agents` knowledge domain in BCQuality and
|
|||
|
||||
Agent findings apply to AL files that implement or invoke Agent SDK surfaces, including agent interfaces, setup, creation, task execution, capability registration, profiles, access controls, instructions, and session-bound subscribers. Return `not-applicable` when the diff contains no AL changes or no Agent SDK implementation or usage.
|
||||
|
||||
An orchestrator invokes this skill with either a `pr-diff` or a `file-path`. The skill produces one JSON document conforming to the DO output contract.
|
||||
An orchestrator invokes this skill with a `pr-diff`, `file-path`, or
|
||||
`folder-path`. For a folder, review all relevant source below it under DO's
|
||||
current-state input semantics. The skill produces one JSON document
|
||||
conforming to the DO output contract.
|
||||
|
||||
## Source
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue