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