Sample output from the Script-Stacking Analysis prompt in the Sonar AI Prompt Library, run against a NetSuite test account. Every name and number here is test data. Back to the post · The library
Customization Governance · Script Execution Risk

Script-Stacking Analysis — Core Transactions

Every user-event and client script deployed against Sales Order, Invoice, and Vendor Bill, attributed to its owning provider, with active/disabled state and event scope resolved from live deployment metadata. The question: how many independent providers' scripts fire when a user saves one of these records — and does any record type exceed 3 active user-event scripts? Third volume in the customization-governance series (Footprint Map · Concept Sweep · this).

TD3016323 · Production · OneWorld Generated 2026-08-22 Prepared by Sonar AI for Tim Dietrich Scope: 38 deployments · 20 scripts

01Executive Summary

All three core transaction types are flagged: Sales Order carries 6 active user-event scripts, Invoice 7, Vendor Bill 6 — every one past the 3-script threshold. Between five and six independent provider families fire on each record. Three "monolith" script pairs (USR, STE, LRC — one UE + one client script each) deploy across all three record types, so a regression in any of them hits the entire order-to-cash and procure-to-pay surface at once.

3 / 3
Record types flagged (>3 active UE scripts)
38
Deployments examined (34 active)
19
Active user-event deployments
9
Provider families in the stacks
3
Unattributed families running on every save (USR · LRC · NAW)
Headline risk — the tri-record monoliths: customscript_usr_ue_transaction, customscript_ste_ue_transaction, and customscript_lrc_field_manager_ue (plus their client-script twins) each deploy against Sales Order, Invoice, AND Vendor Bill. One bad change → three broken transaction types. Two of the three families (USR, LRC) have no confirmed owner.
Headline caveat — execution order is not SQL-determinable. NetSuite runs same-event UE scripts in the sequence on the Scripted Records page, which is not exposed to SuiteQL (scriptedrecord → "Invalid search type"). This report resolves who fires, not in what order — §04 gives the click-path to capture the authoritative sequence.
Mitigating structure: the NAW script is scoped to DELETE events only on both its records, and 4 of 38 deployments are disabled (two superseded EBP SS1 deployments, two Ship Central/ATB SO deployments) — the effective save-path UE stack is 6 (SO) / 6 (Invoice) / 5 (Vendor Bill), still all past threshold.

02Stack Matrix

Active script counts per record type, split by script type, with the effective save-path depth (event-scoped scripts removed) and distinct provider families. Computed from the deployment array at render time.

Record typeActive UESave-path UE†Active ClientDisabledProvider familiesFlag (>3 UE)

† Active UE scripts that fire on create/edit saves — excludes the DELETE-scoped NAW deployment. Every UE script here declares no event-type restriction unless badged.

03Execution Stacks by Record Type

Each card lists the full stack — user-event lane, then client lane. Rows are ordered by script internal id (creation order — a weak proxy only; see §04). Disabled deployments are dimmed: they don't fire today, but one checkbox re-arms them.

04Execution Ordering — What Is and Isn't Knowable

