はじめに
今回はServiceNow Japanの「#BuildWithBuildAgentTokyo」企画の参加記事となります。
ServiceNow Build Agentでアプリを1つ作ってみました。
企画紹介記事↓
つくったもの/着想
作ったのは、「このレコードをこう更新したら、どの処理が動くのか」を、保存する前に設定から判定するアプリです。名前は「Record Operation Analyzer」としました。
既存機能であるAccess Analyzerから着想を得ました。
やるからには現場で使えそうなものをつくりたいなという気持ちがあり、このアイディアとなっています。
Build Agentに渡したプロンプトなどは最後に公開しますので、まずはなにができたかを見ていただければと!
完成したもの
ページ上部に対象テーブル、実行ユーザー、オペレーション(Insert/Update/Delete)、変更想定のフィールドとその値を入力し、「Analyze」ボタンを押すと・・・・

実行されるビジネスルールや通知を条件付きで解説してくれます!

(されないものも区分ごとに表示されます)
初回投稿後追記:イメージ動画(スマホは横画面推奨)
1. こんな問い合わせ、ありませんか
運用をしていると、次のような問い合わせがよく来ます。
・「担当グループを変えたのに、グループに通知が来ない」
・「このビジネスルール(BR)、なぜ動かないの?」
調べる方法はいくつかありますね。Debug Business Rule、Script Tracer、メールのログなどです。
ただ、どれも実際に操作したあとに見るものです。動いたものはログに出ますが、動かなかった理由までは分かりません。しかも本番環境では、「試しに更新してみる」わけにもいきません。
もちろん、そのためのテスト環境、非本番環境というものがあると思いますが、ServiceNowの設定箇所や関連処理は多岐にわたります。多岐にわたる箇所から理由も込みで調査する時間を効率化できないかなと思い作成しました。
もう1つ、データパッチの前にも同じ悩みがあります。
数件のレコードを手で直すとき、「この更新で通知が飛ばないか」「どのBRが動くか」を、直す前に知りたいことがあります。
「実際にやっちゃう前に答えられたらいいのに~~~」。これが今回のアイディアです。
2. つくったもの詳細: Record Operation Analyzer

メニュー: All > Record Operation Analyzer > Analyze)
メニューから開きます
使い方はシンプルで、次の項目を入れて「Analyze」を押すだけです。
・テーブル
・操作(Insert / Update / Delete)
・対象レコード(UpdateとDeleteのとき)
・変更後の値(複数行。値を空にする指定もできます)
・実行ユーザー
Analyzeを押すと、その操作で動く自動処理を、実行順に並べて表示します。対象にしたのは次の6種類です。
・Business Rule
・Data Policy
・通知
・イベント
・Script Action
・Flow
それぞれに、次のどれかの判定を付けます。
・発火: 条件に一致した
・不発: 条件に一致しなかった(どの条件が合わなかったかを表示)
・要目視: 設定からは判定できない(スクリプトの条件など)
・無効: 処理自体が無効、または未公開
・対象外: 操作の種類などが合わない
結果の上部です。変更した項目と、判定ごとの件数が出ます
見た目は、Access Analyzerの「Evaluate access」に寄せました。
Access Analyzerは、権限の判定をレイヤーごとに列で並べてくれますよね。あれと同じ考え方で、1つの処理について「操作 / ユーザー条件 / 条件 / スクリプト条件」の判定ステップを列に並べています。どこで落ちたかが一目で分かるようにしたかったんです。
構成は、スコープアプリの中にScript Include 11本と、UI Page(JellyとGlideAjax)1つです。解析は読み取り専用で、解析対象のレコードは保存しませんし、解析対象のスクリプトも実行しません。
3. 実例: 担当者の有無で通知が変わる
冒頭の「担当グループを変えたのに通知が来ない」を、実際に解析してみます。
OOTBの通知「Incident assigned to my group」は、レコードの更新で起動し、条件は次のとおりです。
assigned_toISEMPTY^assignment_groupVALCHANGES^assignment_groupISNOTEMPTY
「担当者が空」で、かつ「担当グループが変わった」ときに送られる、という条件ですね。
3-1. シナリオA: 担当者ありのまま、担当グループだけ変更
担当者が入っているインシデントで、担当グループだけを変更して解析します。


