packages/opencode/src/bundled-skills/business-rule-patterns/SKILL.md
This skill should be used when the user asks to "create a business rule", "before insert", "after update", "async business rule", "business rule not working", "current vs previous", or any Business Rule development.
npx skillsauth add groeimetai/snow-flow business-rule-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Business Rules are server-side scripts that execute when records are displayed, inserted, updated, or deleted.
| Type | Timing | Use Case | Performance Impact | | ----------- | ------------------------- | ------------------------------------- | ------------------ | | Before | Before database write | Validate, modify current record | Low | | After | After database write | Create related records, notifications | Medium | | Async | Background (after commit) | Heavy processing, integrations | None (background) | | Display | When form loads | Modify form display, set defaults | Low |
// In Business Rules, these are always available:
current // The record being operated on
previous // The record BEFORE changes (update/delete only)
gs // GlideSystem utilities
Use for validation and field manipulation:
// Prevent update if condition not met
;(function executeRule(current, previous) {
if (current.state == 7 && previous.state != 6) {
current.setAbortAction(true)
gs.addErrorMessage("Must resolve before closing")
}
})(current, previous)
// Auto-populate fields
;(function executeRule(current, previous) {
if (current.isNewRecord()) {
current.setValue("caller_id", gs.getUserID())
current.setValue("opened_by", gs.getUserID())
}
})(current, previous)
Never do in Before rules:
current.update() (causes recursion!)Use for related record operations:
// Create child record when priority is P1
;(function executeRule(current, previous) {
if (current.priority.changesTo(1)) {
var task = new GlideRecord("task")
task.initialize()
task.setValue("short_description", "P1 Follow-up: " + current.number)
task.setValue("parent", current.sys_id)
task.insert()
}
})(current, previous)
// Update parent record
;(function executeRule(current, previous) {
var parent = new GlideRecord("problem")
if (parent.get(current.problem_id)) {
parent.setValue("related_incidents", parent.related_incidents + 1)
parent.update()
}
})(current, previous)
Use for heavy processing that shouldn't block the transaction:
// External integration
;(function executeRule(current, previous) {
var integrator = new ExternalSystemIntegration()
integrator.syncIncident(current.sys_id)
})(current, previous)
// Send custom notification
;(function executeRule(current, previous) {
gs.eventQueue("incident.priority.high", current, current.assigned_to, gs.getUserID())
})(current, previous)
current.isNewRecord() // True if insert
current.isValidRecord() // True if record exists
current.getValue("field") // Get field value
current.setValue("field", val) // Set field value
current.setAbortAction(true) // Cancel the operation
current.operation() // 'insert', 'update', 'delete'
current.isActionAborted() // Check if aborted
current.priority.changes() // Field changed (any value)
current.priority.changesTo(1) // Changed TO this value
current.priority.changesFrom(3) // Changed FROM this value
current.priority.nil() // Field is empty
// Check if field was modified
if (current.state != previous.state) {
gs.info("State changed from " + previous.state + " to " + current.state)
}
// Check specific change
if (current.assigned_to.changes() && !previous.assigned_to.nil()) {
gs.info("Reassignment occurred")
}
Use conditions to limit when the rule runs:
| Condition | Meaning |
| -------------------------------- | -------------------------- |
| current.active == true | Only active records |
| current.isNewRecord() | Only on insert |
| current.priority.changes() | Only when priority changes |
| gs.hasRole('admin') | Only for admins |
| current.assignment_group.nil() | Only when unassigned |
// Before Insert/Update
if (current.assignment_group.changes() && !current.assignment_group.nil()) {
var members = new GroupMembers(current.assignment_group)
current.assigned_to = members.getNextAvailable()
}
// After Update
if (current.state.changesTo(7)) {
// Closed
var tasks = new GlideRecord("task")
tasks.addQuery("parent", current.sys_id)
tasks.addQuery("state", "!=", 7)
tasks.query()
while (tasks.next()) {
tasks.setValue("state", 7)
tasks.update()
}
}
development
This skill should be used when the user asks to "App Engine Studio", "workspace builder", "custom workspace", "AES", "low code", "app development", "studio", or any ServiceNow App Engine Studio development.
tools
This skill should be used when the user asks to "create a widget", "build a widget", "service portal widget", "sp_widget", "fix widget", "widget not working", "ng-click not working", or any Service Portal widget development.
development
This skill should be used when the user asks to "create chatbot", "virtual agent", "VA topic", "NLU", "conversation", "chat flow", "topic block", or any ServiceNow Virtual Agent development.
development
This skill should be used when the user asks to "vendor", "supplier", "contract", "procurement", "SLA", "vendor risk", "vendor performance", or any ServiceNow Vendor Management development.