05Key Findings

  1. F1 — All three record types exceed the UE threshold (FLAGGED). Active user-event scripts: Sales Order 6, Invoice 7, Vendor Bill 6 against a threshold of 3. Save-path depth after event-scoping: 6 / 6 / 5. Every save of a core transaction runs a five-to-six-script gauntlet before it commits.
  2. F2 — Three monolith families ride every core transaction (OPERATIONAL). USR (customscript_usr_ue_transaction + _cs_), STE (customscript_ste_ue_transaction + _cs_), and LRC Field Manager (customscript_lrc_field_manager_ue + _cs) are single scripts with per-record deployments on all three types. Single point of failure across the transactional core; STE is at least a known bundle (SuiteTax) — USR and LRC are not confirmed.
  3. F3 — Attribution gap on the hot path (OPERATIONAL). USR, LRC, and NAW families have no confirmed provider. script.owner is no help — it records the installing user (Kathryn Glass for 17 of 20 scripts, Ryan Rote for the SCM trio), not the vendor. Three unattributed script families executing on every core-transaction event is a governance hole: nobody can say what they do, who patches them, or whether they can be retired. (NAW also owns the unattributed custentity_naw_trans_need_approval field from the Footprint Map.)
  4. F4 — The EBP generation pair repeats in script form (COSMETIC). "Generic Payment Information Hider" exists as SS1 (customscript_2663_payment_info_ue — deployments present but disabled on Invoice + Vendor Bill) and SS2 (customscript_15486_payment_info_ue — active on both). Exactly the pattern of the DD-file-format field duplicate from the Concept Sweep: two bundle generations, one concept. The SS1 deployments are latent re-enable hazards.
  5. F5 — Event scoping is underused (OBSERVATION). Only 1 of 23 UE deployments declares an event-type restriction (NAW → DELETE). Every other script fires on every event and self-filters in code — invisible to this analysis and to any admin reading the deployment list. Scoped deployments would make the true stack legible.
  6. F6 — Sales Order is the most contested record in the account (SYNTHESIS). Combining volumes: 10 field providers (Footprint Map), the color/size silo lives on its lines (Concept Sweep), and now 7 provider families' scripts (6 UE + 7 client active). Any SO form or workflow change needs multi-party impact review.
  7. F7 — Error-rate evidence unavailable this session (LIMITATION). Every scriptnote query — including shapes previously verified in this account — returned "An unexpected SuiteScript error". Whether the stacked scripts are actually erroring is unknown from this run; the script-telemetry-review process (or the scripting tool group's log reader) is the follow-up path. The account field notes claiming scriptnote is queryable need a recheck.

06Recommendations

P1 · GovernanceCapture the execution-order baseline

Open Customization > Scripting > Scripted Records for salesorder, invoice, and vendorbill; record (screenshot or export) the UE sequence for each. This is the one fact this report cannot supply, and it's the fact that determines who wins when two scripts write the same field. Store it beside this report as the baseline.

P1 · GovernanceIdentify USR, LRC, and NAW

Three families, six active deployments each way, zero confirmed ownership. Check bundle association on the script records (the Scripts list UI shows "From Bundle"), read the source file headers (usr_ue_transaction.js, lrcfm_user_event.js, WD_NAW_UE_deleteExceptions.js), and interview. The WD_ file prefix on NAW/SCM files suggests a common implementation partner.

P2 · HygieneDelete or annotate the four disabled deployments

The two EBP SS1 payment-hider deployments (superseded by SS2), customdeploy_atb_ue_salesorder, and customdeploy_packship_ue_nonadvinvt are disabled-but-present. A disabled deployment is one checkbox from re-arming — either remove them (bundle-update-safe: document instead if bundle-owned) or annotate why they're kept.

P2 · TelemetryRun the script-telemetry review

F7 blocked error-rate evidence. Run the account's script-telemetry-review process against the 12 active UE scripts on these records — error counts, governance usage, execution time — to convert "stacking risk" from structural to measured.

P3 · PracticePrefer event-scoped deployments going forward

New UE deployments should declare event types (the NAW DELETE scoping is the good example already in-account). It costs nothing and keeps the effective stack visible to admins without reading source.

07Methodology

  1. Schema probe first. SELECT * FROM scriptdeployment FETCH FIRST 1 ROWS ONLY established the exposed columns: recordtype (plain uppercase names), status, isdeployed, eventtype, executioncontext, and the id-vs-primarykey split.
  2. Record-type encoding survey. A GROUP BY over UE/client deployments confirmed the encoding (SALESORDER, INVOICE, VENDORBILL) and gave account-wide stack counts for context.
  3. Main pull. All UE + client deployments for the three types, joined to script (type, inactive flag, owner) and file (source filename). 38 rows. A first attempt included executioncontext — dropped on re-query because every deployment carries the full ~35-context list, bloating output with zero signal.
  4. Active = status='RELEASED' AND isdeployed='T' AND script not inactive. All 38 rows are RELEASED and no script is inactive, so the discriminator here is isdeployed (34 T / 4 F).
  5. Provider attribution by scriptid prefix against the account's known bundle inventory (same registry as the Footprint Map): ste_ SuiteTax, scm_ SCM bundle, 2663_/15486_ EBP generations, packship_ Ship Central, rac_/atb_ local-uncertain, usr_/lrc_/naw_ unattributed. script.owner was pulled and rejected as an attribution source (it's the installing user).
  6. Ordering honesty. scriptedrecord probed and found not SQL-exposed; the scriptdeployment.id per-script-sequence trap verified (multiple rows share id=1) and documented; listing order fell back to script internal id, labeled as creation order.
  7. Error-log attempt. Three scriptnote query shapes (grouped, filtered, plain recent-rows) all returned "An unexpected SuiteScript error" — recorded as F7 rather than silently omitted.

