I was wrong. AVEVA Tech Note 537 ("Creating an Application Object Script
Using GRAccess", April 2008) documents the supported pattern:
ConfigurableAttributes[<script>.<field>].SetValue(MxValue) inside a
CheckOut/Save/CheckIn cycle. graccesscli's existing
FindAttributeForMutation already follows this — writes to MxCategoryPackageOnly_Lockable
script-text fields persist correctly.
The earlier "writeback gap" diagnosis was a phantom caused by a reader-side
issue. `object attribute value get` against a script body returns
"Supported: False / Attribute value is not exposed" because
MxValueDetails uses a case-sensitive `ReadProperty(attr, "Value")` lookup
plus an accessor probe (GetBoolean -> GetInteger -> GetFloat -> GetDouble
-> GetString) that can fall through silently for some MxValue shapes. The
COM-side property is exposed as `value` (lowercase), readable as
`attr.value.GetString()` -- which the live probe at
`analysis/ide-edit-investigation/probe_setvalue/` does and confirms the
post-write content matches the marker exactly.
Live verification on $TestMachine.UpdateTestChangingInt.DeclarationsText
and $DelmiaReceiver.ProcessRecipe.{ExecuteText,DeclarationsText}:
=== verdict ===
marker landed on same-proxy ConfigurableAttributes: True
marker landed on same-proxy Attributes : True
marker landed on fresh-proxy ConfigurableAttributes: True
marker landed on fresh-proxy Attributes : True
The probe also confirmed that two earlier graccesscli `object scripts set`
invocations (which I had wrongly believed failed) had persisted -- the
marker text I wrote previously was still on disk in
ProcessRecipe.{ExecuteText,DeclarationsText} when read directly via
attr.value.GetString(). The probe restored both fields to their original
values.
This commit:
- Updates the misleading [Command(...)] / [CommandOption(...)]
descriptions in GRAccessSurfaceCommands.cs back to honest versions
citing TN-537.
- Restores the --file-using examples for `object scripts set` and
`object scripts create` across script-editing.md, llm-integration.md,
usage.md, and zb-testmachine.md.
- Removes the test that asserted the (wrong) EnsureMutableViaSetValue
guard. Re-aims ScriptCommandDescriptions_… at the corrected wording.
- Removes two leftover EnsureMutableViaSetValue calls in the trigger-period
/ trigger-type write paths (both targeted MxCategoryWriteable_C_Lockable
attributes; would never have fired even if the helper still existed).
- Adds analysis/ide-edit-investigation/REPORT.md (replacing the earlier
wrong report) plus the probe sources under probe_setvalue/.
The MxValueDetails reader gap (case-sensitive ReadProperty + accessor
probe) is a real follow-up: `object attribute value get` should
case-insensitively read `value` and try GetString first when the
underlying MxValue.DataType is MxString. Out of scope here -- that's a
separate, smaller fix.
Test count delta: 67 -> 66 (-2 wrong tests, +1 corrected description test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.6 KiB
LLM Integration Guide
This document is the implemented machine-facing contract for using graccess_cli as an LLM automation layer for AVEVA/Wonderware System Platform IDE work through GRAccess.
The CLI still preserves legacy human output and existing --json shapes. LLMs should use --llm-json for a stable envelope.
Stable Envelope
Galaxy-bound routed commands and LLM helper commands accept --llm-json.
{
"success": true,
"command": "object snapshot",
"galaxy": "ZB",
"target": "TestMachine",
"data": {},
"commandResult": null,
"warnings": [],
"unavailable": [],
"error": null,
"exitCode": 0
}
Failures use the same envelope with success=false, error, and exitCode.
Discovery
Use capabilities before generating plans:
graccess capabilities --json
graccess capabilities --llm-json
The capability registry is code-backed, not scraped from prose. It returns command name, dispatcher command/subcommand, argument metadata, mutation status, session routing support, confirmation target rule, and output schema name.
Read Workflow
Prefer session mode for repeated reads:
graccess session start --galaxy ZB --node .
graccess object snapshot --galaxy ZB --name TestMachine --type template --llm-json
object snapshot includes object details, all attributes, configurable attributes, extended attributes, relationships, lineage, children, contained objects, package-backed attribute values/script bodies where available, and Unavailable entries for COM-backed sections that cannot be read safely.
Useful read commands:
graccess object get --galaxy ZB --name TestMachine --type template --llm-json
graccess object lineage --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object children --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object attribute value get --galaxy ZB --name TestMachine --type template --attribute Description --llm-json
graccess object scripts list --galaxy ZB --name TestMachine --type template --llm-json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
The CLI tries direct GRAccess reads first. If direct GRAccess does not expose inheritance, attribute values, or script bodies, read commands may export the target object to a temporary .aaPKG, parse textual/package entries, recurse into nested package archives, parse binary UTF-16 script extension records, and delete the temp files. SQL Server repository reads are not part of normal CLI behavior and should only be used for development verification/debugging.
Script body access is adapter-dependent. For ScriptExtension objects, a bare script name maps to .ExecuteText; object scripts set --file <path> writes the matching script body attribute through GRAccess. Mutating script and attribute commands prefer ConfigurableAttributes[...] before Attributes[...], because script extension settings such as TriggerPeriod, TriggerType, Expression, and ExecuteText are configuration attributes per AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess"). If neither direct GRAccess nor the package fallback exposes body text on a read, script read commands return structured unavailable details — but writes via the TN-537 pattern persist; round-trip evidence is in analysis/ide-edit-investigation/probe_setvalue/.
Use the script settings wrapper for IDE-style script configuration:
graccess object scripts settings set --galaxy ZB --name '$TestMachine' --type template --script UpdateTestChangingInt --trigger-period-ms 500 --lock-trigger-period --confirm --confirm-target '$TestMachine' --llm-json
Use the create wrapper for new object-level script extensions:
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --file OnScan.txt --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json
Inheritance And Embedded Objects
For template families, model inheritance and containment explicitly. Do not rely on a single field being populated by every GRAccess version.
graccess object snapshot --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object lineage --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object children --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object query-condition --galaxy ZB --type all --condition derivedOrInstantiatedFrom --value '$gMachine' --llm-json
graccess object query-condition --galaxy ZB --type all --condition basedOn --value '$gMachine' --llm-json
graccess object query-condition --galaxy ZB --type all --condition containedBy --value '$TestMachine' --llm-json
Read data.Lineage, data.Children, data.ContainedObjects, data.Object.DerivedFrom, data.Object.BasedOn, ContainedName, and HierarchicalName when available. The documented ZB $TestMachine family is an example: $TestMachine extends $gMachine, while $TestMachine.DelmiaReceiver and $TestMachine.MESReceiver are contained embedded templates.
Create derived templates and instances with contained objects when the family requires embedded children:
graccess template derive --galaxy ZB --name '$gMachine' --type template --new-name '$MyMachine' --confirm --confirm-target '$gMachine' --llm-json
graccess template instantiate --galaxy ZB --name '$TestMachine' --type template --new-name TestMachine_021 --create-contained --confirm --confirm-target '$TestMachine' --llm-json
Edit contained child templates or instances by targeting their own object names, such as $TestMachine.DelmiaReceiver or DelmiaReceiver_001, and use normal checkout/save/checkin safety.
Validation And Batch
Per-command dry-run validates command shape and confirmation without calling mutating GRAccess methods:
graccess object attribute value set --galaxy ZB --name TestMachine --type template --attribute Description --value Updated --data-type string --confirm --confirm-target TestMachine --dry-run --llm-json
Plan validation:
graccess validate --request plan.json --llm-json
Batch validation or execution:
graccess batch --file plan.json --mode validate --llm-json
graccess batch --file plan.json --mode execute --llm-json
Plan shape:
{
"Galaxy": "ZB",
"Node": ".",
"Commands": [
{
"Command": "object checkout",
"Args": {
"name": "TestMachine",
"type": "template",
"confirm": true,
"confirm-target": "TestMachine"
}
}
]
}
Every mutating step must include its own confirm=true and exact confirm-target. There is no global mutation confirmation. Batch execute stops on first failure.
IDE Intent Wrappers
Use intent-level commands instead of generic property edits when possible:
graccess area list --galaxy ZB --llm-json
graccess area create --galaxy ZB --template '$Area' --name Area_Test --confirm --confirm-target '$Area' --llm-json
graccess engine list --galaxy ZB --llm-json
graccess engine create --galaxy ZB --template '$AppEngine' --name AppEngine_Test --confirm --confirm-target '$AppEngine' --llm-json
graccess instance assign-area --galaxy ZB --name TestMachine_001 --area Area_Test --confirm --confirm-target TestMachine_001 --llm-json
graccess instance assign-engine --galaxy ZB --name TestMachine_001 --engine AppEngine_Test --confirm --confirm-target TestMachine_001 --llm-json
graccess instance assign-container --galaxy ZB --name TestMachine_001 --container ParentObject --confirm --confirm-target TestMachine_001 --llm-json
graccess io assign --galaxy ZB --name TestMachine_001 --attribute DeviceAddress --value D100 --confirm --confirm-target TestMachine_001 --llm-json
object set --property area|host|container|toolset|security-group attempts to resolve the named GRAccess object first, then falls back to raw string assignment for version-specific setters.
Safety Rules
- Start with
capabilitiesand read-only snapshots. - Do not mutate without an exact object or file target.
- Never use wildcard bulk mutation in production.
- Use
--dry-run --llm-jsonbefore mutation plans. - Snapshot before editing and re-read after editing.
- Stop after the first failed mutation or
CommandResult.Successful=false. - Treat missing or unavailable fields as unknown, not false.
- Prefer test objects or derived templates before production targets.
- Use session mode for repeated galaxy-bound commands.
- Use deployment commands only when the user explicitly names targets.