Martin Rudack
Giga Sage

bannerklein.jpg

 

In my last article, I explored ServiceNow's new AI Experience Framework and created my first widget from scratch. I purposely did this without AI because I wanted to learn and understand this new technology. Even in the age of generative AI, I firmly believe you need to understand how the underlying technology works before letting AI do the heavy lifting. Recent examples showed that you cannot rely on AI to be always available. And of course you should not follow AI blindly and therefore you first need to understand it.

 

But now that I have built some widgets manually, the logical next step is to see how AI can support. The AI UX Framework is a modern, AI-ready UI stack built around a "vibe code first" approach. Let’s put it to the test.

The idea is to create the same widget as in the first article. Or to say it more precisely to let it create by AI.

 

If you haven’t read my first article you can find it here: https://www.servicenow.com/community/employee-slate-and-employee/building-my-first-widget-with-servi...

 

 

This time, I jumped into an instance with Now Assist enabled. When you open the Widget Builder experience, you get the option to describe the widget you want, and the AI will generate it for you.

 

 

1Overview.png

 

Here is the initial prompt I used:

 

“Create a widget which shows the open private tasks of the currently logged in user.  The tasks should be displayed in a table at the bottom of the widget. The widget should contain an input field to enter a Short Description for a new task to be created. The task should be created if a button with the label ‘Create’ is clicked. The widget should also contain a button to close all open private tasks of that user once it is clicked. At the top of the widget the currently logged in user should be greeted and the count of currently open private tasked should be displayed in a badge. If the user created a new task or closes all tasks the change should be directly visible on the widget.” 

 

And the AI got to work.

 

2generating.png

 

AI Output: 

 

Spoiler

Reasoning through your request...Let me analyze this widget requirement:

  1. Shows open private tasks for currently logged in user
  2. Has an input field for creating new tasks (Short Description)
  3. Has a "Create" button to create new tasks
  4. Has a button to close all open private tasks
  5. Displays greeting with user name and open task count in a badge
  6. Shows tasks in a table at the bottom
  7. Changes should be immediately visible (real-time updates)


This needs:

  • Server data (to fetch tasks, create tasks, close tasks)
  • Form inputs (text input, buttons)
  • Table display
  • Action handling (create task, close all tasks)
  • Real-time updates after actions


For ServiceNow, "private tasks" likely refers to tasks in the sc_task table where the assignedto is the current user, or it could be personal tasks in a task table. I'll use `sctask` since that's the standard ServiceNow task table.
Component structure:

  • Properties: loading, error states
  • Server communication for actions
  • Form handling for new task creation
  • Table display for existing tasks
  • Badge for task count
  • User greeting


Server script:

  • Handle create task action
  • Handle close all tasks action
  • Always fetch current tasks after any action
  • Get current user info


I need to be careful about:

  • All user-facing strings using i18n
  • Proper accessibility for form and table
  • State management (loading, error, success states)
  • Proper escaping in JSON
  • Required translations list
  • Using correct styling patterns
  • Action handling pattern in server script


Let me write the component and server script:
Component:

  • Extends AIUXWidgetElement
  • Has form input and buttons
  • Has table display
  • Handles actions via server.get()
  • Shows loading/error/success states
  • Uses proper styling


Server script:

  • IIFE pattern
  • Handles createtask and closeall_tasks actions
  • Always fetches fresh data




  • Uses GlideRecordSecure for database queries with proper error handling

 

After a few seconds, it displayed the widget in the preview, and at first glance, it looked very promising.

 

3output.png

 

The only problem? The preview showed zero open tasks, even though I had just created one manually before creating the widget.

 

Looking at the AI's output log, I found the problem:

For ServiceNow, "private tasks" likely refers to tasks in the sc_task table

 

I didn’t want sc_task records. My intention was to show vtb_task records.

 

I asked AI to change it to the vtb_task table.

 

Second prompt:

Private tasks refer to vtb_task table and not sc_task. Please change it.

 

AI Output: 

Spoiler

Reasoning through your request...Looking at the existing widget JSON, I can see it's currently set up to work with the sc_task table (Service Catalog tasks). The user wants to change this to use the vtb_task table for private tasks instead.
Let me analyze what needs to be changed:

  1. In the server script, all references to 'sc_task' need to be changed to 'vtb_task'
  2. I need to be careful to preserve all existing functionality and only change the table references
  3. The component code doesn't directly reference the table name, so it should remain unchanged
  4. All other logic, error handling, and data structures should remain the same


Looking at the server script:

  • The createtask action creates a new record in `'sctask' - change to 'vtb_task'`
  • The closealltasks action queries 'sc_task' - change to 'vtb_task'
  • The main data fetch queries 'sc_task' - change to 'vtb_task'