不発になり、理由に(assigned_toISEMPTY)が出ます
結果は不発です。理由の欄には、合わなかった条件として (assigned_toISEMPTY) が出ます。
3-2. シナリオB: 担当グループを変更し、担当者を空にする
次に、担当グループを変えると同時に、担当者を空にして解析します。
同じ通知が、今度は発火判定になりましたね。
発火に変わりました。注意の欄には、通知の設定(send_selfとweight)も出しています。send_selfは、操作した本人に通知を送るかどうかの設定なので、実行ユーザーによって受け取る人が変わることがあります。
つまり、「グループを変えたのに通知が来ない」の答えは「担当者が入っていたから」です。これを画面1枚で説明できるようになりました。
3-3. イベントの連鎖も追う
通知はレコードの更新だけでなく、イベントからも送られます。そこで、発火したBRのスクリプトから gs.eventQueue('イベント名', ...) を抜き出して、どのイベントが発行されるかも判定しています。
たとえばOOTBのBR「incident events」は、1つのスクリプトの中で、if文で条件を分けて複数のイベントを発行しています。アプリはこのif条件を読んで、今回の更新では incident.updated と incident.assigned.to.group は発行され、incident.commented などは発行されない、と判定します。
after BRのフェーズに「イベント発行」の行が出ます
発行されたイベントから、イベントで起動する通知とScript Actionをたどって、「イベント処理(非同期)」のフェーズに並べています。
※イベント発行の契機BRなどを結果にリンクさせたいんですが、改善予定です
3-4. もう1つの使いどころ: データパッチの前に
冒頭で触れたデータパッチの場面でも使えます。
数件のレコードをフォームやリストで手作業で直すと、通常の保存と同じなので、BRも通知もそのまま動きます。直す予定のレコードを選び、変更後の値を入れてAnalyzeすれば、通知が飛ぶか、副作用のあるBRが動くかを事前に確認できます。
動くと分かれば、事前に関係者へ連絡する、直し方を変える、といった判断ができますね。
スクリプトで一括更新するときは setWorkflow(false) を入れるのが通例だと思いますが、入れない場合や、入れるかどうかを決める前に影響を見たいときにも使えます。
使うときの注意点は次のとおりです。
・解析はレコード1件ずつです。数件なら1件ずつ解析すれば十分ですね。件数が多く、レコードごとに条件が変わる場合(担当者の有無など)は、パターンごとに代表のレコードを選んで解析します
・実行ユーザーは、実際に直す人を選びます。send_selfのように、ユーザーで結果が変わる判定があるためです
・解析は、BRやエンジンが動く通常の更新を前提にしています。setWorkflow(false) で更新するとBRは動きません(Docs「Classic Business rules」)。setWorkflow(false)は監査などBR以外の処理も止めるので、使うかどうかは別に判断が必要です
・フォームで直す場合はClient ScriptとUI Policyも動きますが、いまの版はサーバー側の処理だけを解析します(9章で触れます)
インポートでも考え方は同じです。Transform Mapの「Run business rules」を外すと、setWorkflow(false)で取り込まれます(Docs「Create a transform map」)。ただ、インポート経由での解析はまだ実機で試していません。
4. 判定のしくみ
ここからは中身の話です。
4-1. 保存しないGlideRecordで条件を評価する
中心にあるのは GlideFilter.checkRecord() です。
Updateでは対象レコードを get() してから変更後の値を setValue() し、Insertでは initialize() してから値を入れます。どちらも保存はしません。この状態のGlideRecordに対して、BRや通知の条件(エンコードクエリ)を評価します。
// 考え方を簡略化したもの
var gr = new GlideRecord('incident');
gr.get(targetSysId); // 変更前の値
gr.setValue('assignment_group', newGroupSysId); // 変更後の値(保存しない)
var matched = GlideFilter.checkRecord(gr, 'assignment_groupVALCHANGES', true);
get() のあとに setValue() すると、GlideRecordは変更前の値を覚えているので、VALCHANGES / CHANGESTO / CHANGESFROMといった「変わったかどうか」の演算子も評価できます。ドットウォークの条件、Flowのトリガー条件、Data Policyの条件も、同じ方法で評価できることを実機で確かめました。
4-2. 不発の理由を出す
「どの条件が合わなかったか」を出すために、エンコードクエリを分解して評価しています。
・^NQ で区切られたブロック同士はOR
・ブロックの中で ^ で区切られたグループ同士はAND
・^OR でつながった句は1つのグループで、グループの中はOR
この構造で評価して、falseになったANDのグループを理由として表示します。「最初にfalseになった句」を出すだけだと、^OR を含む条件で的外れな理由が出てしまうので、ここは丁寧に分けました。
4-3. 細かい落とし穴への対応
・大文字・小文字: checkRecord は大文字・小文字を区別します。そこで文字列を比べる句は、new GlideFilter(filter, title) に setCaseSensitive(true) と setCaseSensitive(false) の両方で評価し、結果が違えば要目視にしています
・ユーザーで変わる値: javascript:gs.getUserID() や、動的フィルター(Me、One of My Groups)は、選んだ実行ユーザーの値に置き換えてから評価する形です
・conditionスクリプト: スクリプトは実行しません。current.assignment_group.changes() のような単純な式1つだけのものは、条件に読み替えて評価します(例: OOTBの「reassignment counter」)。それ以外は要目視です
4-4. 実行順
並び順は、公式Docs「Scripts and engines execution order」に合わせました。
- before BR(order < 1000)
- beforeエンジン(Data Policyなど。Docs上は順不同)
- before BR(order >= 1000)
- データベースの操作
- after BR(order < 1000)
- afterエンジン(Flowのトリガーなど。Docs上は順不同)
- メール通知(weightの順)
- after BR(order >= 1000)
このあとに、非同期で動くもの(async BR、バックグラウンドのFlow)と、イベント処理を別のフェーズとして並べています。イベントはキューに入り、スケジュールジョブで非同期に処理されるためです。

