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>
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>
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>
`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>
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>