I need to preserve:

  • All existing field references (shortdescription, assignedto, state, active, etc.)
  • All existing logic and error handling
  • All existing functionality
  • The component code entirely (no changes needed)
  • All translations and other fields


Since this is a table change, I should also add a table validation check for vtb_task to ensure it exists on the instance, as this appears to be a custom or plugin-specific table (vtb likely stands for some custom application).

4 accaptrejectchanges.png

 

The AI generated some changes, which you can either accept or reject. I accepted and checked the Server Script, and sure enough, all references to sc_task were updated to vtb_task. But my open tasks were still missing from the preview.

 

I double-checked the code five times but no obvious errors, everything looked fine. Then, I tested the widget's "Create" to see if that is working and something strange happened. It successfully created and displayed a task, but it was an sc_task, not a vtb_task! I checked the code again. There were absolutely no sc_task references left.

 

5mitscTask.png

 

I tried manually saving the widget.

 

I reloaded the page several times.

 

I ran cache.do.

 

Nothing worked. The UI still displayed sc_task records. Even the Test Runner confirmed the Server Script was fetching a vtb_task and storing it in the data object, but the UI refused to reflect it.

 

6skript execution.png

 

 

Let’s ask the AI to fix it.

 

Prompt 3:

The preview ot the widget still shows sc_task even if the Script Runner of the Server Script shows that a vtb_task is fetched and stored in the data object. What is wrong?

 

AI Output: 

Spoiler

Reasoning through your request...The user is asking about a preview issue where the widget is showing sctask instead of vtbtask, even though the server script is fetching from vtb_task. Looking at the provided widget JSON, I can see:

  1. The server script is indeed querying 'vtb_task' table
  2. There's a table validation check for 'vtb_task'
  3. The script handles the case where vtb_task table doesn't exist by setting tasksUnavailable flag


The issue is likely that the vtb_task table doesn't exist in the preview environment, so it's falling back to some default behavior or the table validation is failing.
Looking more closely at the server script:

// Validate vtb_task table exists before querying it

var tableName = 'vtb_task';

var tableCheck = new GlideRecord(tableName);

if (tableCheck.isValid()) {

    // ... vtb_task logic

} else {

    data.tasksUnavailable = true;

    data.tasksUnavailableMessage = gs.getMessage('Private tasks feature is not available on this instance.');

    data.tasks = [];

    data.openTaskCount = 0;

}


The client-side component doesn't seem to handle the data.tasksUnavailable flag properly. The component should check for this flag and show an appropriate message instead of trying to render tasks.
But wait, let me re-read the user requirement. They're saying "the preview of the widget still shows sctask even if the Script Runner of the Server Script shows that a vtbtask is fetched". This suggests that when they test the server script in isolation, it correctly fetches vtbtask data, but when they preview the widget, it's showing sctask data instead.
This could be because:

  1. The widget preview is using cached data or demo data
  2. There's a demodata field that might contain sctask data
  3. The widget preview environment doesn't have vtb_task table, so some fallback is happening




  1. The demodata field is empty, so that's not the issue. The most likely scenario is that in the preview environment, the vtbtask table doesn't exist, causing the validation to fail and the widget to display empty or default data. Yet the user specifically mentions seeing sctask records, which implies there's either a fallback mechanism or cached data pulling from sctask. However, I don't see any explicit fallback to sctask in the code—the script either queries vtbtask or sets empty data with unavailable flags. I need to examine the client component to see how it handles this scenario.


The component doesn't actually check the tasksUnavailable flag; it just renders based on the tasks array. When vtbtask doesn't exist, the server sets tasks to an empty array and flags it as unavailable, but the component ignores that flag and simply displays "No open tasks." This still doesn't account for why sctask data would appear. There might be caching at play, or the preview could be serving stale data. The real issue seems to be a disconnect: the server script correctly fetches from vtbtask when run directly, but the preview widget continues showing sctask records regardless.
The component needs to actively check and respond to the tasksUnavailable flag rather than silently falling back to an empty state. I should update it to display a meaningful message when the table is unavailable, which would clarify what's happening and prevent confusion about which table is being queried.

 

But this change also did not change anything. So, I tried it for one last time.

 

Prompt 4:

I still only see sc_task records and not vtb_task records.

 

AI Output: 

Spoiler

Reasoning through your request...The user is reporting that they still only see sctask records and not vtbtask records. Looking at the existing widget, I can see that:

  1. The widget is designed to work with the 'vtb_task' table
  2. It has validation logic to check if the vtb_task table exists using tableCheck.isValid()
  3. If the table doesn't exist, it shows an unavailable message
  4. The user is saying they're seeing sctask records instead of vtbtask records


This suggests that either:

  1. The vtb_task table doesn't exist on their instance (which the widget should handle gracefully)
  2. There's some confusion about which table should be used
  3. The widget is somehow querying the wrong table


