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
@@ -426,7 +426,7 @@ namespace ZB.MOM.WW.GRAccess.Cli.Commands
public override Dictionary<string, object> Args() { var args = base.Args(); args["script"] = Script; return args; }
}
[Command("object scripts set", Description = "Attempt to set object script text through GRAccess; package-only ScriptExtension text fails fast")]
[Command("object scripts set", Description = "Set an object script text field via IAttribute.SetValue on ConfigurableAttributes (TN-537 pattern)")]
public sealed class ObjectScriptsSetCommand : ObjectScriptsGetCommand
{
public override string Subcommand => "scripts-set";
@@ -434,7 +434,7 @@ namespace ZB.MOM.WW.GRAccess.Cli.Commands
[CommandOption("file", Description = "Script source file", IsRequired = true)]
public string File { get; init; }
[CommandOption("field", Description = "Script-text field to write: ExecuteText (default), DeclarationsText, StartupText, ShutdownText, OnScanText, OffScanText, Expression. Package-only fields fail fast instead of silently no-oping.")]
[CommandOption("field", Description = "Script-text field to write: ExecuteText (default), DeclarationsText, StartupText, ShutdownText, OnScanText, OffScanText, Expression. Pass to target a non-body field; omit to default to ExecuteText.")]
public string Field { get; init; } = "";
public override Dictionary<string, object> Args() { var args = base.Args(); args["file"] = File; args["field"] = Field; return args; }
@@ -454,7 +454,7 @@ namespace ZB.MOM.WW.GRAccess.Cli.Commands
[CommandOption("trigger-type", Description = "Script trigger type value")]
public string TriggerType { get; init; } = "";
[CommandOption("expression", Description = "Script expression text. On local ScriptExtension builds this is package-only and fails fast.")]
[CommandOption("expression", Description = "Script expression value")]
public string Expression { get; init; } = "";
[CommandOption("lock-trigger-period", Description = "Lock TriggerPeriod in this object after setting it")]
@@ -475,7 +475,7 @@ namespace ZB.MOM.WW.GRAccess.Cli.Commands
}
}
[Command("object scripts create", Description = "Create a ScriptExtension primitive and optionally initialize mutable settings")]
[Command("object scripts create", Description = "Create a ScriptExtension primitive and optionally initialize its body/settings")]
public sealed class ObjectScriptsCreateCommand : ObjectScriptSettingsCommandBase
{
public override string Subcommand => "scripts-create";
@@ -1664,7 +1664,6 @@ namespace ZB.MOM.WW.GRAccess.Cli.GRAccess
if (!string.IsNullOrWhiteSpace(triggerPeriodMs))
{
var attr = FindAttributeForMutation(obj, scriptName + ".TriggerPeriod");
EnsureMutableViaSetValue(attr, scriptName + ".TriggerPeriod");
attr.SetValue(CreateMxValue(triggerPeriodMs, "elapsed-ms"));
yield return CommandSummary(attr, $"Set script {scriptName}.TriggerPeriod");
@@ -1679,7 +1678,6 @@ namespace ZB.MOM.WW.GRAccess.Cli.GRAccess
if (!string.IsNullOrWhiteSpace(triggerType))
{
var attr = FindAttributeForMutation(obj, scriptName + ".TriggerType");
EnsureMutableViaSetValue(attr, scriptName + ".TriggerType");
attr.SetValue(CreateMxValue(triggerType, "string"));
yield return CommandSummary(attr, $"Set script {scriptName}.TriggerType");