Install
$ agentstack add skill-louage-frw-agentic-coding-skill-events ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Skill: AL Event-Driven Architecture
Purpose
Discover, design, and implement event-driven patterns in AL Business Central extensions: event discovery via Event Recorder, subscriber/publisher creation, IsHandled patterns, and extensibility design.
When to Load
This skill should be loaded when:
- Extending standard BC functionality (posting, validation, master data changes)
- Designing custom integration events for a new codeunit or extension
- A subscriber is not firing and needs investigation
- An architecture requires extensibility points for future consumers
- Implementing document posting interventions, workflow triggers, or UI customizations
Core Patterns
Pattern 1: Event Discovery with Event Recorder
Before writing any subscriber, discover which events are raised during the business process:
1. Open the Event Recorder in VS Code ← VS Code command (not an agent tool)
2. Start recording
3. Execute the business process in BC (e.g., post a sales order)
4. Stop recording
5. Analyze the event list, filter by object or keyword
6. Identify the OnBefore/OnAfter event closest to your extension point
Use al_search_objects and al_get_object_definition to inspect publisher signatures. Use al_find_references to see who else subscribes to the same event.
Pattern 2: Event Subscriber
// Subscriber codeunit, name with "Handler" suffix
codeunit 50100 "Sales Document Events Handler"
{
// Attribute breakdown:
// ObjectType, Table, Codeunit, Page, Report, etc.
// ObjectId, Database::"Sales Header" or Codeunit::"Customer Management"
// EventName, exact name of the publisher procedure (e.g., OnBeforeInsert)
// ElementName, '' for table/codeunit events; field name for page field triggers
// SkipOnMissingLicense, false (recommended: always run)
// SkipOnMissingPermission, false (recommended: always run)
[EventSubscriber(ObjectType::Table, Database::"Sales Header",
OnBeforeInsert, '', false, false)]
local procedure OnBeforeInsertSalesHeader(
var SalesHeader: Record "Sales Header";
RunTrigger: Boolean)
begin
ValidateCustomFields(SalesHeader);
end;
}
Subscriber rules:
- Procedure MUST be
local - Parameter signature MUST exactly match the publisher (name, type, order)
- One subscriber per event per codeunit (keep handler codeunits focused)
- Never use
Commitinside a subscriber unless absolutely necessary
Pattern 3: Integration Event Publisher with IsHandled
The standard extensibility pattern: OnBefore allows interception, OnAfter allows reaction.
codeunit 50101 "Customer Management"
{
procedure CreateCustomer(var Customer: Record Customer): Boolean
var
IsHandled: Boolean;
begin
// 1. Raise OnBefore, subscribers can validate, modify, or skip
OnBeforeCreateCustomer(Customer, IsHandled);
if IsHandled then
exit(true);
// 2. Default logic
if not Customer.Insert(true) then
exit(false);
// 3. Raise OnAfter, subscribers can react (logging, integration, etc.)
OnAfterCreateCustomer(Customer);
exit(true);
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeCreateCustomer(
var Customer: Record Customer;
var IsHandled: Boolean)
begin
// Empty body, subscribers provide the implementation
// IsHandled = true → skip default logic
end;
[IntegrationEvent(false, false)]
local procedure OnAfterCreateCustomer(var Customer: Record Customer)
begin
// Empty body, subscribers react after creation
end;
}
IntegrationEvent attribute:
IntegrationEvent(IncludeSender, GlobalVarAccess)IncludeSender = false, recommended for clean API (no coupling to publisher internals)GlobalVarAccess = false, recommended (prevent subscribers from accessing publisher global vars)
Pattern 4: Business Event
Business events are user-disablable, use for optional, non-critical functionality:
[BusinessEvent(IncludeSender)]
local procedure OnCustomerCreditLimitExceeded(
Customer: Record Customer;
RequestedAmount: Decimal)
begin
// Subscribers may send notification, log, or block, user can disable
end;
| Attribute | Cannot be disabled | Use for | |---|---|---| | IntegrationEvent | Correct | Critical business logic, posting interventions | | BusinessEvent | Can be disabled by user | Optional features, external integrations, notifications |
Pattern 5: Well-Designed Event Parameters
// ✅ Good, rich context, var where modification expected, IsHandled for OnBefore
[IntegrationEvent(false, false)]
local procedure OnBeforePostDocument(
var DocumentHeader: Record "Sales Header"; // var, subscriber may modify
var DocumentLines: Record "Sales Line"; // var, subscriber may modify
PostingDate: Date; // value, read-only context
var IsHandled: Boolean) // var, subscriber may skip default
begin
end;
// ✅ Good, results context for OnAfter
[IntegrationEvent(false, false)]
local procedure OnAfterPostDocument(
DocumentHeader: Record "Sales Header"; // value, read-only (already posted)
PostedDocumentNo: Code[20]; // result reference
PostingResult: Boolean) // success/failure
begin
end;
Parameter design rules:
var Record, when subscribers should be able to modify the record- Non-var
Record, when record is read-only context - Always include
var IsHandled: BooleaninOnBeforeevents - Include result/outcome parameters in
OnAfterevents - Use meaningful parameter names that describe business intent
Workflow
Step 1: Discover Events
- Open the Event Recorder in VS Code (a VS Code command, not an agent tool)
- Execute the target business process in BC
- Review recorded events, filter by relevant object
- Use
al_get_object_definitionto inspect publisher signatures - Use
al_find_referencesto check existing subscribers (avoid conflicts)
Step 2: Design Event Architecture
For subscribing to existing events:
- Identify the right OnBefore / OnAfter event from Step 1
- Match the parameter signature exactly
- Create a handler codeunit with naming:
"{Domain} Events Handler"
For creating new integration events in your codeunit:
- Identify logical extension points in your business logic
- Add
OnBeforeevent withIsHandledpattern before key operations - Add
OnAfterevent after key operations with result context - Follow naming:
OnBefore{Action}/OnAfter{Action} - Choose
IntegrationEvent(critical) orBusinessEvent(optional)
Step 3: Implement
Use the AL: Insert Event Subscriber VS Code command (not an agent tool) to scaffold subscriber/publisher structures, then fill the body:
// Subscriber handler codeunit
codeunit 50102 "Customer Validation Handler"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management",
OnBeforeCreateCustomer, '', false, false)]
local procedure ValidateOnBeforeCreate(
var Customer: Record Customer;
var IsHandled: Boolean)
begin
ValidateCustomerCreditLimit(Customer);
if ShouldSkipDefaultProcessing(Customer) then
IsHandled := true;
end;
}
Step 4: Verify
- Build:
al_build, check for compilation errors (signature mismatches) - Set breakpoint inside subscriber
- Debug:
al_debug - Execute the business process, confirm subscriber fires
- If subscriber does NOT fire, check:
- Signature mismatch (most common cause)
- Wrong
ObjectType/ObjectId - Extension not published/active
- Another subscriber set
IsHandled = truebefore yours - SkipOnMissingLicense / SkipOnMissingPermission attributes
Common Scenarios
| Scenario | Event Source | Pattern | |---|---|---| | Validate before posting | OnBeforePost* on Codeunit "Sales-Post" | Subscriber + IsHandled | | Add fields to sales order | OnAfterInsert on Table "Sales Header" | Subscriber (set defaults) | | External integration trigger | OnAfterPostDocument (custom) | Publisher in your codeunit | | Custom approval workflow | OnBeforeRelease* | Subscriber + IsHandled to block | | UI field validation | Page trigger OnAfterValidate on field | Subscriber with ElementName | | Master data change audit | OnAfterModify on Table "Customer" | Subscriber (log changes) |
References
- Event Recorder, Microsoft Docs
- Events in AL
- IntegrationEvent Attribute
- BusinessEvent Attribute
- EventSubscriber Attribute
Constraints
- This skill covers event discovery, design, and implementation patterns, it does NOT duplicate the passive rules in
al-events.instructions.md(auto-applied to all.alfiles) - Do NOT create publishers without an
OnBefore+OnAfterpair at minimum - Do NOT use
Commitinside event subscribers unless absolutely required - Do NOT set
IsHandled = truewithout clear documentation of why default logic is skipped - Subscriber debugging (breakpoints, snapshots) → load
skill-debug.md - Subscriber performance overhead analysis → load
skill-performance.md
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Louage
- Source: Louage/frw-agentic-coding
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.