Looking at the server script, I can see it's correctly trying to query 'vtbtask'. The user's complaint suggests they want to see vtbtask records specifically, not sc_task records.
Since the user is saying they "still only see sctask records", this implies they expected to see vtbtask records but are seeing something else. However, looking at the widget code, if the vtbtask table doesn't exist, it should show the unavailable message, not sctask records.
The issue might be that they want to actually use the sctask table instead of vtbtask, or there's some other system that's showing sctask records when they expect vtbtask.
But the user's requirement is "I still only see sctask records and not vtbtask records" - this sounds like they want to see vtbtask records instead of sctask records.
However, this widget is already designed to query vtbtask, so if they're seeing sctask records, that means either:

  1. The widget is not the source of what they're seeing
  2. There's some other issue


Looking more carefully at the widget, I don't see anywhere that it would query sctask. The widget correctly queries vtbtask and has proper fallback handling when the table doesn't exist.

The phrasing could mean they want to switch from seeing sctask to seeing vtbtask, which the widget should already do, or they want to change the widget to use sctask instead. Given the ambiguity, the most straightforward interpretation is that they want the widget to work with sctask table instead of vtbtask table, since they're only seeing sctask records. I should update the widget to query sc_task instead.
This requires changing the table reference from 'vtbtask' to 'sctask' in the server script and updating any error messages accordingly. The validation logic can stay the same since sc_task is a standard ServiceNow table, though I'll maintain the safety check pattern.

Now it actually wants to change the table back to sc_task.

 

At this point, I stopped because currently I don’t know enough about the new tech stack this is running on and how everything is implemented. I need to understand it in more detail to really debug these kinds of issues.

 

But I still wanted to have a working widget, therefore I started fresh with a new widget and changed the initial prompt to be more precise.

 

New prompt:

Create a widget which shows the open private tasks [vtb_task] of the currently logged in user.  The tasks should be displayed in a table at the bottom of the widget. The widget should contain an input field to enter a Short Description for a new task to be created. The task should be created if a button with the label ‘Create’ is clicked. The widget should also contain a button to close all open private tasks of that user once it is clicked. At the top of the widget the currently logged in user should be greeted and the count of currently open private tasked should be displayed in a badge. If the user created a new task or closes all tasks the change should be directly visible on the widget.

 

AI Output: 

Spoiler

Reasoning through your request...Let me break down this widget request:

  1. Shows open private tasks from the vtb_task table for the current user
  2. Displays tasks in a table at the bottom
  3. Has an input field for creating new tasks (Short Description)
  4. Has a "Create" button to create new tasks
  5. Has a button to close all open tasks
  6. Shows user greeting at top with count of open tasks in a badge
  7. Updates should be immediately visible after creating or closing tasks


This requires:

  • Server data (vtb_task table queries)
  • Form functionality (input + create button)
  • Action handling (create task, close all tasks)
  • Real-time updates after actions


I need to check if vtb_task is a custom table - it sounds like it could be from a plugin or scoped app, so I should add a table validation check in the server script.
The widget needs:

  1. Static properties for any configuration
  2. Internal state for form data, loading states
  3. Server script that handles both data fetching and actions (create task, close all)
  4. All 4 states (loading, error, empty, data)
  5. Form validation and submission
  6. Proper accessibility
  7. i18n for all strings


Let me design this:
Static properties:

  • Could have a limit for number of tasks to show, but not strictly necessary


Internal state:

  • newTaskDescription (form input)
  • loading
  • error
  • creating (for create button loading state)
  • closing (for close all button loading state)


Server actions:

  • create_task (with description from input)
  • closealltasks


The widget should show:

  • Header with greeting + task count badge
  • Form with input + create button
  • Close all button
  • Task table


I need to make sure to use proper table structure, form validation, loading states, etc.
Let me code this:

 

8second try.png

 

The widget looked the same as in the first try and again, no tasks were visible. However,  this time the AI directly used the correct table. Reading through the Server Script, I spotted the issue immediately. The AI used an incorrect string value for the state:

gr.addQuery('state', 'Open');

 

I changed it to:

gr.addQuery('state', 1);

 

After fixing this manually, my widget was working as expected.

 

9 after change.png

 

My Takeaways

 

  • Be as precise as possible when describing the widget. Include technical table names and actual attribute values that the widget should use.

 

  • Don't rely entirely on AI. First learn how technology works and then let AI support you. Don’t let it do all the work alone if you can’t do it on your own.

 

  • The AI Experience Framework is a technology that comes with a completely new tech stack. We need more documentation to understand what is really happening in the background so that we have a chance to debug it in case something strange happens.

 

  • Looking at the current state of the AI Experience Framework and the rising demand from customers, this is going to be heavily used very soon. If you haven’t already started playing with it, now is the time.

 

 

 

 

Version history
Last update:
an hour ago
Updated by:
Contributors