happy-technologies-llc/happy-platform-skills/business-rules

Claude Code and agent skill by happy-technologies-llc/happy-platform-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Develop secure, efficient business rules with correct timing, conditions, Glide API patterns, error handling, and tests.

Install

npx vskill@latest install happy-technologies-llc/happy-platform-skills/business-rules

SKILL.md

Business Rules Development

Overview

Business rules are server-side scripts that execute when records are displayed, inserted, updated, deleted, or queried. They are the backbone of ServiceNow automation.

  • What problem does it solve? Automates record-level logic, enforces data integrity, and triggers workflows based on record changes
  • Who should use this skill? ServiceNow developers building custom automation logic
  • Expected outcomes: Well-structured, performant business rules that follow ServiceNow best practices

Prerequisites

  • Roles: admin or scoped app developer role
  • Knowledge: JavaScript fundamentals, GlideRecord API basics
  • Access: sys_script table, target table for business rule
  • Related skills: admin/script-execution, admin/update-set-management

When to Use Business Rules

Timing Matrix
WhenTriggerUse Casecurrent/previous
beforeInsert/Update/DeleteValidate data, set field values, abort operationsBoth available
afterInsert/Update/DeleteCreate related records, send notifications, external integrationsBoth available
asyncInsert/Update/DeleteLong-running operations, external API callsOnly current
displayQuery/DisplayCalculate runtime values, populate scratchpadOnly current
Decision Guide

Use BEFORE when:

  • Setting default values based on other fields
  • Validating data before save
  • Modifying field values before commit
  • Aborting invalid operations with current.setAbortAction(true)

Use AFTER when:

  • Creating child/related records
  • Sending notifications (after record is committed)
  • Updating other tables
  • Triggering workflows

Use ASYNC when:

  • Making external REST/SOAP calls
  • Processing large data sets
  • Operations that can fail without blocking the user
  • Long-running calculations

Use DISPLAY when:

  • Calculating values for form display only
  • Populating g_scratchpad for client scripts
  • Runtime-only field values (not stored)

Procedure

Phase 1: Create a Business Rule
Step 1.1: Query Table Schema

First, understand the target table structure.

Using MCP:

Tool: SN-Get-Table-Schema
Parameters:
table_name: incident
Step 1.2: Create Basic Business Rule

Using MCP:

Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Set Priority Based on Impact and Urgency
collection: incident
active: true
when: before
order: 100
filter_condition: impactCHANGES^ORurgencyCHANGES
script: |
(function executeRule(current, previous /*null when async*/) {
// Calculate priority from impact and urgency matrix
var impact = parseInt(current.impact);
var urgency = parseInt(current.urgency);
// Priority matrix: 1=Critical, 2=High, 3=Moderate, 4=Low, 5=Planning
var matrix = {
'1-1': 1, '1-2': 2, '1-3': 3,
'2-1': 2, '2-2': 3, '2-3': 4,
'3-1': 3, '3-2': 4, '3-3': 5
};
var key = impact + '-' + urgency;
var newPriority = matrix[key] || 4;
if (current.priority != newPriority) {
current.priority = newPriority;
gs.info('Priority calculated: ' + newPriority + ' for ' + current.number);
}
})(current, previous);
Step 1.3: Create After Business Rule

Using MCP:

Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Create Related Task on P1 Incident
collection: incident
active: true
when: after
order: 200
filter_condition: priority=1^stateVALCHANGES1
script: |
(function executeRule(current, previous /*null when async*/) {
// Only on insert or when becoming P1
if (current.operation() == 'insert' ||
(previous && previous.priority != 1)) {
var task = new GlideRecord('sc_task');
task.initialize();
task.short_description = 'P1 Response: ' + current.short_description;
task.description = 'Critical incident requires immediate response.\n\nIncident: ' + current.number;
task.assignment_group = current.assignment_group;
task.assigned_to = current.assigned_to;
task.priority = 1;
task.parent = current.sys_id;
var taskId = task.insert();
gs.info('Created P1 response task: ' + taskId + ' for ' + current.number);
}
})(current, previous);
Step 1.4: Create Async Business Rule

Using MCP:

Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Notify External System on Incident Create
collection: incident
active: true
when: async
order: 500
action_insert: true
script: |
(function executeRule(current, previous /*null when async*/) {
try {
var request = new sn_ws.RESTMessageV2('External Notification', 'POST');
request.setStringParameterNoEscape('incident_number', current.number.toString());
request.setStringParameterNoEscape('short_description', current.short_description.toString());
request.setStringParameterNoEscape('priority', current.priority.toString());
var response = request.execute();
var httpStatus = response.getStatusCode();
if (httpStatus == 200 || httpStatus == 201) {
gs.info('External notification sent for: ' + current.number);
} else {
gs.error('External notification failed: ' + httpStatus + ' - ' + response.getBody());
}
} catch (e) {
gs.error('External notification error: ' + e.message);
}
})(current, previous);
Phase 2: Using current and previous Objects
Step 2.1: Understanding current vs previous

The current object represents the record being processed. The previous object contains field values before the current transaction.

Key Differences:

Aspectcurrentprevious
AvailabilityAll business rulesbefore/after only (null in async)
ModifiableYes (before rules)No (read-only)
Insert operationsHas valuesnull
ValuesNew/modifiedOriginal before change
Step 2.2: Detecting Field Changes

