graccesscli: correct script-edit docs to TN-537 truth (writes DO persist)

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>
This commit is contained in:
Joseph Doherty
2026-05-05 21:33:51 -04:00
parent e4e54254d8
commit c52d8d0171
14 changed files with 3346 additions and 38 deletions
+2 -2
View File
@@ -60,7 +60,7 @@ graccess object scripts get --galaxy ZB --name TestMachine --type template --scr
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`. Mutating script and attribute commands prefer `ConfigurableAttributes[...]` before `Attributes[...]`. On the local GRAccess build, body fields such as `ExecuteText` and `Expression` are package-only and are not persisted by `IAttribute.SetValue`, so the CLI fails fast for those writes instead of returning a false success. Mutable settings such as `TriggerPeriod` and `TriggerType` remain available through `object scripts settings set`. If neither direct GRAccess nor the package fallback exposes body text, script read commands return structured unavailable details instead of pretending success.
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:
@@ -71,7 +71,7 @@ graccess object scripts settings set --galaxy ZB --name '$TestMachine' --type te
Use the create wrapper for new object-level script extensions:
```powershell
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json
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
+13 -12
View File
@@ -17,7 +17,7 @@ For LLM-driven script work, read script metadata and validate guarded edits with
```powershell
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
graccess object scripts settings set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --trigger-period-ms 500 --confirm --confirm-target TestMachine --dry-run --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --dry-run --llm-json
```
## What The CLI Can Edit Today
@@ -28,8 +28,9 @@ graccess object scripts settings set --galaxy ZB --name TestMachine --type templ
| Script library export | `script-library export` |
| Script library import/add | `script-library import`, `script-library add`, `galaxy import-script-library` |
| Object script metadata | `object scripts list`, `object scripts get` |
| Object script body read | `object scripts get` package fallback for script body attributes such as `ExecuteText` |
| Object script body read/write | `object scripts get` (package fallback) and `object scripts set --file <path> [--field <name>]` for body fields like `ExecuteText` / `DeclarationsText` / `StartupText` / `ShutdownText` / `OnScanText` / `OffScanText` / `Expression`. See AVEVA Tech Note 537. |
| Object script mutable settings | `object scripts settings set --trigger-type`, `--trigger-period-ms` |
| Object script create/delete | `object scripts create`, `object scripts delete` |
| Script-like attributes | `object attribute set`, lock, security, buffer |
| Extension primitives | `object extension add/delete/rename` |
| Full object package script payloads | Use `objects export` and `galaxy import-objects` |
@@ -91,17 +92,17 @@ $scripts = (@($attrs) + @($extended)) | Where-Object {
$scripts | Select-Object Name, DataType, Category, Locked
```
If the script-like setting is a scalar writable attribute, edit it through the normal template edit flow. Script extension attributes are looked up through `ConfigurableAttributes` first, then `Attributes`.
For local `ScriptExtension` objects, body fields such as `ExecuteText`, `StartupText`, `OnScanText`, and `Expression` are package-only. GRAccess `IAttribute.SetValue` can return success without persisting package-only fields, so `object scripts set` and `object scripts settings set --expression` fail fast when the attribute category starts with `MxCategoryPackageOnly`. Use the IDE or a future package-rewrite path for those fields.
If the script-like setting is a scalar writable attribute, edit it through the normal template edit flow. Script extension attributes are looked up through `ConfigurableAttributes` first, then `Attributes` — the pattern AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess") prescribes for `ScriptExtension` text fields. `object scripts set`, `object scripts settings set`, and `object attribute value set` all use that ordering. A bare `--script <name>` writes to `<name>.ExecuteText`; pass `--field <FieldName>` (or include the suffix in `--script`) to target `DeclarationsText`, `StartupText`, `ShutdownText`, `OnScanText`, `OffScanText`, or `Expression`.
```powershell
graccess object checkout --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object attribute value set --galaxy ZB --name TestMachine --type template --attribute SomeWritableScriptSetting --value Updated --data-type string --confirm --confirm-target TestMachine
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine
graccess object save --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Update writable script setting' --confirm --confirm-target TestMachine
graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Update script body' --confirm --confirm-target TestMachine
```
> **Reader caveat.** `object attribute value get` against a script text field may report `Supported: False` ("Attribute value is not exposed by this GRAccess attribute") even when the value is set. The write itself persists; the limitation is in `MxValueDetails` reading the COM-side `value` accessor for these field types. Verify post-write content with `object scripts get --llm-json` (which uses the package-export readback) instead. Round-trip evidence is in `analysis/ide-edit-investigation/probe_setvalue/` (commit `e4e5425..`).
Update periodic script settings and lock the interval for deployment inheritance:
```powershell
@@ -140,16 +141,16 @@ Delete:
graccess object extension delete --galaxy ZB --name TestMachine --type template --extension-type ScriptExtension --primitive OnScan2 --object-extension --confirm --confirm-target TestMachine
```
These commands manage primitive structure. The wrapper below creates the same `ScriptExtension` primitive and can initialize mutable settings:
These commands manage primitive structure. The wrapper below creates the same `ScriptExtension` primitive and can initialize the body and common settings in the same checkout flow (per AVEVA TN-537: AddExtensionPrimitive → Save → set body/settings via ConfigurableAttributes):
```powershell
graccess object checkout --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate'
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate'
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'
graccess object save --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate'
graccess object checkin --galaxy ZB --name '$MyTemplate' --type template --comment 'Add OnScan script' --confirm --confirm-target '$MyTemplate'
```
After adding a `ScriptExtension` primitive, body text still needs the IDE/package path when the projected body attributes are package-only.
After adding a `ScriptExtension` primitive, set its body with `object scripts set --script <primitiveName>` (defaults to `ExecuteText`) or target a different field with `--field DeclarationsText` / `StartupText` / etc.
## Full-Fidelity Object Script Edits
@@ -203,7 +204,7 @@ graccess instance deploy --galaxy ZB --name TestMachine_ScriptTest_001 --type in
```text
graccess object scripts list --galaxy ZB --name TestMachine --type template --json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
graccess object scripts settings set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --trigger-period-ms 500 --confirm --confirm-target TestMachine --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --llm-json
```
The local GRAccess examples sometimes show `Template.Scripts[index].ScriptString`, but this repository's installed `ArchestrA.GRAccess.dll` does not expose a public object script collection. It exposes script extension projections as attributes and package records. The CLI can read body text through exported package parsing and can write mutable settings through `IAttribute.SetValue`; package-only body/expression fields are not persisted by that GRAccess setter.
The local GRAccess examples sometimes show `Template.Scripts[index].ScriptString`, but this repository's installed `ArchestrA.GRAccess.dll` does not expose a public object script collection — it exposes script extension projections as attributes (e.g. `UpdateTestChangingInt.ExecuteText`). The CLI writes those attributes through the AVEVA TN-537 pattern: `IgObject.ConfigurableAttributes[<script>.<field>].SetValue(MxValue)`, surrounded by `CheckOut → Save → CheckIn`. Reads use the package-export fallback when the GRAccess attribute-value accessor doesn't surface the field directly.
+4 -5
View File
@@ -242,15 +242,14 @@ Scalar `string`, `bool`, `int`, `float`, and `double` writes are supported first
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
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt.ExecuteText --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --llm-json
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
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json
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
```
Direct object script body access depends on the local GRAccess object model. Reads inspect the exported package fallback for script extension bodies and script text fields such as `ExecuteText`, `DeclarationsText`, `StartupText`, `ShutdownText`, `OnScanText`, `OffScanText`, and `Expression`.
For writes, the CLI follows AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess"): script body and setting mutations prefer `IgObject.ConfigurableAttributes[...]`, then fall back to `Attributes[...]` only if the configurable collection does not expose the requested field. `object scripts set` writes the matching script body attribute via `IAttribute.SetValue(MxValue)`; a bare `--script <name>` defaults to `<name>.ExecuteText`; pass `--field <FieldName>` to target `DeclarationsText`, `StartupText`, `ShutdownText`, `OnScanText`, `OffScanText`, or `Expression`. `object scripts settings set` writes common script settings such as `TriggerPeriod`, `TriggerType`, and `Expression`; `--lock-trigger-period` / `--lock-trigger-type` apply `MxLockedInMe` so derived instances inherit the value on deploy. `MxElapsedTime` values are stored in 100 ns increments, so `--trigger-period-ms 500` writes `5,000,000`. `object scripts create` calls `AddExtensionPrimitive("ScriptExtension", <script>, true)` then initializes the body/settings in the same checkout flow.
For writes, the CLI uses the public GRAccess path exposed by `IgObject.ConfigurableAttributes[...]` / `Attributes[...]` and `IAttribute.SetValue`. This is valid for mutable settings such as `TriggerPeriod` and `TriggerType`. `MxElapsedTime` values are stored in 100 ns increments, so `--trigger-period-ms 500` writes `5,000,000`.
Script body fields and `Expression` on the local `ScriptExtension` projection are package-only (`MxCategoryPackageOnly*`). GRAccess `IAttribute.SetValue` can report success for those fields without persisting the value, so the CLI now fails fast when it sees a package-only category. `object scripts create` can add the `ScriptExtension` primitive and initialize mutable settings, but it cannot initialize package-only body text through GRAccess. Use the IDE or a future package-rewrite path for body/expression edits.
For reads, `object scripts get --llm-json` and `object snapshot --llm-json` use the package-export fallback to surface body content. `object attribute value get` against a script text field may report `Supported: False` ("Attribute value is not exposed by this GRAccess attribute") even when the value is set — that's a `MxValueDetails` accessor-probe gap (the value persisted, the reader didn't surface it). Use `object scripts get` to verify post-write content. Round-trip evidence: `analysis/ide-edit-investigation/probe_setvalue/`.
### Area, engine, assignment, and I/O wrappers
+1 -1
View File
@@ -316,5 +316,5 @@ The parent instance shape matches `$TestMachine` by count and script metadata. `
- Edit embedded child objects by targeting the child instance tagname, such as `DelmiaReceiver_001`, or its hierarchical name when a command supports hierarchical lookup.
- Do not mutate `$TestMachine` until the existing checkout state is understood; the template was `checkedOutToMe` during capture.
- Direct scalar value readback for many template attributes is not exposed by the current generic attribute value path.
- Direct script body readback is not exposed by the current generic value path; use `object scripts get --llm-json` package fallback for reads. Package-only script body/expression fields are not persisted by GRAccess `IAttribute.SetValue`; use the IDE/package path for those edits.
- Direct script body readback is not exposed by the current generic value path; use `object scripts get --llm-json` package fallback for reads and `object scripts set --file <path> [--field <name>]` for script body attribute writes. The CLI follows AVEVA Tech Note 537 — writes go through `IgObject.ConfigurableAttributes[<script>.<field>].SetValue(MxValue)` and persist correctly even though `object attribute value get` may report the post-write value as "not exposed" (that's a `MxValueDetails` reader-side gap, not a write-side no-op).
- Extended attributes failed because the local COM type library was not registered.