非同期のフェーズとイベント処理のフェーズ↑
5. 判定できないものは「要目視」
設定だけでは判定できないものもあります。主なものは次の3つです。
・conditionスクリプトでScript Includeを呼ぶもの(例: new TaskStateUtil(current)...)
・GlobalテーブルのBR。Docsには「全ページで読み込まれる」とあり、実機のDebugログでは、レコードの操作での実行が記録されませんでした
・Insertで、既定値スクリプトを適用しないフィールドを参照する条件(例: OOTBのBR「insert_incident」の current.number.nil())

要目視の行には、判定できない理由を出しています
ここは割り切って、分からないものは分からないと出すようにしました。発火か不発かを無理に決めると、誤った答えを自信満々に返してしまうからです。調べる人にとっては「ここだけ自分で見ればいい」と分かるほうが助かるかなと思います。
6. Build Agentで作ってみてのコツ
他の方も書いてるかと思いますが、「最初は設計だけ出させる」がポイントだなと思いました。
最初のプロンプトでは、「設計だけをお願いします。実装はまだ始めないでください」と伝えました。設計書をレビューして、修正点を返してから実装に進んでいます。

設計だけを依頼したところ↑
※Build Agentのdocsのどこかで見た記憶ですが、これ(最初は設計だけ)がプラクティスとしてもあるみたいです。
ちなみにBuild Agentのクレジット節約のため、構想の壁打ちとプロンプト作成は自前の別のAIでやり取りしながらプロンプトを作成しました。
設計書では、読み取るテーブルとフィールドの一覧を出してもらいました。このとき「フィールド名は推測せず、このインスタンスの辞書で実在を確認した名前を書いてください。確認できなかったものは未確認と明記してください」と書いたのがよかったです。
7. 今後の課題
いまの版では、フェーズ1としてサーバー側の処理だけを対象にしています。次に広げたいのは次のあたりです。
・Client ScriptとUI Policy(フォーム、Workspace、APIといった経路を選べるようにする)
・SLA、割当ルール、Data Lookup(優先度の計算など)
・通知の受信者の重複の判定
・FlowのRun Trigger(Onceなど)の判定
また、実際に使ってもらう前には、ロールによるアクセス制限(adminと専用ロールだけが使える設定など)を実機で確かめる必要があります。
8.BuildAgentに渡したプロンプト
お待たせしました(?)今後のどなたかの参考になれば&このアイディア良い!と思った方が現場で役立つかもしれない意味を込めて、
①最初にAgentへ渡したプロンプト
と
ここまでの動作確認を経て現時点版ができた段階で、
②途中のやりとりで出た要望や考慮点も最初から要望や設計に組み込めてたら、最初どんなプロンプトになっていたか」も壁打ち側のAIに出させたAgentへ渡すはずのプロンプト
をここで供養しておきます。
①最初にAgentへ渡したプロンプト
これから作るアプリの**設計だけ**をお願いします。実装(レコードやファイルの作成)はまだ始めないでください。設計を確認してから、実装の指示を別に出します。
## 1. アプリの概要
アプリ名(仮): Record Operation Analyzer
ServiceNow の管理者・開発者向けの解析ツールです。次の4つを指定して「Analyze」を押します。
- テーブル
- 操作(Insert / Update / Delete)
- 実行ユーザー
- フィールドの入力値(任意)
すると、その操作で動く自動処理(Business Rule、Data Policy、通知、イベント、Flow など)を、**設定(メタデータ)から判定して実行順に並べて表示**します。
UI は Access Analyzer の「Evaluate access」の画面構成(上に条件入力、下に結果)を参考にしてください。
想定する利用場面は、次のような利用者からの問い合わせに、実際に操作する前に根拠付きで答えることです。
- 「このインシデントを更新したのに通知が来ない」
- 「この BR はなぜ動かないのか」
## 2. 既存機能との違い(設計の軸)
- Script Tracer や Debug Business Rule は、実行したあとのログです。このアプリは**実行せずに、設定から事前に判定**します。
- コミュニティ製の DevTools「What Runs」は、テーブル単位で動きうる処理を一覧にするだけで、条件は評価しません。このアプリは**条件を評価して「発火 / 不発 / 要目視」を判定し、不発の理由(どの条件が合わなかったか)を表示**します。ここが最大の価値です。
## 3. 絶対条件(安全性)
- 解析は**読み取り専用**です。解析対象のレコードを insert / update / delete しないでください。`GlideRecord` の `update()` / `insert()` / `deleteRecord()` を解析処理で呼ばない設計にしてください。
- BR の condition スクリプトや本体スクリプトなど、**解析対象のスクリプトは実行しないでください**。スクリプトで判定が必要なものは「要目視」として扱います。
- 本アプリは独自スコープのアプリにします。他スコープや Global のテーブルを読むために必要な、クロススコープの権限設定も設計に含めてください。
- 利用できるのは admin(または本アプリ専用ロール)だけにしてください。
## 4. 入力
| 項目 | 必須 | 内容 |
|---|---|---|
| テーブル | 必須 | 例: incident |
| 操作 | 必須 | Insert / Update / Delete |
| 対象レコード | Update/Delete で必須 | 既存レコードを選ぶ。これを変更前の値として使う |
| 変更後の値 | 任意 | フィールドと値を複数指定する(Insert では初期値になる) |
| 実行ユーザー | 必須 | 判定に使うユーザー(ロール、グループ) |
経路(UI16 フォーム / Workspace / API など)の選択と、クライアント側(Client Script、UI Policy)の判定は**フェーズ2**で扱います。今回の設計では拡張できる構造にしておくだけで構いません。
## 5. 解析の流れと実行順序
タイムラインの順序は、ServiceNow 公式 Docs「Scripts and engines execution order」に合わせてください。
1. before BR(order < 1000)
2. before エンジン(Docs 上は順不同): Data Policy、Data Lookup、割当ルールなど
3. before BR(order >= 1000)
4. DB 操作
5. after BR(order < 1000)
6. after エンジン(Docs 上は順不同): Flow のトリガーなど
7. メール通知(insert/update 起動とイベント起動)
8. after BR(order >= 1000)
9. async BR(非同期のため最後に別枠で表示)
親テーブルから継承される BR や通知も対象にしてください(例: incident なら task の分も)。
## 6. 各処理の判定方法
判定結果は、どれも次の3種類にします。
- **発火**: 条件に一致した
- **不発**: 条件に一致しなかった。どの条件が合わなかったかを表示する
- **要目視**: スクリプト条件があるなど、設定からは判定できない
| 対象 | 判定の内容 |
|---|---|
| Business Rule | active、when、操作フラグ、order で絞り込み、条件ビルダーの条件(エンコードクエリ)を評価する。condition スクリプトがあれば「要目視」にする |
| Data Policy | 条件を評価する |
| 通知(レコード起動) | 操作フラグと条件を評価する |
| イベント | 発火した BR のスクリプトから `gs.eventQueue('イベント名'...)` を静的に抽出する。イベント登録と突き合わせる |
| 通知(イベント起動)、Script Action | 抽出したイベント名で通知と Script Action を引き、イベントからの連鎖として表示する |
| Flow(レコードトリガー) | テーブルと操作で該当フローを抽出する。トリガー条件の評価は、保存形式を確認してから決める |
**条件評価の方式**: 保存しない GlideRecord を用意します。Update では対象レコードを `get()` して変更後の値を `setValue()` し、Insert では `initialize()` して値を設定します。そのうえで `GlideFilter.checkRecord(gr, encodedQuery, true)` で評価する方式を第一案にしてください。次の点を設計に含めてください。
- 不発の理由を出すため、エンコードクエリを条件ごとに分解して個別に評価する。`^OR` と `^NQ` の扱いも含める
- 変更系の演算子(VALCHANGES / CHANGESTO / CHANGESFROM)の扱い
- 大文字・小文字の区別(`setCaseSensitive` の要否)
- `javascript:` で始まる動的な値(例: `gs.getUserID()`)は、解析するユーザーで評価できない場合「要目視」にする
- `GlideFilter` が期待どおり動かないケースに備えた代替案
## 7. 追加の警告(玄人向け)
- after BR(order < 1000)のスクリプトに `setWorkflow(false)` があれば、「後続の通知エンジンが動かなくなる可能性」を警告する
- 発火する before BR のスクリプトが `current.フィールド` に代入していれば、「後続の条件判定に影響する可能性」を警告する
- 同じフィールドに代入している BR が複数あれば、競合として警告する
## 8. 出力(結果画面)
- 実行順のタイムライン(フェーズごとにまとめる)
- 各項目に表示するもの: 種別、名前、テーブル(継承元を含む)、order、判定(発火/不発/要目視)、理由、元レコードへのリンク
- 「不発」「要目視」も既定で表示する。フィルターで絞り込めるようにする
- 結果の上部に、発火・不発・要目視の件数サマリー
## 9. 設計書として出してほしいもの
1. 全体構成(画面、Script Include などのサーバー処理、権限)
2. 読み取るテーブルとフィールドの一覧。**フィールド名は推測せず、このインスタンスの辞書で実在を確認した名前**を書いてください。確認できなかったものは「未確認」と明記してください。
3. 判定ロジックの詳細(第6章の各対象ごと)
4. 画面の構成とワイヤーフレーム(テキストで構いません)
5. 本アプリで新しく作るテーブルが必要かどうか(解析結果を保存するかどうかを含む)。必要なければ作らない方針で構いません
6. 実装の順番。最初に `GlideFilter.checkRecord` の挙動を確かめる小さな検証ステップを置いてください
7. テスト方法。例: incident の `assignment_group` を変更する Update を解析し、OOTB の割当通知が「発火」、state を条件にした通知が「不発」と理由付きで出ること
8. 設計上のリスクと、判断が必要な点(私への質問)
## 10. 今回のスコープ(フェーズ1)
- 対象: Business Rule、Data Policy、通知、イベント連鎖、Script Action、Flow(抽出のみ)
- 対象外: Client Script、UI Policy、ACL(ACL は Access Analyzer に任せる)、SLA、割当ルール、Workflow(レガシー)
- 動作確認するテーブル: incident(ほかのテーブルでも動く汎用設計にする)
②途中のやりとりで出た要望や考慮点も最初から要望や設計に組み込めてたら、最初どんなプロンプトになっていたか」も壁打ち側のAIに出させたAgentへ渡すはずのプロンプト
これから作るアプリの**設計だけ**をお願いします。実装(レコードやファイルの作成)はまだ始めないでください。設計を確認してから、実装の指示を段階ごとに出します。
# 1. アプリの概要
アプリ名: Record Operation Analyzer(新規の独自スコープ。スコープ名は `x_<会社コード>_roa` の形式で、会社コードはこのインスタンスの既定値)
ServiceNow の管理者・開発者向けの解析ツールです。次を指定して「Analyze」を押します。
- テーブル
- 操作(Insert / Update / Delete)
- 対象レコード(Update / Delete で必須。変更前の値として使う)
- 変更後の値(任意。フィールドと値を複数指定。Insert では初期値になる。値を「空にする」指定もできる)
- 実行ユーザー
すると、その操作で動く自動処理(Business Rule、Data Policy、通知、イベント、Script Action、Flow)を、**設定(メタデータ)から判定して実行順に並べて表示**します。
想定する利用場面は次の2つです。
- 「担当グループを変えたのに通知が来ない」「この BR はなぜ動かないのか」という問い合わせに、実際に操作する前に根拠付きで答える
- 数件のレコードを手作業で直す(データパッチ)前に、通知や副作用のある BR が動くかを確かめる
# 2. 既存機能との違い(設計の軸)
- Script Tracer や Debug Business Rule は、実行したあとのログです。このアプリは**実行せずに、設定から事前に判定**します
- コミュニティ製の DevTools「What Runs」は、テーブル単位で動きうる処理を一覧にするだけで、条件は評価しません。このアプリは**条件を評価して判定し、不発の理由(どの条件が合わなかったか)を表示**します。ここが最大の価値です
- Access Analyzer は権限の分析で、自動処理は対象外です
# 3. 絶対条件(安全性)
- 解析は**読み取り専用**です。解析対象のレコードを insert / update / delete しないでください。`update()` / `insert()` / `deleteRecord()` を解析処理で呼ばない設計にしてください
- BR の condition スクリプトや本体スクリプトなど、**解析対象のスクリプトは実行しないでください**
- 他スコープや Global のテーブルを読むためのクロススコープの権限を、Fluent などで明示的に宣言する設計にしてください
- ロール `x_<scope>.analyzer` を作成し、アプリメニュー、モジュール、画面、サーバーの呼び出し口のすべてを、admin とこのロールだけが使えるようにしてください
- 新規テーブルは作らず、解析結果は保存しません(エクスポートはブラウザでのダウンロードのみ)
- フィールド名・テーブル名は推測せず、このインスタンスの辞書で実在を確かめた名前を書いてください。確かめられなかったものは「未確認」と明記してください
# 4. 判定の区分
判定は次の5つです。それぞれの定義を、設計書と画面の説明に使ってください。
| 区分 | 定義 |
|---|---|
| 発火 | 操作の種類が合い、条件もすべて満たす(読み替えた単純なスクリプト条件を含む) |
| 不発 | 操作の種類は合うが、条件に合わない。合わなかった条件(AND グループ)を理由に出す |
| 要目視 | 設定だけでは判定できない。理由を出す |
| 無効 | 処理自体が無効、または未公開(例: active=false のフロー) |
| 対象外 | 処理の対象とする操作が違う(操作フラグが合わない)。Global テーブルの BR で操作フラグが合わないものも含む |
BR の判定順は、操作フラグ → ロール条件 → 条件ビルダーの条件 → condition スクリプトです。
画面の表では、判定ステップ(操作 / ユーザー条件 / 条件 / スクリプト条件)を列にして、各ステップの結果(一致 / 不一致 / スキップ / 未設定 / 要目視)を示します。ユーザー条件は、BR のロール条件と Flow の run_when_user_setting をまとめた列です。
# 5. 各処理の判定方法
## 5-1. 条件の評価方式
保存しない GlideRecord(Update は `get()` のあと `setValue()`、Insert は `initialize()` のあと `setValue()`)に、`GlideFilter.checkRecord` を使う案を第一案にしてください。**方式は、必ず実装の前のスパイクで確かめてから決めてください**(6章)。次の点を設計に含めてください。
- **変更系の演算子**(VALCHANGES / CHANGESTO / CHANGESFROM): `get()` のあと `setValue()` した GlideRecord は変更前の値を保持しているはずなので、`checkRecord` がそのまま評価できる可能性があります。「評価できない」と決めつけず、スパイクで確かめてください。Insert での挙動も確かめてください
- **不発の理由**: エンコードクエリを、次の構造として扱い、グループごとに評価してください。「最初に false になった句」を出すだけだと、`^OR` を含む条件で誤った理由が出ます
- `^NQ` で区切られたブロック同士は OR
- ブロック内で `^` で区切られたグループ同士は AND
- `^OR` でつながった句は1つのグループで、グループ内は OR
- 理由として表示するのは、「false になった AND グループ」です
- **大文字・小文字**: `checkRecord` は区別するという報告があります(PRB605673)。「既定は区別なし」と決めないでください。公式の型定義では `GlideFilter` のコンストラクタは `constructor(filter: string, title: string)` の2引数です。自由入力の文字列フィールドで、`setCaseSensitive(true/false)` と `match(gr, true)` を試し、**実際の BR の挙動(メモリ上の current に対する評価か、DB クエリか)と同じ結果になる方法**を採用してください。実際の挙動の確認には、PDI で一時的な before update BR を作って確かめてください(確認後に削除)。文字列を比べる句は、区別する・しないの両方で評価し、結果が違えば「要目視」にする案も検討してください
- **ユーザーで変わる値**: `javascript:gs.getUserID()` などは、選んだ実行ユーザーの値に置き換えてから評価してください。`javascript:gs.beginningOfToday()` のようにユーザーに依存しない値は、置換せずそのまま評価してください。「要目視」にするのは、ユーザー依存の既知パターン以外で判断できないものだけにしてください
- **動的フィルター**: 「assigned_to is (dynamic) Me」「One of My Groups」などは、`DYNAMIC<sys_id>` 演算子になります。解析実行セッションのユーザーで評価されるかを確かめ、`sys_filter_option_dynamic` の定義から、選んだユーザーで解決できるパターン(Me、My Groups など)だけ置換し、ほかは「要目視」にしてください
- **ドットウォーク条件**(例: `caller_id.vip=true`)の評価
- `GlideFilter` が期待どおり動かないケースに備えた代替案
## 5-2. 対象ごとの判定
| 対象 | 判定の内容 |
|---|---|
| Business Rule | `sys_script` の active、when(`sys_choice` で実際の値を確かめる。`async` と `async_always` の両方、`before_display` も)、操作フラグ、order で絞り込み、ロール条件(辞書で項目を確かめる。実行ユーザーのロールで判定)、条件ビルダーの条件を評価する |
| condition スクリプト(BR) | 実行しない。単純な式1つだけのもの(`current.<field>.changes()`、`.changesTo(リテラル)`、`.changesFrom(リテラル)`、`.nil()`、`== / != リテラル`、`gs.hasRole('...')`、`current.operation() == '...'`)だけ条件に読み替えて評価し、それ以外は要目視 |
| Global テーブルの BR | 操作フラグが合うものは「要目視」(Docs では「全ページで読み込まれる」とあるが、レコード操作で実行されるかは未確認。実機の Debug ログでは記録されなかった) |
| Insert の既定値 | 既定値スクリプトを適用しないフィールド(ユーザーが明示的に値を入れたものを除く)を参照する条件は、「要目視」にする(例: `current.number.nil()`) |
| Data Policy | 条件の格納先を辞書で確かめる。`sys_data_policy_rule` は Table / Field name / Read Only / Mandatory の4項目だけなので、条件は `sys_data_policy2` の conditions 型のフィールドにあると読める。あれば本体の条件を評価する。必須(`mandatory=true`)にしたフィールドが変更後の値で空になる場合は警告する |
| 通知(レコード起動) | 操作フラグと条件を評価する。`weight` を表示する。同じテーブルで発火と判定した通知が複数あり、weight が 0 以外のときは、「weight によって送信されない可能性」を注記する(受信者の重複判定はフェーズ2)。実行ユーザーが受信者に含まれていても、`send_self` が false なら本人には送られない旨を注記する(通知自体は発火のまま) |
| イベント | 発火した BR のスクリプトから `gs.eventQueue` を静的に抽出し、囲む if の条件で「発行 / 発行されない / 要目視」を判定する。イベント名が文字列リテラルでない場合は要目視。トップレベルで呼ばれない関数の中は、連鎖の対象外 |
| 通知(イベント起動)、Script Action | 抽出したイベント名で引き、イベントからの連鎖として表示する。Script Action の条件(Condition script)は要目視 |
| Flow(レコードトリガー) | `sys_flow_record_trigger`(table / on_insert / on_update / run_flow_in)から `sys_hub_flow_base.remote_trigger_id` で引く。辞書で確かめ、`sys_hub_trigger_instance` 案と比べて確実なほうを採用する。トリガー条件を評価する。**ライブの `sys_hub_flow` のトリガーだけを使い、公開スナップショットの `sys_hub_flow_snapshot` は使わない。フロー1つにつき1行**にする。無効・未公開は「無効」。`run_flow_in` が foreground なら after エンジンの枠、background なら async の枠。Run Trigger(Once / For each unique change など)は判定しない旨を、Flow を含むフェーズの見出し行に**1回だけ**表示する(各行には出さない)。過去の実行履歴で結果が変わるもの、対話セッションのみの条件は、注意欄に出す。子テーブルでも動く設定があれば、親テーブルのトリガーも対象にする。条件以外にトリガーの発火に影響する項目は、辞書で確かめた名前で洗い出す |
## 5-3. 警告
結果の上部に、注意色の帯でまとめて表示してください。
- `abort_action=true` の BR が発火と判定された場合は「確実に中断」。スクリプト内の `setAbortAction` は「条件次第で中断(要目視)」。中断の可能性があるときは、それ以降のフェーズ(DB 操作、after、通知、async)に「中断時は実行されない」と注記する
- 発火する Data Policy が必須にしたフィールドが、変更後の値で空になる場合
- after BR(order < 1000)のスクリプトに `setWorkflow(false)` がある場合(後続の通知エンジンが動かなくなる可能性。order >= 1000 にすれば通知処理の後に動く)
- 発火する before BR のスクリプトが `current.フィールド` に代入している場合(後続の条件判定に影響する可能性)。同じフィールドに代入する BR が複数ある場合は、競合として警告する
# 6. 実装の前のスパイク
最初に、検証用の Script Include と Background Script だけで、次を確かめてください。アプリ本体は、スパイクの判定が出るまで作らないでください。
| ID | 確認する内容 |
|---|---|
| S1 | スコープアプリから、`sys_script`、`sysevent_email_action`、`sys_data_policy2` などの Global テーブルを GlideRecord で読めるか(Can read、クロススコープの挙動) |
| S2 | `checkRecord` の基本の評価 |
| S3 | VALCHANGES / CHANGESTO / CHANGESFROM を個別に評価できるか。Insert(S3b)でも |
| S4 | 大文字・小文字(2引数のコンストラクタ、`setCaseSensitive` の有無、実際の BR の挙動との比較) |
| S5 | `^OR` / `^NQ` を含む条件の評価 |
| S6 | ドットウォーク条件 |
| S7 | ユーザー依存の `javascript:` 値の置換(S7b: 動的フィルター `DYNAMIC<sys_id>`) |
| S8 | Flow のトリガー条件の評価(S8b: トリガーの追加オプションの洗い出し) |
| S9 | Data Policy の条件の格納先と評価 |
各スパイクについて、「実行したスクリプト(全文)」「出力ログ」「判定(第一案を採用するか、代替案に切り替えるか)」を表で報告してください。判定基準(何が分かれば第一案、何が分かれば代替案)は、設計書に先に書いてください。
# 7. 実行順(タイムライン)
公式 Docs「Scripts and engines execution order」に従ってください。
1. before BR(order < 1000)
2. before エンジン(Docs 上は順不同): Data Policy、Data Lookup、割当ルールなど
3. before BR(order >= 1000)
4. DB 操作
5. after BR(order < 1000)(発行されるイベントは、発行元の BR のフェーズに「イベント発行」として表示)
6. after エンジン(Docs 上は順不同): foreground の Flow など
7. メール通知(レコード起動。weight の順)
8. after BR(order >= 1000)
9. async(async BR、background の Flow)。「フォーム送信後にスケジューラーが実行する」と注記
10. イベント処理(非同期): イベント起動の通知と Script Action。Docs「Events」に、スケジュールジョブがイベントキューを読んだときに処理される、とあるため
親テーブルから継承される BR や通知も対象にしてください(例: incident なら task の分も)。テーブル欄には「task(継承)」と表示します。
# 8. 画面
ServiceNow に最初からあった機能のように見える画面にします。レイアウトは Access Analyzer の「Evaluate access」を手本にします(上に条件の入力、下に結果。判定ステップを列に並べる)。
## 8-1. 実装方式
- React の UI Page と、npm パッケージ `@servicenow/react-components`(Horizon Design System の React ラッパー。版は固定)を使います。サーバーは GlideAjax で Script Include を呼びます
- Docs では React での UI 開発は experimental です。**スパイクで、このアプリへの React 構成の追加、テーマの色での表示、GlideAjax の呼び出しを確かめてから**進めてください。確かめられなければ、Jelly の UI Page に切り替える案も出してください
- 色は Polaris テーマの CSS 変数(`rgb(var(--now-color_background--primary))` の形)で付け、色の値(#xxxxxx)を直接書かないでください。絵文字は使わないでください。表示の文言は日本語です(操作は Insert / Update / Delete の英語表記)
## 8-2. 入力エリア
- 見出し(Heading)→ 条件の入力(Card)→ 結果エリア
- 2列のフォーム。左: テーブル、操作(RadioButtons)、対象レコード。右: 実行ユーザー
- テーブル、対象レコード、実行ユーザー、変更後の値のフィールドは Typeahead で検索します
- テーブル: sys_db_object から「Incident [incident]」の形式
- 対象レコード: 選んだテーブルのレコードを番号・表示値で検索。テーブルを変えたらクリア
- 実行ユーザー: アクティブな sys_user
- フィールド: 継承元を含め、「ラベル [フィールド名]」の形式
- 値の欄は型で切り替えます: choice は「ラベル(値)」の Select(評価には値を使う)、参照は参照先の Typeahead(表示値で入力し、サーバー側で sys_id に解決)、boolean は true / false の Select、その他は Input
- 値の行ごとに「空にする」の Checkbox。値が未入力で「空にする」もない行があれば、Analyze 時にエラーを出して止める
- 行を足すボタン「フィールドを追加」は、最後の行の下の左端に置く(右端だと見落とすため)
- フィールド、値、「空にする」、削除ボタンは、4列のグリッドで縦位置をそろえる。HDS の部品は置いただけでは列の幅いっぱいに広がらないので、`display: block; width: 100%` などで広げる
- 右下に Analyze(primary)とクリア(secondary)。解析中は Loader を出し、ボタンを無効にする
## 8-3. 結果エリア
- 見出しとメタ情報(テーブル / 操作 / 対象レコード / 実行ユーザー / 実行日時)
- 変更した項目: 変更前 → 変更後(空は「(空)」)
- 警告の帯(Alert。アイコン名を明示する): 中断は critical、要目視は warning、ほかは info
- 件数と絞り込み: Tabs(「すべて」+ 発火 / 不発 / 要目視 / 無効 / 対象外。各タブに件数)
- フェーズ: Accordion(見出しにフェーズ名と件数)
- 表: HDS に汎用のデータ表の部品がないので、HTML の table で作る。Next Experience のリストに寄せる(横罫線のみ、行の高さ、ホバー)
- すべてのフェーズで列の幅をそろえる(`table-layout: fixed` と共通の colgroup)
- 列: 名前(元レコードへのリンク。別タブ)/ 判定 / 種別 / テーブル / order / 判定ステップ4列 / 理由 / 注意
- 判定は HighlightedValue。5区分が色で見分けられ、文字がはっきり読めること(不発は件数が最も多いので特に)
- 判定ステップは、色付きの Icon(一致=緑、不一致=赤、要目視=橙、判定なし=灰)と Tooltip(元の文字)
- 理由の条件式は、すべて同じ規則で等幅のフォントにする。単語の途中では、ほかに折り返す場所がないときだけ折る
- 同じ名前の Flow が複数あるときは、名前の後ろにトリガーの区別がつく情報を小さく付ける
- 説明のない Data Policy は「(説明なし)」と表示し、リンクで元レコードを開けるようにする
- 結果なしは TemplateMessage
- ページは横にはみ出さないこと。長い文字は折り返す
# 9. 設計書として出してほしいもの
1. 全体構成(画面、Script Include、権限、メニュー、ロール)
2. 読み取るテーブルとフィールドの一覧(辞書で確かめた名前。未確認は明記)
3. 判定ロジックの詳細(5章の対象ごと)と、スパイクの判定基準
4. 画面の構成(テキストのワイヤーフレーム)
5. 実装の順番(下の10章を手本に)
6. テスト方法: incident の担当グループを変える Update を解析し、OOTB の通知「Incident assigned to my group」が、担当者ありなら不発(理由 `assigned_toISEMPTY`)、担当者を空にすれば発火になること。state を条件にした通知が不発と理由付きで出ること。**実際の更新との照合手順**(次の10章)も含める
7. 設計上のリスクと、判断が必要な点(私への質問)
# 10. 実装の進め方(設計を承認したあとの段階)
1. スパイク(6章)
2. 共通基盤: テーブルの継承、ユーザーのロール、条件の評価、スクリプトからのイベント抽出
3. Collector: BR、Data Policy、通知、イベント連鎖、Flow
4. Orchestrator(実行順の組み立て)と、2つのシナリオの通し確認(担当者ありは不発、担当者なしは発火)
5. 画面とメニュー、ロール
6. **実際の更新との照合**: 私が実際にレコードを更新し、Debug Business Rule のログ、sysevent、sys_email、sys_flow_context を渡します。解析の結果と BR ごとに突き合わせ、「解析で発火 → 実際は実行」「発火 → スキップ」「不発・対象外 → 実行(見逃し)」「要目視 → どちらか」「ログにあるが解析に出ない」に分けて報告してください。Update / Insert / Delete のすべてで行います
# 11. 進め方のルール
- アプリを作る前に、既存のものがあれば Update Set に書き出して保存してください(戻せるようにするため)
- 1回のメッセージでは、指示した段階だけを行い、ビルドとインストールまでして、短く報告して止まってください
- インストールの前に、既存のレコードが削除されないかを確かめてください。削除が含まれる場合は止めて報告してください
- 報告は件数、変更したファイル名、確認の結果だけにして、ファイルの全文や長いログは出さないでください(スパイクの報告は例外で、6章の形式で出す)
- 画面を変えたときは、サーバーの応答だけでなく、画面での表示を確かめる方法も報告してください(画像を取れない場合は、そう報告してください)
- 新しい画面は、別のエンドポイントで作り、既存の画面は残してください。メニューの切り替えは最後に行います
- 既存のレコードを書き換える必要が出たら、止めて報告してください
💡②は実際に試してはいません(どっかで再現性あるか試すか・・・)
まとめ
「通知が来ない」「BRが動かない」「データパッチしたらどうなる?」の問い合わせに、実施/レコード保存する前に設定から答えるアプリを、Build Agentで作ってみました。
作ってみて感じたのは、「判定できないものを正直に要目視と出す」のが大事だということです。全部を自動で決めようとするより、「ここだけ見てください」と言えるほうが、調べる人にとっては頼りになるかなと思います。
Build Agentについては、設計だけ先に出させることでかなり安定して進められました。
今後は、Client ScriptやUI Policyの判定にも広げていきたい
(なんならマジで実際に導入したい)と考えています!
本記事がどなたかのお役に立てれば幸いです!
いつもは人力100%記事ですが、今回はせっかくなので記事執筆も最初はAIオールインで書いてみてます。
私の文体も学習させたんですが、最終的にちょっと人間味を入れる手直ししてます。(普段より人の関与度が低い)
🌟 ServiceNow World Forum Tokyo 2026 🌟
2026年10月27日(火)〜28日(水)に、グランドプリンスホテル新高輪で「ServiceNow World Forum Tokyo」が開催されます。Build Agentをはじめ、最新の機能に触れられる機会なので、ぜひ登録してみてください!!
ServiceNow World Forum Tokyo 2026 の詳細・登録はこちら