08Source Queries

Q1 — Deployment schema probe
Read the real column list first — and note id vs primarykey.
SELECT * FROM scriptdeployment FETCH FIRST 1 ROWS ONLY
Q2 — Record-type encoding + account-wide stack survey
SELECT sd.recordtype, s.scripttype, COUNT(*) AS n
FROM scriptdeployment sd JOIN script s ON s.id = sd.script
WHERE s.scripttype IN ('USEREVENT','CLIENT')
GROUP BY sd.recordtype, s.scripttype ORDER BY COUNT(*) DESC
-- recordtype = plain uppercase names (SALESORDER, INVOICE, VENDORBILL)
Q3 — The main pull: full stack for the three record types
Do NOT select executioncontext — every row carries the full ~35-context list (pure bloat).
SELECT sd.recordtype, s.scripttype, s.name AS script_name, s.scriptid AS script_sid,
 sd.scriptid AS deploy_sid, sd.status, sd.isdeployed, sd.eventtype, s.isinactive, f.name AS filename
FROM scriptdeployment sd
JOIN script s ON s.id = sd.script
LEFT JOIN file f ON f.id = s.scriptfile
WHERE sd.recordtype IN ('SALESORDER','INVOICE','VENDORBILL')
 AND s.scripttype IN ('USEREVENT','CLIENT')
ORDER BY sd.recordtype, s.scripttype, s.name
-- → 38 rows; active = isdeployed='T' (all RELEASED, no inactive scripts)
Q4 — The ordering trap (cautionary exhibit)
-- scriptdeployment.id is PER-SCRIPT sequence, not global (many rows share id=1):
SELECT sd.id, sd.recordtype, sd.scriptid FROM scriptdeployment sd
JOIN script s ON s.id = sd.script
WHERE sd.recordtype = 'SALESORDER' AND s.scripttype = 'USEREVENT'
-- global unique key = primarykey; NEITHER encodes execution order.
-- Execution order lives on the Scripted Records page only:
--   scriptedrecord → "Invalid search type" (not SQL-exposed)
Q5 — Script owners (attribution rejection test)
SELECT s.id, s.scriptid, s.name, s.owner, e.entityid AS owner_name
FROM script s LEFT JOIN employee e ON e.id = s.owner
WHERE s.scriptid IN ('...the 20 scripts...') ORDER BY s.id
-- → owner = installing user (17× one admin, 3× another), NOT the vendor.
-- Attribution must come from scriptid prefixes + bundle records.
Q6 — Error-log attempt (failed; documented as F7)
SELECT s.scriptid, COUNT(*) FROM scriptnote sn JOIN script s ON s.id = sn.script
WHERE sn.type = 'ERROR' AND s.scriptid IN (...) GROUP BY s.scriptid
-- → "An unexpected SuiteScript error" on this and two simpler shapes.
-- Follow-up path: script-telemetry-review process / scripting tool group.

09Assumptions, Caveats & Limitations

  1. Scope = script deployments with recordtype exactly matching the three types. Scripts touching these records indirectly (scheduled/MR scripts loading SOs, workflows, Suitelets transforming records) are out of scope — this is the event-time stack only. Workflows in particular can add save-time logic and were not enumerated.
  2. Execution order is NOT claimed. Listing order is script creation order; the Scripted Records page is the only authority (§04).
  3. Provider attribution is the prefix heuristic (same limits as the Footprint Map): USR/LRC/NAW honestly unattributed; RAC/ATB local-uncertain; script.owner rejected as evidence.
  4. "Active" means deployable metadata state (RELEASED + isdeployed + script active). A script can be metadata-active and behaviorally dormant (self-filtering to nothing) — source-level review was out of scope.
  5. Event-scope credit given only to declared eventtype restrictions. Scripts that self-filter in code still count in the save-path stack — from the platform's perspective they load and execute.
  6. Client-script stacking is form-mediated: which client scripts actually attach can vary by custom form; the deployment-level view is the upper bound per record type.
  7. Error-rate evidence unavailable this session (scriptnote errors — F7); stacking risk is assessed structurally, not from failure telemetry.
  8. Point-in-time snapshot (2026-08-22). Bundle updates add/remove deployments; re-run Q2/Q3 to refresh. Threshold (>3 active UE) is the requested flag line, not a NetSuite limit.