...
write_objects mechanics on DisplaysList (document objects, read-before-write)write_objects → get_state → check errorList → visual checkpoint → fix → move on)...
| Tool | When |
|---|---|
get_table_schema('DisplaysList') | Once at start of session to confirm field names |
list_elements('ThemeColors') | Once to get the brush catalogue |
list_elements('<ElementName>') | Before using an element type you have not used this session |
list_dynamics() / list_dynamics('<Type>') | Before attaching a dynamic you have not used this session |
get_objects('DisplaysList', names=['X'], detail='full') | Before every modification to an existing display (document-object rule) |
write_objects(table_type='DisplaysList', data=[...]) | After your plan is complete |
get_state(target='designer') | After every write, to check errorList |
get_screenshot(target='display', name='X') | At visual checkpoints during a Canvas build, or once at the end of a Dashboard build. See §3 for the cap. |
...
get_objects(detail='full') if modifying an existing displaywrite_objects(data=[...])get_state(target='designer'), look at errorListerrorList non-empty, fix and go back to step 3...
If errorList is absent or empty, the display compiled clean.
errorList is ground truth for compile correctness: bindings resolve, required fields are present, the display will render without crashing. A clean errorList is necessary before you declare the display done.
errorList is blind to visual correctness. None of the following produce an error:
Left/TopTextBlock whose LinkedValue clips because its rendered length exceeds WidthRadialGauge with YMaxValue below the tag's engineering-range maximum (pegs at max forever)Polygon with valid Points but Fill equal to the background (invisible)All of those compile clean. All are operator-rejected.
This step closes the gap errorList leaves open. The rules differ by paradigm because spatial correctness matters far more in Canvas than in Dashboard.
Canvas: spatial correctness is the whole point. Coordinate-based composition of pipes, vessels, zone layouts, and P&ID flow is the class of work where authoring blind gets it wrong the most. Take get_screenshot at authoring checkpoints:
Hard cap: 3 screenshots per display per session. If three checkpoints did not resolve the visual problem, a fourth will not either — the problem is judgment, not pixels. Hand back to the user with a summary of what was built and what looks wrong; ask them to open Designer and guide the next move.
Dashboard: grid reflow is resolved at render time against a specific window size, and most Dashboard mistakes (wrong control choice for the cell purpose, missing Cell.HeaderLink, broken DataGrid → detail wiring) are visible in the structure, not the pixels. Take one get_screenshot at the end, before declaring done, to confirm cells populated and content did not collapse unexpectedly. If that one screenshot reveals a problem, fix and screenshot again; beyond the second screenshot, hand back to the user.
Not for iterative self-soothing. Do not take a screenshot after every write just to feel certain. The cost is real and the behavior encourages re-work spirals over clean planning. Screenshots go at checkpoints, not at every turn.
Write a one-paragraph summary when you declare done, regardless of paradigm: which zones or cells exist, what lives in each, which tags drive what. The summary is what the user reads before they look at the display themselves — and it forces you to notice gaps ("I reserved a zone for alarms but never put an AlarmViewer in it") that errorList cannotwrite_objects success IS the confirmation. The errorList check covers compile correctness. Screenshots are only for sharing visual context with the user — not for the AI to confirm its own work.
...
// Silently invisible
{ "Type": "Polygon", "Left": 100, "Top": 100, "Width": 50, "Height": 50 }
//
...
Renders
...
correctly
...
{
...
"Type":
...
"Polygon",
...
"Left":
...
100,
...
"Top":
...
100,
...
"Width":
...
50,
...
"Height":
...
50,
...
"Points":
...
"25,0
...
50,50
...
0,50"
...
}
Polygon auto-closes (last point connects back to first). Polyline doesn't. For pipe runs use Gridline (constrains to horizontal/vertical segments — the P&ID convention).
...
Every symbol uses Type: "Symbol". Not Type: "Pump", not Type: "Valve". One element type, many SymbolName values.
{
"Type": "Symbol",
"SymbolName": "Wizard/PUMP",
"Left": 400, "Top": 300,
"Width": 80, "Height": 80,
"SymbolLabels": [
{ "Type": "SymbolLabel", "Key": "State", "LabelName": "State", "LabelValue": "@Tag.Pump1/Running", "FieldType": "Expression" },
{ "Type": "SymbolLabel", "Key": "RPM", "LabelName": "RPM", "LabelValue": "@Tag.Pump1/Speed", "FieldType": "Expression" }
]
}The complete Wizard catalog is 5 symbols:
...
The Contents field format starts with CSharp\r\n or VBdotNet\r\n before the code. Full CodeBehind guidance lives in the Skill Scripts Expressions skill.
Full CodeBehind guidance lives in the Skill Scripts Expressions skill.
| Mistake | Fix |
|---|---|
Declaring done on empty errorList alone | errorList covers compile correctness only. Run the paradigm-specific visual checkpoint in §3 before declaring done. |
| Taking screenshots after every write | Checkpoints, not reassurance. Canvas: up to 3 per display. Dashboard: one at end. |
| Mistake | Fix |
| Hardcoding hex without thinking about theme switching | Use *Theme properties; reserve hex for process-meaning |
| Polygon / Gridline without Points | Always include Points — min 3 for Polygon, 2 for Polyline/Gridline |
Using ObjectName instead of Name | Field is always Name |
Using DisplaysDraw as table_type | Visual editor UI, not a writable table. Use DisplaysList |
Omitting PanelType | Required — "Canvas" or "Dashboard" |
Wrapping the envelope in JsonFormat | Properties go at top level, no wrapper |
| Partial write on an existing display | Always read-modify-write the complete document |
Using @Label.X in a display-element binding | @Label. is for symbol internals only — use @Tag. |
| Setting colors without clearing theme | Set value AND clear theme: {Fill: '#FF3498DB', FillTheme: ''} |
| Relying on a static element/symbol list | Always call list_elements() / list_dynamics() to get the authoritative catalog at runtime |
...
# Session startup
get_table_schema('DisplaysList')
list_elements('ThemeColors')
# ...
...
...
...
...
...
...
...
list_elements('<ElementName>')
...
list_dynamics('<DynamicName>')
...
...
...
...
...
get_objects('DisplaysList',
...
names=['X'],
...
detail='full')
...
write_objects(table_type='DisplaysList',
...
data=[...])
...
get_state(target='designer')
...
#
...
verify
...
errorList
...
empty
get_screenshot(target='display', name='X')
...
Display envelope template:
{
"Name": "MyDisplay",
"PanelType": "Canvas",
"DisplayMode": "Page",
"Navigate": "true",
"Size": "1600 x 900",
"OnResize": "StretchFill",
"Width": 1600,
"Height": 900,
"Background": "theme:PageBackground",
"Elements": []
}...