Using .changes() Method (Recommended):

Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Log State Changes
collection: incident
active: true
when: after
order: 100
script: |
(function executeRule(current, previous /*null when async*/) {
// EFFICIENT: Exit early if field hasn't changed
if (!current.state.changes()) {
return; // No work to do
}
// Get old and new values
var oldState = previous ? previous.state.getDisplayValue() : '(new)';
var newState = current.state.getDisplayValue();
gs.info('Incident ' + current.number + ' state changed: ' + oldState + ' -> ' + newState);
// Add work note
current.work_notes = 'State changed from ' + oldState + ' to ' + newState;
})(current, previous);

Manual Change Detection (When .changes() Not Suitable):

javascript
// For complex comparisons or calculated changes
if (previous && current.priority != previous.priority) {
// Priority changed
}
// For reference fields, compare sys_id
if (previous && current.assigned_to.toString() != previous.assigned_to.toString()) {
// Assignment changed
}
// For multiple fields
var fieldsToCheck = ['state', 'priority', 'assigned_to'];
var changedFields = [];
for (var i = 0; i < fieldsToCheck.length; i++) {
var field = fieldsToCheck[i];
if (current[field].changes()) {
changedFields.push(field);
}
}
if (changedFields.length > 0) {
gs.info('Changed fields: ' + changedFields.join(', '));
}
Step 2.3: Checking Operation Type
javascript
// Determine what triggered the business rule
var operation = current.operation();
switch (operation) {
case 'insert':
// Record is being created
gs.info('New record: ' + current.getTableName());
break;
case 'update':
// Record is being updated
gs.info('Update to: ' + current.getUniqueValue());
break;
case 'delete':
// Record is being deleted
gs.info('Deleting: ' + current.getUniqueValue());
break;
}
Phase 3: Condition Field vs Script Conditions
Step 3.1: Condition Field (Preferred for Simple Conditions)

The condition field uses encoded queries and is evaluated BEFORE the script runs. This is more efficient because:

  • Evaluated at the database level
  • Script never executes if condition fails
  • No JavaScript overhead

Best Practices for Condition Field:

# Only run on active P1 incidents
active=true^priority=1
# Only when state changes to Resolved
stateVALCHANGES6
# Only when assigned_to changes
assigned_toCHANGES
# Multiple conditions (AND)
active=true^priority=1^stateVALCHANGES6
# Only on insert (no previous value for state)
stateISEMPTYfalse^ORstateISEMPTY

Common Condition Operators:

OperatorMeaningExample
CHANGESField value changedstateCHANGES
VALCHANGESChanged TO specific valuestateVALCHANGES6
CHANGESFROMChanged FROM specific valuestateCHANGESFROM1
=Equalspriority=1
!=Not equalsstate!=7
ISEMPTYField is emptyassigned_toISEMPTY
ISNOTEMPTYField has valueassigned_toISNOTEMPTY
Step 3.2: Script Conditions (For Complex Logic)

Use script conditions only when the condition field cannot express the logic.

Using MCP:

Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Complex Condition Example
collection: incident
active: true
when: before
order: 100
condition: |
// Script condition - returns true/false
// Available: current, previous
// Check if escalating (priority going from higher number to lower)
if (current.priority.changes()) {
var oldPri = previous ? parseInt(previous.priority) : 5;
var newPri = parseInt(current.priority);
return newPri < oldPri; // True if escalating
}
return false;
script: |
(function executeRule(current, previous /*null when async*/) {
// This only runs if condition returned true
current.work_notes = 'Incident escalated from P' + previous.priority + ' to P' + current.priority;
gs.info('Escalation detected: ' + current.number);
})(current, previous);

When to Use Script Conditions:

  • Complex date calculations
  • Cross-table validations
  • Dynamic conditions based on user roles
  • Calculations involving multiple fields
  • Conditions requiring GlideRecord queries
Phase 4: Common Glide API Methods
Step 4.1: GlideRecord Essentials
javascript
// Query records
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('priority', 1);
gr.orderByDesc('sys_created_on');
gr.setLimit(10);
gr.query();
while (gr.next()) {
gs.info('Found: ' + gr.number + ' - ' + gr.short_description);
}
// Get single record by sys_id
var incident = new GlideRecord('incident');
if (incident.get('sys_id_value')) {
gs.info('Found: ' + incident.number);
}
// Get by field value
var user = new GlideRecord('sys_user');
if (user.get('user_name', 'admin')) {
gs.info('Admin sys_id: ' + user.sys_id);
}
// Insert new record
var newTask = new GlideRecord('task');
newTask.initialize();
newTask.short_description = 'New task from business rule';
newTask.assignment_group = current.assignment_group;
var sysId = newTask.insert();
// Update record
var toUpdate = new GlideRecord('incident');
if (toUpdate.get('sys_id_value')) {
toUpdate.work_notes = 'Updated via business rule';
toUpdate.update();
}

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
29
Repository forks
11
7-day trend score
17

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/happy-technologies-llc/happy-platform-skills/business-rules/badge)](https://verified-skill.com/skills/happy-technologies-llc/happy-platform-skills/business-rules)

Something wrong with this skill or its page? Tell us.