Commit Graph

7 Commits

Author SHA1 Message Date
Joseph Doherty 4e242ca824 Revert "graccesscli: fail fast on package-only attribute writes (writeback gap fix)"
This reverts commit bd95ace1c5.
2026-05-05 21:19:00 -04:00
Joseph Doherty 87c0124174 Clarify script mutation limits 2026-05-05 16:40:55 -04:00
Joseph Doherty c12fbc5988 graccesscli/docs: document the package-only writeback boundary
Adds a new "Editing Script Content" section to script-parsing.md that
explains the IAttribute.SetValue silent-no-op behavior on
MxCategoryPackageOnly* attributes, enumerates which ScriptExtension
fields fall on each side of the boundary (text fields = no, trigger
settings = yes), names the EnsureMutableViaSetValue safety check that
landed in bd95ace, and includes a one-liner for inspecting any
object's package-only attributes via `object attributes --configurable`.

Closes the documentation gap surfaced when validating the round-trip
script edit on \$DelmiaReceiver.ProcessRecipe — the previous docs
described the read path comprehensively but said nothing about which
attributes can actually be written.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:52:57 -04:00
Joseph Doherty bd95ace1c5 graccesscli: fail fast on package-only attribute writes (writeback gap fix)
Investigation findings: IAttribute.SetValue silently no-ops for any
attribute whose AttributeCategory starts with `MxCategoryPackageOnly`.
The COM call returns Successful=true and the dispatcher reports OK,
but a verify-read via direct IAttribute.GetValue confirms the value
is unchanged.

Discovered while validating the new --field flag round-trip on
\$DelmiaReceiver.ProcessRecipe.ExecuteText:

- BEFORE write: ExecuteText body = `Me.RecipeDownloadFlag = false;`
- WRITE: scripts set --field ExecuteText --file marker.txt → OK
- AFTER write: ExecuteText body = `Me.RecipeDownloadFlag = false;`
  (UNCHANGED — the marker prefix from marker.txt is absent)
- ConfigurableAttributes shows `ProcessRecipe.ExecuteText` category =
  `MxCategoryPackageOnly_Lockable`. Same for DeclarationsText,
  StartupText, ShutdownText, OnScanText, OffScanText, Expression.
- Per docs/script-parsing.md:8, "Object-level scripts ... may appear
  as attributes, extension attributes, or only inside exported object
  packages" — script-text fields are in the third bucket.

Trigger settings (TriggerType, TriggerPeriod) are
`MxCategoryWriteable_C_Lockable` and remain effective via SetValue —
verified by `scripts settings set --trigger-period-ms 1000` returning
OK on a clean checkout state.

Add `EnsureMutableViaSetValue(IAttribute, name)` helper that throws
InvalidOperationException with a clear remediation hint when called on
a package-only attribute. Wired into three write sites:

- `case "value-set"` (object attribute value set)
- `ObjectScriptSet` (scripts set body writer)
- `SetScriptSettings` Expression branch (the one trigger-related field
  that's package-only; TriggerType/TriggerPeriod stay unguarded)

Validation against live ZB galaxy:

- Before fix: `scripts set --field ExecuteText` returned
  `success: true` but didn't mutate the body.
- After fix: same call returns `success: false` with
  `"Attribute 'ProcessRecipe.ExecuteText' has category
  'MxCategoryPackageOnly_Lockable'. Package-only attributes silently
  no-op via IAttribute.SetValue and can only be edited through the
  package import/export round-trip..."` (exit code 1).
- `scripts settings set --trigger-period-ms 1000` against the same
  script: still OK (TriggerPeriod is Writeable_C_Lockable).

The real fix for editing script bodies is the package-rewrite path —
export `.aaPKG`, modify the binary script-extension records, re-import
via `galaxy.ImportObjects`. graccesscli already has the read half
(`PackageSnapshotParser.Parse`); the write half is a separate followup.

61 → 63 → 63 (no test count delta this commit; the new helper is exercised
end-to-end by the live validation above; unit tests for the failure path
require a mock IAttribute proxy that's out of scope here).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:45:07 -04:00
Joseph Doherty 3f6bfebd6d graccesscli: extend script editor surface — --field, --lock-trigger-type, scripts delete
Three additions to the script editor commands. Each one closes a real
gap surfaced by the round-trip-test against \$DelmiaReceiver.ProcessRecipe.

1. `object scripts set --field <name>` — explicit text-field selection.

   Previously `scripts set` always wrote to <Name>.ExecuteText (via
   ScriptAttributeName's default). To rewrite DeclarationsText / StartupText
   / ShutdownText / OnScanText / OffScanText / Expression, callers had to
   pass the full attribute name as `--script Foo.StartupText`, which is
   brittle. The new `--field` flag accepts any of the seven canonical
   ScriptTextSuffixes and composes <script>.<field> directly. Validates
   against the suffix list so an unrecognised --field surfaces a friendly
   error rather than a downstream FindAttributeForMutation failure.
   Default behavior (no --field) is unchanged: ExecuteText.

2. `object scripts settings set --lock-trigger-type` — parallel to the
   existing --lock-trigger-period. After writing TriggerType the new flag
   calls SetLocked(MxLockedInMe), matching the lock pattern on the period
   field. Without it, --trigger-type writes the value but leaves the
   attribute unlocked.

3. `object scripts delete` — script-named alias for the existing
   extension-delete subcommand. Wraps obj.DeleteExtensionPrimitive(
   "ScriptExtension", scriptName) inside AtomicObjectEdit (checkout / save
   / checkin). Removes the burden of remembering the generic
   `--extension-type ScriptExtension --primitive <Name>` form.

Test count delta: 61 -> 63 (+2 command-shape assertions for the new
ObjectScriptsSetCommand and ObjectScriptsDeleteCommand).

Live round-trip-test against \$DelmiaReceiver.ProcessRecipe:
- `--field DeclarationsText` write composed `ProcessRecipe.DeclarationsText`,
  CheckOut/Save/CheckIn all returned OK.
- `--field ExecuteText` round-trip same.
- A subsequent re-read shows the original body, suggesting that
  IAttribute.SetValue silently no-ops for ScriptExtension text fields on
  this GRAccess version (or the package-export reader pulls from a
  different snapshot than the just-saved revision). This is upstream of
  the editor surface — the new flags route correctly to the same SetValue
  path that scripts set already used. Diagnosing the SetValue
  ineffectiveness for script-text fields is a separate followup that
  should look at IScriptExtension-specific COM interfaces (per
  docs/script-parsing.md:8 "Object-level scripts are less direct").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:09:34 -04:00
Joseph Doherty 65537570b8 graccesscli: enumerate ScriptExtension via canonical text-attribute suffixes
`object scripts list` previously walked obj.Attributes through a
substring heuristic (`IsScriptAttribute`) that matched any name
containing "script", "expression", "execute", "startup", "shutdown",
or "scan". Two failure modes:

- "deSCRIPTion" contains "script", so every `MoveIn*.Description` /
  `MoveOut*.Description` attribute on `$MESReceiver` (and any other
  object with `.Description` UDAs) was mis-emitted as a script.
- `ScanState` / `ScanStateCmd` (ordinary attribute UDAs) leaked in via
  the "scan" keyword.

Net result on $MESReceiver: 25 entries, all false positives, zero
true ScriptExtensions.

The GRAccess COM API does not expose an `IScriptExtension` enumerable
(verified against ArchestrA.GRAccess.dll string table — only
`AddExtensionPrimitive` / `DeleteExtensionPrimitive` /
`RenameExtensionPrimitive` mutators exist). The only attribute-side
signal that an object carries a real ScriptExtension is the canonical
text-attribute suffix list documented in docs/script-parsing.md:
ExecuteText, DeclarationsText, StartupText, ShutdownText, OnScanText,
OffScanText, Expression.

Replace `IsScriptAttribute` with `ScriptExtensionPrefix(name)` that
detects exactly those suffixes, then group attributes by prefix:
- Each ScriptExtension yields one logical entry named by its prefix.
- The Fields[] list reports which text attributes are present.
- Sibling `<prefix>.TriggerType` / `<prefix>.TriggerPeriod` attributes
  are surfaced as TriggerType / TriggerPeriod when present.
- ExtensionType is always "ScriptExtension".

Outer `ObjectScripts(IGalaxy, IgObject, PackageSnapshot)` updated to:
- Thread ExtensionType / Fields / TriggerType / TriggerPeriod through.
- Look up body content by prefix first, then fall back to
  `<prefix>.ExecuteText` (the package's serialized bodies are keyed
  by full attribute name).
- Mark every `<prefix>.<field>` as emitted so the package-fallback
  branch doesn't re-emit each text field as its own entry.

Drop dead `IsScriptAttribute` (no other callers).

Validation against live ZB galaxy:
- $MESReceiver: 25 → 0 (correct: no ScriptExtensions on this template)
- $TestMachine: 1 entry — UpdateTestChangingInt with body
  "Me.TestChangingInt = System.Random().Next(1,1000);" + all 7 fields
  + trigger metadata.
- $DelmiaReceiver: 2 entries — ProcessRecipe + Reset, both with bodies
  ("Me.RecipeDownloadFlag = false;") + trigger metadata.

61/61 existing tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:54:41 -04:00
Joseph Doherty 32f26272ae Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md:

- aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary
  logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands:
  last, tail, range, unread, fields. Stable JSON envelope under --llm-json.
  Build template under lib/build/ for rebuilding aaLogReader.dll.

- aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide
  (Markdown converted from CHM), API reference for the ArchestrA.Toolkit
  namespace, and the Monitor / Watchdog VS sample solutions.

- graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy
  configuration via the ArchestrA GRAccess COM interop. Includes session
  daemon, IPC protocol, and llm-json envelope contract.

- grdb: SQL/DDL exploration of the Galaxy Repository database. DDL
  captures, reusable queries, hierarchy / contained-name <-> tag-name
  translation notes.

- histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL
  linked-server, extension tables, every wwXxx time-domain extension,
  every retrieval mode, alarm/event SQL recipes, REST API. Distilled
  from the 243-page Historian Retrieval Guide.

Root contains:
- CLAUDE.md: thin index pointing into each tool's README.
- DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:22:20 -04:00