From af214e13ff2b5d525a399f21ae91414bc47e4c4b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:11:06 +0700 Subject: [PATCH 01/80] docs: normalize interoperability evidence authority --- .../interoperability-convergence-target.json | 492 ++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 evidence/interoperability-convergence-target.json diff --git a/evidence/interoperability-convergence-target.json b/evidence/interoperability-convergence-target.json new file mode 100644 index 000000000..23b1f4354 --- /dev/null +++ b/evidence/interoperability-convergence-target.json @@ -0,0 +1,492 @@ +{ + "schemaVersion": 1, + "name": "IEC 61850 discovery and SCL interoperability convergence", + "status": "physical-retest-passed-merge-ready", + "target": { + "discovery": "Maintain bounded MMS discovery efficiency while preserving exact IEC 61850 model evidence.", + "model": "Build the canonical IEC 61850 model from structure-first MMS evidence with correct LN identity, DO/SDO hierarchy, FC ownership, DataSet/RCB semantics, and schema-aware CDC handling.", + "scl": "Save Edition 2 IID / Edition 1 ICD that reopens in ARSAS, reconnects to the relay, and is usable as trusted SCL." + }, + "activeStack": { + "enginePerformance": { + "repository": "masarray/ARIEC61850", + "pullRequest": 134, + "head": "a31e396f0bbc9507215a8c9d125e910555ed6f0a", + "status": "merged", + "testedHead": "a31e396f0bbc9507215a8c9d125e910555ed6f0a", + "mainMerge": "e6779ff74e5716af4fcfc3dc926dae0567b3cdb0" + }, + "engineModelAndScl": { + "repository": "masarray/ARIEC61850", + "pullRequest": 135, + "basePullRequest": 134, + "head": "648124097621046f5f127ceb1cf853fea54db730", + "status": "merged", + "testedHead": "9935d6902d786cc69b299260fe36b835944d5e81", + "mainMerge": "648124097621046f5f127ceb1cf853fea54db730" + }, + "consumerIntegration": { + "repository": "masarray/arsas", + "pullRequest": 324, + "branch": "test/smart-ied-discovery-pr134", + "status": "physical-proven-merge-ready" + } + }, + "physicalReference": { + "relay": "AA1E1F06R4", + "logicalDevices": 32, + "logicalNodes": 119, + "dataSets": 2, + "runtimeReportControls": 34, + "logicalSclReportControls": 32, + "acceptedDiscoveryBudget": { + "associations": 1, + "maximumConfirmedMmsRequests": 417, + "maximumGetVariableAccessAttributes": 119, + "maximumReads": 156, + "note": "Internal physical acceptance ceiling retained as a regression budget." + }, + "rejectedLegacyArsasCapture": { + "associations": 2, + "confirmedMmsRequests": 30552, + "getVariableAccessAttributes": 23724, + "reads": 6548, + "note": "Regression signature. A future field build showing this pattern is rejected." + }, + "acceptedSclShape": { + "lDevices": 32, + "logicalNodes": 119, + "dataSets": 2, + "fcda": 58, + "logicalReportControls": 32, + "expandedTopLevelDataObjects": 860, + "expandedDataObjectsIncludingSdo": 906, + "expandedScalarLeaves": 4925 + } + }, + "discoveryAcceptance": { + "exactlyOneAssociation": true, + "supplementalLegacyAssociationForbidden": true, + "recursivePerLeafGvaStormForbidden": true, + "secondFullGetNameListSweepForbidden": true, + "eagerInitialFcValueReadDuringStructuralDiscovery": false, + "typeStrategy": "LN/root structure first, bounded leaf fallback only when exact structure evidence is unavailable.", + "physicalPerformanceGate": "Keep one association and remain within the locked request, GVA and Read ceilings while preserving or improving TTFI and total discovery time on the physical acceptance device." + }, + "modelAcceptance": { + "prefixedLogicalNodeIdentityExamples": [ + "RPRE_MMXU1 => prefix=RPRE_, lnClass=MMXU, lnInst=1", + "FPRE_MMXN1 => prefix=FPRE_, lnClass=MMXN, lnInst=1", + "I01ATCTR1 => prefix=I01A, lnClass=TCTR, lnInst=1" + ], + "standardSdoProjection": [ + "WYE", + "DEL", + "SEQ" + ], + "descendantFunctionalConstraintOwnership": true, + "standardRegistryAuthority": [ + "TCTR", + "TVTR", + "LTIM", + "EEName", + "MltLev" + ], + "edition2ServiceTrackingCdc": [ + "CST", + "BTS", + "UTS", + "STS", + "CTS" + ], + "edition1TrackingDowngrade": "omit with explicit warning; never emit invalid Edition 1 SCL", + "mmsTypeDeclarationOrder": "LN-root TypeSpecification declaration order is authoritative through model, SCL and FC-root projection.", + "settingFunctionalConstraints": "SG/SE are setting data; only actual SGCB produces SettingControl.", + "mhaiThdProjection": "MHAI ThdA/ThdPhV are WYE with CMV phase SDOs." + }, + "sclAcceptance": { + "profile": "full-model", + "reopenInArsasRequired": true, + "exactAssociationRebuildRequired": true, + "nativeCallingIdentityRequired": true, + "initialTrustedSclReads": "safe bounded SCL-guided reads after trusted-SCL reconnect; multi-DO CF groups use DO-scoped structured Reads because SCL LNodeType order is not authoritative for MMS FC-root DO order.", + "rcbProjection": "34 runtime RCB -> 32 logical ReportControl; ConfReportControl max=34", + "zeroSilentModelDeletion": true, + "physicalReconnectRequired": true, + "reuseTransportAndReportingLock": "Both Edition 2 and Edition 1 must match 32/32 MMS domains, complete all planned safe FC-root Reads without transport failures, preserve both static DataSets, arm exactly the configured Analog/Digital report plans, resolve all 58 runtime points, and observe actual InformationReport traffic.", + "semanticProjectionTarget": "projectionErrors=0 and projected leaf cache has no case-collapse loss." + }, + "promotion": { + "productionPromoted": false, + "mergeAllowedBeforePhysicalRetest": false, + "physicalRetestRequired": false, + "requiredEvidence": [ + "new ARSAS PCAP", + "new generated Ed2 IID", + "new generated Ed1 ICD", + "diagnostic report", + "Ed2 SCL-assisted reuse diagnostic with projectionErrorSamples", + "Ed1 SCL-assisted reuse diagnostic with projectionErrorSamples" + ], + "mergeAllowedAfterPhysicalRetest": true, + "physicalRetestPassed": true, + "physicalRetestDate": "2026-09-19", + "note": "R10 physical discovery/save/reopen evidence passed for the exact ARSAS/engine source tree. Merge is allowed; production promotion remains a separate release decision." + }, + "legacyPolicy": { + "rule": "Historical PRs may remain as provenance only; they must not be used as new branch bases after an active convergence authority supersedes them.", + "knownInvalidAuthority": [ + "PR #125 calling identity profile" + ], + "activeAuthorityOnly": [ + "ARIEC61850 #134", + "ARIEC61850 #135", + "ARSAS #324" + ] + }, + "physicalEvidence": { + "arsasR9": { + "relay": "AA1E1F06R4", + "testedArtifact": { + "appHead": "a89d6ef230951fde98f2b35e38dbc2dc84b6382f", + "engineHead": "3e12fb9d84ae27ddbebb2389ed2eb58d0fde6d90" + }, + "discovery": { + "associations": 1, + "confirmedMmsRequests": 323, + "getNameList": 138, + "getVariableAccessAttributes": 119, + "getNamedVariableListAttributes": 2, + "reads": 64, + "topLevelDataObjects": 860, + "dataObjectsIncludingSdo": 906, + "scalarLeaves": 4925, + "dataSets": 2, + "fcda": 58, + "logicalReportControls": 32, + "settingControls": 1 + }, + "reuseEdition2": { + "expectedDomains": 32, + "matchedDomains": 32, + "fcRoots": 563, + "successfulReads": 563, + "failedReads": 0, + "projectedLeaves": 4250, + "initialValueCache": 4239, + "projectionErrors": 46, + "staticDataSets": 2, + "staticReportControls": 32, + "reportBackedRuntimePoints": 58, + "unresolvedRuntimePoints": 0, + "actualInformationReportObserved": true + }, + "reuseEdition1": { + "expectedDomains": 32, + "matchedDomains": 32, + "fcRoots": 562, + "successfulReads": 562, + "failedReads": 0, + "projectedLeaves": 4055, + "initialValueCache": 4055, + "projectionErrors": 46, + "staticDataSets": 2, + "staticReportControls": 32, + "reportBackedRuntimePoints": 58, + "unresolvedRuntimePoints": 0, + "actualInformationReportObserved": true + } + }, + "arsasR10": { + "relay": "AA1E1F06R4", + "testedArtifact": { + "appHead": "eb8eb13d491f9aa265205852b8a4bab07af440ff", + "engineTestedHead": "9935d6902d786cc69b299260fe36b835944d5e81", + "engineMergedMain": "648124097621046f5f127ceb1cf853fea54db730", + "engineTree": "1cf7e08f333f24994625e8fe8416dbd0a16195b1" + }, + "discovery": { + "associations": 1, + "confirmedMmsRequests": 323, + "getNameList": 138, + "getVariableAccessAttributes": 119, + "getNamedVariableListAttributes": 2, + "reads": 64, + "topLevelDataObjects": 860, + "dataObjectsIncludingSdo": 906, + "scalarLeaves": 4925, + "dataSets": 2, + "fcda": 58, + "logicalReportControls": 32, + "settingControls": 1 + }, + "saveScl": { + "instanceEvidence": 3854, + "edition2ValCount": 3202, + "edition1ValCount": 3106, + "edition2RoundTrip": { + "logicalDevices": 32, + "logicalNodes": 119, + "dataSets": 2, + "logicalReportControls": 32 + }, + "edition1RoundTrip": { + "logicalDevices": 32, + "logicalNodes": 119, + "dataSets": 2, + "logicalReportControls": 32 + } + }, + "reuseEdition2": { + "expectedDomains": 32, + "observedDomains": 32, + "matchedDomains": 32, + "missingDomains": 0, + "extraDomains": 0, + "logicalDevices": 32, + "logicalNodes": 119, + "topLevelDataObjects": 860, + "dataAttributes": 5677, + "initialTargets": 709, + "fcRootTargets": 525, + "doScopedTargets": 184, + "successfulReads": 709, + "failedReads": 0, + "projectedLeaves": 4441, + "projectedUniqueValues": 4441, + "initialValueCache": 4441, + "cacheLoss": 0, + "projectionErrors": 0, + "staticDataSets": 2, + "staticMembers": 58, + "staticReportControls": 32, + "reportBackedRuntimePoints": 58, + "unresolvedRuntimePoints": 0, + "primaryUnresolvedAtSelection": 2, + "actualInformationReportObserved": true, + "cyclicMmsProcessPolling": 0 + }, + "reuseEdition1": { + "expectedDomains": 32, + "observedDomains": 32, + "matchedDomains": 32, + "missingDomains": 0, + "extraDomains": 0, + "logicalDevices": 32, + "logicalNodes": 119, + "topLevelDataObjects": 845, + "dataAttributes": 5470, + "initialTargets": 708, + "fcRootTargets": 524, + "doScopedTargets": 184, + "successfulReads": 708, + "failedReads": 0, + "projectedLeaves": 4246, + "projectedUniqueValues": 4246, + "initialValueCache": 4246, + "cacheLoss": 0, + "projectionErrors": 0, + "staticDataSets": 2, + "staticMembers": 58, + "staticReportControls": 32, + "reportBackedRuntimePoints": 58, + "unresolvedRuntimePoints": 0, + "primaryUnresolvedAtSelection": 2, + "actualInformationReportObserved": true, + "cyclicMmsProcessPolling": 0 + } + } + }, + "openGaps": { + "trustedSclProjectionParity": { + "targetProjectionErrors": 0, + "observedEdition2": 46, + "observedEdition1": 46, + "status": "resolved-r10-physical", + "rule": "Do not classify SCL reuse as semantically converged until projectionErrors reaches zero on both editions. Successful MMS Reads alone are insufficient.", + "rootCause": "R9 PCAP + Ed2 IID reproduction matched all 46 physical errors: every mismatch is in CF, across 18 FC roots / 191 affected SCL leaves, caused by cross-DO positional ordering differences between SCL LNodeType order and the MMS CF structure. No SCL count= arrays are present.", + "sourceRepair": "For multi-DO CF groups, InitialFcReadPlanner now emits exact LN$CF$DO targets. InitialFcValueProjector projects each DO value directly, eliminating cross-DO positional mapping while retaining batching.", + "r10ObservedEdition2": 0, + "r10ObservedEdition1": 0, + "note": "R9 historical 46/46 evidence is retained for provenance; R10 physically proves the repair on both editions." + }, + "edition2CaseDistinctInitialValueCache": { + "targetLoss": 0, + "observedProjectedMinusCached": 11, + "status": "resolved-r10-physical", + "rule": "LTRK t and T and any other case-distinct IEC 61850 paths must remain separate through TypeSpecification mapping, initial projection, ARSAS cache, canonical instance evidence, DAI/Val export and reopen. cacheLoss must be zero.", + "sourceRepair": "ARSAS trusted-SCL initial-value cache now uses StringComparer.Ordinal. Engine canonical value dedup, TypeSpecification member resolution, and canonical SCL instance-value targeting are exact-case. Diagnostics expose projectedUniqueValues and cacheLoss.", + "r10ObservedProjectedMinusCached": 0, + "r10Edition2ProjectedUniqueValues": 4441, + "r10Edition2InitialValueCache": 4441, + "note": "R9 historical loss=11 is retained for provenance; R10 physically proves exact-case cache preservation." + }, + "preallocatedAddReportControls": { + "observedWithoutDataSet": 30, + "status": "resolved-r10-export-and-reuse", + "rule": "Do not invent datSet. rptID-backed singleton runtime names ending 01 must project to indexed logical ReportControl max=1; an unassigned indexed slot is a warning, not a fatal missing-DataSet error.", + "r10FatalMissingDatasetErrors": 0, + "r10LogicalReportControls": 32, + "note": "Unassigned indexed ADD slots remain without invented datSet and no longer block reuse/reporting." + }, + "saveTimeInstanceValues": { + "observedR9ExportValCount": 0, + "status": "mechanism-physically-proven-semantic-diff-open", + "rule": "Keep structural discovery read-free; acquire bounded safe FC-root values only during explicit Save SCL and verify resulting instance evidence physically.", + "r10InstanceEvidence": 3854, + "r10Edition2ValCount": 3202, + "r10Edition1ValCount": 3106, + "note": "Save-time value enrichment is physically proven. Exact semantic path/value validation remains the acceptance criterion; raw Val count alone is not treated as a quality metric." + }, + "templateReuse": { + "status": "next-improvement", + "rule": "Reduce duplicate LNodeType/DOType/DAType templates only after wire behavior and semantic SCL reuse are stable.", + "r10Edition2": { + "lNodeType": 119, + "doType": 906, + "daType": 752, + "fileBytes": 731266 + }, + "acceptance": "Intern only structurally and semantically identical templates. Expanded 32/119/860/906/4925 model, FC ownership, values and round-trip behavior must remain unchanged." + }, + "saveEnrichmentReuse": { + "status": "next-optimization", + "observation": "Ed2 and Ed1 saves in one unchanged live session both produced instanceEvidence=3854; the bounded enrichment snapshot can be reused across edition serializers when association/model generation is unchanged.", + "rule": "Do not cache across reconnect, model-generation change, or explicit refresh. Optimization must not alter P0 discovery." + } + }, + "p0StructuralDiscoveryFreeze": { + "contractId": "P0-R9-STRUCTURAL", + "status": "implemented-and-r9-physically-proven", + "scope": "AA1E1F06R4 physical acceptance plus source/CI freeze. Counts are evidence and gates for this relay, not generic hardcoded runtime behavior for other IEDs.", + "sourcePolicy": { + "smartRouteRequired": true, + "associationScopedSingleFlightRequired": true, + "applicationMmsGateExclusive": true, + "logicalNodeRootTypeStrategyRequired": true, + "eagerInitialFcReadsForbidden": true, + "supplementalLegacyBrowseForbidden": true, + "secondFullGetNameListSweepForbidden": true, + "recursivePerLeafGvaStormForbidden": true, + "saveTimeAndTrustedSclValueReadsRemainSeparate": true + }, + "frozenOptions": { + "maxConcurrentChains": 8, + "unknownPeerMaxConcurrentChains": 4, + "maxDomains": 256, + "maxVariableNamesPerDomain": 20000, + "maxVariableListNamesPerDomain": 4096, + "maxNameListPages": 64, + "probeReportAttributes": true, + "maxReportAttributeProbes": 64, + "readDataSetDirectories": true, + "maxDataSetDirectoryReads": 64 + }, + "aa1e1f06r4Acceptance": { + "associations": 1, + "referenceConfirmedMmsRequests": 323, + "maximumConfirmedMmsRequests": 417, + "referenceGetNameList": 138, + "referenceGetVariableAccessAttributes": 119, + "maximumGetVariableAccessAttributes": 119, + "referenceGetNamedVariableListAttributes": 2, + "referenceReads": 64, + "maximumReads": 156, + "logicalDevices": 32, + "logicalNodes": 119, + "topLevelDataObjects": 860, + "dataObjectsIncludingSdo": 906, + "scalarLeaves": 4925, + "dataSets": 2, + "fcda": 58, + "logicalReportControls": 32, + "settingControls": 1 + }, + "rule": "Do not optimize or enrich structural discovery further while SCL semantic convergence is still open. Any required instance-value acquisition belongs to explicit Save SCL or trusted-SCL reconnect phases." + }, + "p1ProjectionOrderRepair": { + "contractId": "P1-CF-DO-SCOPED", + "status": "physically-proven-r10", + "preservesP0StructuralDiscovery": true, + "physicalR9Reproduction": { + "projectionErrors": 46, + "affectedFcRoots": 18, + "affectedSclLeaves": 191, + "functionalConstraint": "CF", + "sclArrayCountAttributes": 0 + }, + "rule": "Never use SCL LNodeType cross-DO order as authority for an MMS multi-DO CF structure. Read each CF DataObject by exact MMS object name and batch those references with the existing bounded Read executor.", + "expectedPhysicalOutcome": { + "projectionErrors": 0, + "noSilentCrossDoValueSwap": true, + "sameAssociation": true, + "noAdditionalDiscoveryGva": true + }, + "physicalR10Outcome": { + "edition2": { + "initialTargets": 709, + "doScopedTargets": 184, + "successfulReads": 709, + "failedReads": 0, + "projectionErrors": 0 + }, + "edition1": { + "initialTargets": 708, + "doScopedTargets": 184, + "successfulReads": 708, + "failedReads": 0, + "projectionErrors": 0 + }, + "reportBackedRuntimePoints": 58, + "unresolvedRuntimePoints": 0, + "actualInformationReportObserved": true + } + }, + "p2CaseSensitiveValuePipeline": { + "contractId": "P2-CASE-EXACT-VALUES", + "status": "physically-proven-r10", + "preservesP0StructuralDiscovery": true, + "preservesP1CfScopedHydration": true, + "physicalR9Evidence": { + "edition2ProjectedLeaves": 4250, + "edition2InitialValueCache": 4239, + "observedCacheLoss": 11, + "edition1ProjectedLeaves": 4055, + "edition1InitialValueCache": 4055, + "edition1ObservedCacheLoss": 0 + }, + "sourceInvariants": { + "trustedSclValueCacheComparer": "StringComparer.Ordinal", + "canonicalInstanceValueDedupComparer": "StringComparer.Ordinal", + "typeSpecificationMemberComparison": "StringComparison.Ordinal", + "sclDataObjectInstanceTargetComparison": "StringComparison.Ordinal", + "sclDaBdaSdoComponentComparison": "StringComparison.Ordinal", + "normalizationMayNotFoldCase": true + }, + "diagnosticContract": [ + "projectedUniqueValues", + "initialValueCache", + "cacheLoss" + ], + "expectedPhysicalOutcome": { + "edition2CacheLoss": 0, + "edition1CacheLoss": 0, + "ltrkLowercaseTAndUppercaseTRemainDistinct": true, + "noAdditionalDiscoveryGva": true, + "sameAssociation": true + }, + "physicalR10Outcome": { + "edition2": { + "projectedUniqueValues": 4441, + "initialValueCache": 4441, + "cacheLoss": 0 + }, + "edition1": { + "projectedUniqueValues": 4246, + "initialValueCache": 4246, + "cacheLoss": 0 + }, + "actualInformationReportObserved": true + } + } +} From db54cf31033ea25e7a343344d84c98fde51f5f69 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:11:30 +0700 Subject: [PATCH 02/80] docs: publish neutral R10 convergence contract --- docs/INTEROPERABILITY_CONVERGENCE.md | 206 +++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 docs/INTEROPERABILITY_CONVERGENCE.md diff --git a/docs/INTEROPERABILITY_CONVERGENCE.md b/docs/INTEROPERABILITY_CONVERGENCE.md new file mode 100644 index 000000000..86fe1c77f --- /dev/null +++ b/docs/INTEROPERABILITY_CONVERGENCE.md @@ -0,0 +1,206 @@ +# IEC 61850 Interoperability Convergence Contract + +## Current stable baseline + +ARSAS **1.6.38** is the current verified stable Windows release. + +Release source: + +- ARSAS release commit: `5e7ebec2779177c7f182a6a30b64218f84200a90` +- R10 post-merge baseline: `b93ef8fb615213eb81cdfda66d89c05a0d658839` +- ARIEC61850 merged authority: `648124097621046f5f127ceb1cf853fea54db730` +- ARIEC61850 physical-tested head: `9935d6902d786cc69b299260fe36b835944d5e81` +- Engine source-tree SHA: `1cf7e08f333f24994625e8fe8416dbd0a16195b1` + +The tested engine head and merged engine authority resolve to the same source tree. + +The machine-readable authority is +`evidence/interoperability-convergence-target.json`. + +## Acceptance model + +The accepted path keeps discovery, SCL generation, trusted-SCL reuse and reporting +as separate bounded phases: + +1. one accepted MMS association for structural discovery; +2. complete LD/LN/DO/SDO/DA/FC identity; +3. canonical Edition 2 IID or Edition 1 ICD generation; +4. reopen through the ARSAS SCL workspace; +5. reconnect using the trusted SCL model; +6. bounded initial value hydration; +7. static DataSet/report activation; +8. runtime value acquisition through report traffic without cyclic MMS process polling. + +## P0 — structural discovery freeze + +Contract: `P0-R9-STRUCTURAL`. + +The physical acceptance device is locked at: + +- 1 MMS association; +- 323 confirmed MMS requests on the accepted R10 path; +- 138 GetNameList requests; +- 119 GetVariableAccessAttributes requests; +- 2 GetNamedVariableListAttributes requests; +- 64 Reads; +- 32 logical devices; +- 119 logical nodes; +- 860 top-level data objects; +- 906 data objects including SDOs; +- 4925 scalar leaves; +- 2 DataSets / 58 FCDA; +- 32 logical ReportControls; +- 1 SettingControl. + +Regression ceilings remain bounded at 417 confirmed MMS requests, +119 GetVariableAccessAttributes requests and 156 Reads. + +Forbidden regressions include: + +- a second discovery association; +- supplemental legacy browsing; +- a second full GetNameList sweep; +- recursive per-leaf GVA expansion; +- eager FC-root value hydration on the structural discovery critical path. + +## P1 — exact DO-scoped CF projection + +Contract: `P1-CF-DO-SCOPED`. + +R9 exposed 46 projection errors caused by using SCL LNodeType DO order as if it +were MMS multi-DO CF child order. The accepted repair reads multi-DO CF data +through exact `LN$CF$DO` references and keeps batching bounded. + +R10 physical reuse proves the repair: + +### Edition 2 + +- initial targets: 709; +- FC-root targets: 525; +- DO-scoped targets: 184; +- successful Reads: 709/709; +- failed Reads: 0; +- `projectionErrors=0`. + +### Edition 1 + +- initial targets: 708; +- FC-root targets: 524; +- DO-scoped targets: 184; +- successful Reads: 708/708; +- failed Reads: 0; +- `projectionErrors=0`. + +## P2 — exact-case value identity + +Contract: `P2-CASE-EXACT-VALUES`. + +The accepted value pipeline preserves case-distinct IEC 61850 paths through +TypeSpecification mapping, trusted-SCL projection, cache storage, canonical +instance evidence and SCL instance-value targeting. + +R10 proves zero cache loss: + +- Edition 2: `projectedUniqueValues=4441`, + `initialValueCache=4441`, `cacheLoss=0`; +- Edition 1: `projectedUniqueValues=4246`, + `initialValueCache=4246`, `cacheLoss=0`. + +## R10 trusted-SCL round trip + +Both generated editions reopen and reconnect through the trusted-SCL path. + +Common acceptance: + +- expected/observed/matched MMS domains: 32/32/32; +- static DataSets: 2; +- static DataSet members: 58; +- missing static members: 0; +- report-backed runtime points: 58/58; +- final unresolved runtime points: 0; +- cyclic MMS process polling: 0; +- actual InformationReport traffic observed. + +The intermediate field `Primary unresolved=2` is not an operational loss. +Static DataSet identity resolves all 58 runtime points before report-backed +acquisition is accepted. + +## Save-time instance evidence + +Save-time value enrichment remains outside the structural discovery critical +path. + +R10 physical results: + +- canonical instance evidence: 3854 leaves; +- Edition 2 exported `Val`: 3202; +- Edition 1 exported `Val`: 3106. + +Raw `Val` count is not treated as a quality metric. Exact semantic +path/value correctness remains the acceptance criterion. + +## ReportControl identity + +The accepted representation remains: + +- 34 concrete runtime RCB objects; +- 32 logical SCL ReportControls; +- `Services/ConfReportControl max=34`; +- 36 digital + 22 analog static DataSet members; +- unassigned indexed slots remain unassigned and do not receive invented + DataSet references. + +## Release verification + +ARSAS 1.6.38 passed: + +- exact release-source restore/build/test; +- Windows portable publish and smoke test; +- Windows installer build and silent install/uninstall smoke test; +- R10 post-merge convergence verification; +- SHA-256 generation; +- SPDX 2.3 SBOM generation; +- app/engine provenance generation; +- artifact attestation; +- public GitHub Release publication. + +Public release identity and package hashes are synchronized in +`landing/latest.json`. + +## Next bounded improvements + +### Template interning + +Edition 2 is semantically correct but can reduce duplicate type templates. +Interning is allowed only when ordered semantic fingerprints are identical. + +The expanded model counts, FC ownership, values, DataSets, ReportControls and +round-trip behavior must remain unchanged. + +### Save-enrichment snapshot reuse + +A later optimization may reuse one bounded save-enrichment snapshot across +Edition 2 and Edition 1 serialization only while the association and model +generation are unchanged. + +The snapshot must be invalidated on reconnect, model-generation change or +explicit refresh. + +This optimization is not part of ARSAS 1.6.38. + +## Regression signatures that remain forbidden + +A build is rejected if it restores any of these patterns: + +- legacy discovery as the primary route; +- a second supplemental discovery association; +- recursive per-leaf GVA expansion; +- speculative high-volume Reads on the structural discovery path; +- cross-DO positional CF projection; +- case-insensitive IEC 61850 member/value identity; +- WYE/DEL/SEQ SDO flattening; +- invalid Edition 1 tracking CDC output; +- invented DataSet bindings for unassigned RCB slots; +- silent canonical-save success without reopen/association validation. + +The physical result, not test count alone, decides interoperability acceptance. From aa5faf4f9e4e6ecf698e29367978ef7a0b537599 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:11:47 +0700 Subject: [PATCH 03/80] ci: neutralize interoperability convergence guard --- .../interoperability-convergence-guard.yml | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 .github/workflows/interoperability-convergence-guard.yml diff --git a/.github/workflows/interoperability-convergence-guard.yml b/.github/workflows/interoperability-convergence-guard.yml new file mode 100644 index 000000000..01d86dfef --- /dev/null +++ b/.github/workflows/interoperability-convergence-guard.yml @@ -0,0 +1,327 @@ +name: Interoperability Convergence Guard + +on: + pull_request: + paths: + - "Services/NativeIec61850Client*.cs" + - "Services/SclAssistedConnectionPreparation.cs" + - "Services/CanonicalSclReloadValidator.cs" + - "MainWindow.xaml.cs" + - "Directory.Build.targets" + - "scripts/enable-smart-discovery-capture.ps1" + - "engines/ARIEC61850.lock.json" + - "evidence/interoperability-convergence-target.json" + - "docs/INTEROPERABILITY_CONVERGENCE.md" + - "tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs" + - ".github/workflows/interoperability-convergence-guard.yml" + - ".github/workflows/scl-interoperability-r7.yml" + workflow_dispatch: + +concurrency: + group: interoperability-convergence-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + interoperability-convergence-contract: + name: interoperability-convergence-contract + runs-on: windows-latest + + steps: + - name: Checkout exact ARSAS revision + shell: pwsh + env: + ARSAS_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + if ($env:ARSAS_SOURCE_SHA -notmatch '^[0-9a-f]{40}$') { + throw "Invalid ARSAS source SHA: $env:ARSAS_SOURCE_SHA" + } + git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:GITHUB_REPOSITORY.git" ARSAS + git -C .\ARSAS fetch --quiet --depth 1 origin $env:ARSAS_SOURCE_SHA + git -C .\ARSAS checkout --quiet --detach $env:ARSAS_SOURCE_SHA + $actual = (git -C .\ARSAS rev-parse HEAD).Trim() + if ($actual -ne $env:ARSAS_SOURCE_SHA) { + throw "ARSAS SHA mismatch. Expected $env:ARSAS_SOURCE_SHA, got $actual." + } + + - name: Verify single convergence authority + shell: pwsh + run: | + $lock = Get-Content .\ARSAS\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json + $target = Get-Content .\ARSAS\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json + + if ($target.status -ne 'physical-retest-passed-merge-ready') { + throw "Convergence status changed unexpectedly: $($target.status)" + } + if ([int]$target.activeStack.enginePerformance.pullRequest -ne 134 -or + [int]$target.activeStack.engineModelAndScl.pullRequest -ne 135 -or + [int]$target.activeStack.engineModelAndScl.basePullRequest -ne 134 -or + [int]$target.activeStack.consumerIntegration.pullRequest -ne 324) { + throw 'Merged engine authority must remain PR #134 + PR #135 with ARSAS #324 provenance.' + } + if ($lock.commit -ne $target.activeStack.engineModelAndScl.head -or + [int]$lock.sourcePullRequest -ne 135) { + throw 'Engine lock drifted from the convergence single source of truth.' + } + if ([bool]$target.promotion.productionPromoted -or + [bool]$target.promotion.mergeAllowedBeforePhysicalRetest -or + [bool]$target.promotion.physicalRetestRequired -or + -not [bool]$target.promotion.mergeAllowedAfterPhysicalRetest -or + -not [bool]$target.promotion.physicalRetestPassed) { + throw 'R10 physical-retest/merge gate drifted.' + } + if ([int]$target.physicalReference.acceptedDiscoveryBudget.associations -ne 1 -or + [int]$target.physicalReference.acceptedDiscoveryBudget.maximumConfirmedMmsRequests -ne 417 -or + [int]$target.physicalReference.acceptedDiscoveryBudget.maximumGetVariableAccessAttributes -ne 119 -or + [int]$target.physicalReference.acceptedDiscoveryBudget.maximumReads -ne 156 -or + [int]$target.physicalReference.rejectedLegacyArsasCapture.associations -ne 2 -or + [int]$target.physicalReference.rejectedLegacyArsasCapture.getVariableAccessAttributes -lt 20000) { + throw 'Physical interoperability regression budget changed unexpectedly.' + } + + + $p0 = $target.p0StructuralDiscoveryFreeze + if ($p0.contractId -ne 'P0-R9-STRUCTURAL' -or + $p0.status -ne 'implemented-and-r9-physically-proven' -or + -not [bool]$p0.sourcePolicy.smartRouteRequired -or + -not [bool]$p0.sourcePolicy.associationScopedSingleFlightRequired -or + -not [bool]$p0.sourcePolicy.applicationMmsGateExclusive -or + -not [bool]$p0.sourcePolicy.logicalNodeRootTypeStrategyRequired -or + -not [bool]$p0.sourcePolicy.eagerInitialFcReadsForbidden -or + -not [bool]$p0.sourcePolicy.supplementalLegacyBrowseForbidden -or + -not [bool]$p0.sourcePolicy.secondFullGetNameListSweepForbidden -or + -not [bool]$p0.sourcePolicy.recursivePerLeafGvaStormForbidden -or + -not [bool]$p0.sourcePolicy.saveTimeAndTrustedSclValueReadsRemainSeparate) { + throw 'P0 structural-discovery freeze policy was weakened.' + } + + if ([int]$p0.frozenOptions.maxConcurrentChains -ne 8 -or + [int]$p0.frozenOptions.unknownPeerMaxConcurrentChains -ne 4 -or + [int]$p0.frozenOptions.maxDomains -ne 256 -or + [int]$p0.frozenOptions.maxVariableNamesPerDomain -ne 20000 -or + [int]$p0.frozenOptions.maxVariableListNamesPerDomain -ne 4096 -or + [int]$p0.frozenOptions.maxNameListPages -ne 64 -or + -not [bool]$p0.frozenOptions.probeReportAttributes -or + [int]$p0.frozenOptions.maxReportAttributeProbes -ne 64 -or + -not [bool]$p0.frozenOptions.readDataSetDirectories -or + [int]$p0.frozenOptions.maxDataSetDirectoryReads -ne 64) { + throw 'P0 frozen smart-discovery options drifted.' + } + + $p0Relay = $p0.aa1e1f06r4Acceptance + if ([int]$p0Relay.associations -ne 1 -or + [int]$p0Relay.referenceConfirmedMmsRequests -ne 323 -or + [int]$p0Relay.maximumConfirmedMmsRequests -ne 417 -or + [int]$p0Relay.referenceGetNameList -ne 138 -or + [int]$p0Relay.referenceGetVariableAccessAttributes -ne 119 -or + [int]$p0Relay.maximumGetVariableAccessAttributes -ne 119 -or + [int]$p0Relay.referenceGetNamedVariableListAttributes -ne 2 -or + [int]$p0Relay.referenceReads -ne 64 -or + [int]$p0Relay.maximumReads -ne 156 -or + [int]$p0Relay.logicalDevices -ne 32 -or + [int]$p0Relay.logicalNodes -ne 119 -or + [int]$p0Relay.topLevelDataObjects -ne 860 -or + [int]$p0Relay.dataObjectsIncludingSdo -ne 906 -or + [int]$p0Relay.scalarLeaves -ne 4925 -or + [int]$p0Relay.dataSets -ne 2 -or + [int]$p0Relay.fcda -ne 58 -or + [int]$p0Relay.logicalReportControls -ne 32 -or + [int]$p0Relay.settingControls -ne 1) { + throw 'P0 AA1E1F06R4 discovery acceptance contract drifted.' + } + + $p1 = $target.p1ProjectionOrderRepair + if ($p1.contractId -ne 'P1-CF-DO-SCOPED' -or + $p1.status -ne 'physically-proven-r10' -or + [int]$p1.physicalR10Outcome.edition2.projectionErrors -ne 0 -or + [int]$p1.physicalR10Outcome.edition1.projectionErrors -ne 0 -or + [int]$p1.physicalR10Outcome.edition2.failedReads -ne 0 -or + [int]$p1.physicalR10Outcome.edition1.failedReads -ne 0 -or + [int]$p1.physicalR10Outcome.reportBackedRuntimePoints -ne 58 -or + [int]$p1.physicalR10Outcome.unresolvedRuntimePoints -ne 0 -or + -not [bool]$p1.physicalR10Outcome.actualInformationReportObserved) { + throw 'P1 physical projection-order proof regressed.' + } + + $p2 = $target.p2CaseSensitiveValuePipeline + if ($p2.contractId -ne 'P2-CASE-EXACT-VALUES' -or + $p2.status -ne 'physically-proven-r10' -or + -not [bool]$p2.preservesP0StructuralDiscovery -or + -not [bool]$p2.preservesP1CfScopedHydration -or + [int]$p2.physicalR9Evidence.observedCacheLoss -ne 11 -or + [int]$p2.expectedPhysicalOutcome.edition2CacheLoss -ne 0 -or + [int]$p2.expectedPhysicalOutcome.edition1CacheLoss -ne 0 -or + -not [bool]$p2.expectedPhysicalOutcome.ltrkLowercaseTAndUppercaseTRemainDistinct -or + -not [bool]$p2.expectedPhysicalOutcome.noAdditionalDiscoveryGva -or + -not [bool]$p2.expectedPhysicalOutcome.sameAssociation -or + [int]$p2.physicalR10Outcome.edition2.cacheLoss -ne 0 -or + [int]$p2.physicalR10Outcome.edition1.cacheLoss -ne 0 -or + [int]$p2.physicalR10Outcome.edition2.projectedUniqueValues -ne [int]$p2.physicalR10Outcome.edition2.initialValueCache -or + [int]$p2.physicalR10Outcome.edition1.projectedUniqueValues -ne [int]$p2.physicalR10Outcome.edition1.initialValueCache -or + $p2.sourceInvariants.trustedSclValueCacheComparer -ne 'StringComparer.Ordinal' -or + $p2.sourceInvariants.typeSpecificationMemberComparison -ne 'StringComparison.Ordinal' -or + $p2.sourceInvariants.sclDataObjectInstanceTargetComparison -ne 'StringComparison.Ordinal') { + throw 'P2 exact-case value pipeline contract was weakened.' + } + + $r10 = $target.physicalEvidence.arsasR10 + if ($r10.testedArtifact.appHead -ne 'eb8eb13d491f9aa265205852b8a4bab07af440ff' -or + $r10.testedArtifact.engineTestedHead -ne '9935d6902d786cc69b299260fe36b835944d5e81' -or + $r10.testedArtifact.engineMergedMain -ne '648124097621046f5f127ceb1cf853fea54db730' -or + [int]$r10.discovery.associations -ne 1 -or + [int]$r10.discovery.confirmedMmsRequests -ne 323 -or + [int]$r10.discovery.getVariableAccessAttributes -ne 119 -or + [int]$r10.reuseEdition2.matchedDomains -ne 32 -or + [int]$r10.reuseEdition2.initialTargets -ne 709 -or + [int]$r10.reuseEdition2.successfulReads -ne 709 -or + [int]$r10.reuseEdition2.failedReads -ne 0 -or + [int]$r10.reuseEdition2.projectionErrors -ne 0 -or + [int]$r10.reuseEdition2.cacheLoss -ne 0 -or + [int]$r10.reuseEdition2.reportBackedRuntimePoints -ne 58 -or + [int]$r10.reuseEdition2.unresolvedRuntimePoints -ne 0 -or + -not [bool]$r10.reuseEdition2.actualInformationReportObserved -or + [int]$r10.reuseEdition1.matchedDomains -ne 32 -or + [int]$r10.reuseEdition1.initialTargets -ne 708 -or + [int]$r10.reuseEdition1.successfulReads -ne 708 -or + [int]$r10.reuseEdition1.failedReads -ne 0 -or + [int]$r10.reuseEdition1.projectionErrors -ne 0 -or + [int]$r10.reuseEdition1.cacheLoss -ne 0 -or + [int]$r10.reuseEdition1.reportBackedRuntimePoints -ne 58 -or + [int]$r10.reuseEdition1.unresolvedRuntimePoints -ne 0 -or + -not [bool]$r10.reuseEdition1.actualInformationReportObserved) { + throw 'R10 physical SCL reuse acceptance evidence regressed.' + } + + $r9 = $target.physicalEvidence.arsasR9 + if ([int]$r9.discovery.associations -ne 1 -or + [int]$r9.discovery.confirmedMmsRequests -ne 323 -or + [int]$r9.discovery.getVariableAccessAttributes -ne 119 -or + [int]$r9.discovery.topLevelDataObjects -ne 860 -or + [int]$r9.discovery.dataObjectsIncludingSdo -ne 906 -or + [int]$r9.discovery.scalarLeaves -ne 4925) { + throw 'R9 physical discovery/model acceptance evidence drifted.' + } + + if ([int]$r9.reuseEdition2.expectedDomains -ne 32 -or + [int]$r9.reuseEdition2.matchedDomains -ne 32 -or + [int]$r9.reuseEdition2.successfulReads -ne [int]$r9.reuseEdition2.fcRoots -or + [int]$r9.reuseEdition2.failedReads -ne 0 -or + [int]$r9.reuseEdition2.reportBackedRuntimePoints -ne 58 -or + [int]$r9.reuseEdition2.unresolvedRuntimePoints -ne 0 -or + -not [bool]$r9.reuseEdition2.actualInformationReportObserved -or + [int]$r9.reuseEdition1.expectedDomains -ne 32 -or + [int]$r9.reuseEdition1.matchedDomains -ne 32 -or + [int]$r9.reuseEdition1.successfulReads -ne [int]$r9.reuseEdition1.fcRoots -or + [int]$r9.reuseEdition1.failedReads -ne 0 -or + [int]$r9.reuseEdition1.reportBackedRuntimePoints -ne 58 -or + [int]$r9.reuseEdition1.unresolvedRuntimePoints -ne 0 -or + -not [bool]$r9.reuseEdition1.actualInformationReportObserved) { + throw 'R9 trusted-SCL reconnect/reporting acceptance evidence regressed.' + } + + if ([int]$target.openGaps.trustedSclProjectionParity.targetProjectionErrors -ne 0 -or + [int]$target.openGaps.trustedSclProjectionParity.observedEdition2 -ne [int]$r9.reuseEdition2.projectionErrors -or + [int]$target.openGaps.trustedSclProjectionParity.observedEdition1 -ne [int]$r9.reuseEdition1.projectionErrors -or + [int]$target.openGaps.trustedSclProjectionParity.r10ObservedEdition2 -ne 0 -or + [int]$target.openGaps.trustedSclProjectionParity.r10ObservedEdition1 -ne 0 -or + [int]$target.openGaps.edition2CaseDistinctInitialValueCache.observedProjectedMinusCached -ne 11 -or + [int]$target.openGaps.edition2CaseDistinctInitialValueCache.r10ObservedProjectedMinusCached -ne 0 -or + [int]$target.openGaps.preallocatedAddReportControls.observedWithoutDataSet -ne 30 -or + [int]$target.openGaps.preallocatedAddReportControls.r10FatalMissingDatasetErrors -ne 0) { + throw 'R9 history or R10 resolved semantic evidence drifted.' + } + + "ENGINE_REPOSITORY=$($lock.repository)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append + "ENGINE_COMMIT=$($lock.commit)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Checkout exact convergence engine + shell: pwsh + run: | + git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:ENGINE_REPOSITORY.git" ARIEC61850 + git -C .\ARIEC61850 fetch --quiet --depth 1 origin $env:ENGINE_COMMIT + git -C .\ARIEC61850 checkout --quiet --detach $env:ENGINE_COMMIT + $actual = (git -C .\ARIEC61850 rev-parse HEAD).Trim() + if ($actual -ne $env:ENGINE_COMMIT) { + throw "Engine SHA mismatch. Expected $env:ENGINE_COMMIT, got $actual." + } + + - name: Verify discovery and SCL anti-regression contracts + shell: pwsh + run: | + $capture = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryCapture.cs -Raw + $sclAssisted = Get-Content .\ARSAS\Services\NativeIec61850Client.SclAssisted.cs -Raw + $canonicalModel = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\LiveIedCanonicalModel.cs -Raw + $typeHierarchy = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\LiveIedVariableTypeHierarchy.cs -Raw + $canonicalExporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\CanonicalLiveIedSclExporter.cs -Raw + $targets = Get-Content .\ARSAS\Directory.Build.targets -Raw + $smart = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscovery.cs -Raw + $singleFlight = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscoverySingleFlight.cs -Raw + $smartGva = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartVariableAccessAttributes.cs -Raw + $lnParts = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850ReferenceParts.cs -Raw + $exporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs -Raw + $registry = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850StandardModelRegistry.cs -Raw + $cdc = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\CdcInferenceEngine.cs -Raw + + $valueCacheIndex = $sclAssisted.IndexOf('_trustedSclInitialValues =', [System.StringComparison]::Ordinal) + if ($valueCacheIndex -lt 0) { + throw 'P2 trusted-SCL value cache declaration is missing.' + } + $valueCacheLength = [Math]::Min(260, $sclAssisted.Length - $valueCacheIndex) + $valueCacheSegment = $sclAssisted.Substring($valueCacheIndex, $valueCacheLength) + if ($valueCacheSegment -notmatch 'StringComparer\.Ordinal' -or + $valueCacheSegment -match 'StringComparer\.OrdinalIgnoreCase' -or + $sclAssisted -notmatch 'projectedUniqueValues' -or + $sclAssisted -notmatch 'initialValueCacheLoss' -or + $sclAssisted -notmatch 'cacheLoss=\{initialValueCacheLoss\}' -or + $canonicalModel -notmatch 'new HashSet\(StringComparer\.Ordinal\)' -or + $typeHierarchy -notmatch 'current\.Children\[index\]\.Name, part, StringComparison\.Ordinal\)' -or + $canonicalExporter -notmatch 'dataObjectName\.Trim\(\),\s*[\s\S]*?StringComparison\.Ordinal\)') { + throw 'P2 case-sensitive value path implementation regressed.' + } + + if ($targets -notmatch 'SCL Interoperability R7 Build' -or + $targets -notmatch 'EnableSmartDiscoveryCaptureRoute' -or + $capture -notmatch 'SmartDiscoveryStructuralFreezeContract = "P0-R9-STRUCTURAL"' -or + $capture -notmatch 'CreateP0FrozenSmartDiscoveryOptions' -or + $capture -notmatch 'MaxConcurrentChains = 8' -or + $capture -notmatch 'UnknownPeerMaxConcurrentChains = 4' -or + $capture -notmatch 'MaxNameListPages = 64' -or + $capture -notmatch 'ProbeReportAttributes = true' -or + $capture -notmatch 'MaxReportAttributeProbes = 64' -or + $capture -notmatch 'ReadDataSetDirectories = true' -or + $capture -notmatch 'MaxDataSetDirectoryReads = 64' -or + $capture -notmatch 'GetOrCreateSmartDiscoveryAssociationFlight' -or + $capture -notmatch 'DiscoverSmartSingleFlightAsync' -or + $capture -notmatch 'ProbeSmartAsync' -or + $capture -notmatch 'initialFcRoots=deferred' -or + $capture -notmatch 'InitialFcReadExecutionResult\? initialRead = null' -or + $capture -match '_session\.DiscoverAsync\(' -or + $capture -match 'DiscoverDomainVariableNamesAsync' -or + $capture -match 'TryBuildSupplementalGetNameListSnapshotAsync' -or + $capture -match 'DiscoverDomainVariableTypeTreeNamesAsync' -or + $capture -match 'AddAdaptiveLogicalNodeSiblingProbeSignalsAsync' -or + $capture -match 'EnrichEngineeringUnitsAsync' -or + $capture -match 'InitialFcReadPlanner\.FromSclModel' -or + $capture -match 'ExecuteInitialFcReadPlanSmartAsync' -or + $smart -notmatch 'DiscoverSmartAsync' -or + $singleFlight -notmatch 'DiscoverSmartSingleFlightAsync' -or + $singleFlight -notmatch 'DiscoverSmartAsync\(options, CancellationToken\.None\)' -or + $smartGva -notmatch 'GetVariableAccessAttributesSmartAsync') { + throw 'P0 structural discovery freeze regressed away from the accepted interoperability convergence path.' + } + + if ($lnParts -notmatch 'instanceStart' -or + $lnParts -notmatch 'IsFourLetterLnClass' -or + $exporter -notmatch 'TryResolveStandardSubDataObjectCdc' -or + $exporter -notmatch 'DataObjectReferencePaths' -or + $exporter -notmatch 'IsEdition2ServiceTrackingCdc' -or + $registry -notmatch 'Key\("TCTR", "ARtg"\)' -or + $registry -notmatch 'Key\("TVTR", "VRtg"\)' -or + $registry -notmatch 'Key\("LTIM", "TmChgDT"\)' -or + $registry -notmatch 'Key\("LTRK", "BrcbTrk"\)' -or + $registry -notmatch 'Key\("XCBR", "EEName"\)' -or + $registry -notmatch 'Key\("LLN0", "MltLev"\)' -or + $cdc -notmatch '"CST".*"BTS".*"UTS".*"STS".*"CTS"') { + throw 'Canonical IEC model / SCL semantic authority regressed.' + } + + Write-Host 'Interoperability convergence contract PASS.' From eafd26fe89cefe8e0bf18ede90dfe732224626a8 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:11:58 +0700 Subject: [PATCH 04/80] ci: use neutral convergence authority --- .github/workflows/scl-interoperability-r7.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/scl-interoperability-r7.yml b/.github/workflows/scl-interoperability-r7.yml index 17a436da3..ae92b3c0c 100644 --- a/.github/workflows/scl-interoperability-r7.yml +++ b/.github/workflows/scl-interoperability-r7.yml @@ -14,8 +14,8 @@ on: - "MainWindow.xaml.cs" - "Services/CanonicalSclReloadValidator.cs" - "engines/ARIEC61850.lock.json" - - "evidence/iedscout-convergence-target.json" - - "docs/IEDSCOUT_CONVERGENCE.md" + - "evidence/interoperability-convergence-target.json" + - "docs/INTEROPERABILITY_CONVERGENCE.md" - "tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs" - "tests/ARSAS.Tests/CanonicalSclReloadValidatorTests.cs" - ".github/workflows/scl-interoperability-r7.yml" @@ -52,7 +52,7 @@ jobs: shell: pwsh run: | $lock = Get-Content .\ARSAS\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json - $convergence = Get-Content .\ARSAS\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json + $convergence = Get-Content .\ARSAS\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json if ($lock.repository -ne 'masarray/ARIEC61850' -or $lock.commit -notmatch '^[0-9a-f]{40}$') { throw 'Invalid ARIEC61850 R7 lock.' @@ -71,7 +71,7 @@ jobs: [bool]$convergence.promotion.physicalRetestRequired -or -not [bool]$convergence.promotion.mergeAllowedAfterPhysicalRetest -or -not [bool]$convergence.promotion.physicalRetestPassed) { - throw 'IEDScout convergence single-source-of-truth does not match the physically proven merged-engine + ARSAS #324 authority.' + throw 'Interoperability convergence authority does not match the physically proven merged-engine + ARSAS #324 authority.' } if ([int]$lock.sourcePullRequest -notin @(134, 135)) { throw "Unexpected R7 engine source PR: $($lock.sourcePullRequest)" @@ -111,7 +111,7 @@ jobs: $cdcInference = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\CdcInferenceEngine.cs -Raw $consumer = Get-Content .\ARSAS\Services\NativeIec61850Client.CanonicalModel.cs -Raw $capture = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryCapture.cs -Raw - $convergence = Get-Content .\ARSAS\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json + $convergence = Get-Content .\ARSAS\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json $buildTargets = Get-Content .\ARSAS\Directory.Build.targets -Raw $lifecycle = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryLifecycle.cs -Raw $sclClient = Get-Content .\ARSAS\Services\NativeIec61850Client.SclAssisted.cs -Raw @@ -175,16 +175,16 @@ jobs: $capture -match 'DiscoverDomainVariableTypeTreeNamesAsync' -or $capture -match 'AddAdaptiveLogicalNodeSiblingProbeSignalsAsync' -or $capture -match 'EnrichEngineeringUnitsAsync') { - throw 'IEDScout convergence source contract regressed: smart structure-first discovery or semantic SCL authority is missing, or a forbidden legacy browse path returned.' + throw 'Interoperability convergence source contract regressed: smart structure-first discovery or semantic SCL authority is missing, or a forbidden legacy browse path returned.' } - if ([int]$convergence.physicalReference.iedScoutCapture.associations -ne 1 -or + if ([int]$convergence.physicalReference.acceptedDiscoveryBudget.associations -ne 1 -or [int]$convergence.physicalReference.rejectedLegacyArsasCapture.associations -ne 2 -or -not [bool]$convergence.discoveryAcceptance.exactlyOneAssociation -or -not [bool]$convergence.discoveryAcceptance.supplementalLegacyAssociationForbidden -or -not [bool]$convergence.sclAcceptance.reopenInArsasRequired -or -not [bool]$convergence.sclAcceptance.physicalReconnectRequired) { - throw 'IEDScout physical acceptance target was weakened.' + throw 'Interoperability physical acceptance target was weakened.' } if ($consumer -notmatch 'LiveIedCanonicalModelBuilder\.Build\(model, communication, initialRead\)' -or @@ -276,7 +276,7 @@ jobs: throw "Portable EXE smoke test failed: $($process.ExitCode)" } - $convergencePath = ".\ARSAS\evidence\iedscout-convergence-target.json" + $convergencePath = ".\ARSAS\evidence\interoperability-convergence-target.json" $convergenceHash = (Get-FileHash $convergencePath -Algorithm SHA256).Hash.ToLowerInvariant() $convergence = Get-Content $convergencePath -Raw | ConvertFrom-Json @@ -304,8 +304,8 @@ jobs: path: | ARSAS\dist\ARSAS-*-win-x64-portable.exe ARSAS\dist\R7-SCL-INTEROP-BUILD.txt - ARSAS\evidence\iedscout-convergence-target.json - ARSAS\docs\IEDSCOUT_CONVERGENCE.md + ARSAS\evidence\interoperability-convergence-target.json + ARSAS\docs\INTEROPERABILITY_CONVERGENCE.md ARSAS\TestResults\*.trx if-no-files-found: error retention-days: 14 From 521588fffe9d5bd8632a066a6012fa5529dfd6b6 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:12:17 +0700 Subject: [PATCH 05/80] ci: point R10 gates at neutral evidence --- .github/workflows/smart-discovery-mainline-readiness.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smart-discovery-mainline-readiness.yml b/.github/workflows/smart-discovery-mainline-readiness.yml index f0b4f05a4..687e9b672 100644 --- a/.github/workflows/smart-discovery-mainline-readiness.yml +++ b/.github/workflows/smart-discovery-mainline-readiness.yml @@ -17,7 +17,7 @@ jobs: - name: Verify R10 physical convergence authority shell: powershell run: | - $target = Get-Content .\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json + $target = Get-Content .\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json $lock = Get-Content .\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json if ($target.status -ne 'physical-retest-passed-merge-ready' -or From 36085f3919a22e7319126498acb95fc67bb161c4 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:12:20 +0700 Subject: [PATCH 06/80] ci: point R10 gates at neutral evidence --- .github/workflows/smart-discovery-post-merge-production.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/smart-discovery-post-merge-production.yml b/.github/workflows/smart-discovery-post-merge-production.yml index b74229e10..e1836ea0e 100644 --- a/.github/workflows/smart-discovery-post-merge-production.yml +++ b/.github/workflows/smart-discovery-post-merge-production.yml @@ -19,7 +19,7 @@ jobs: - name: Verify R10 physical convergence authority shell: powershell run: | - $target = Get-Content .\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json + $target = Get-Content .\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json $lock = Get-Content .\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json if ($target.status -ne 'physical-retest-passed-merge-ready' -or @@ -86,7 +86,7 @@ jobs: shell: powershell run: | New-Item -ItemType Directory -Force .\TestResults | Out-Null - $target = Get-Content .\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json + $target = Get-Content .\evidence\interoperability-convergence-target.json -Raw | ConvertFrom-Json [ordered]@{ schemaVersion = 1 sourceCommit = $env:GITHUB_SHA From d47b5b4b1b5a59538b33b0ee78b3a7fe96d1de57 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:12:44 +0700 Subject: [PATCH 07/80] ci: enforce neutral public source tree --- scripts/verify-source-clean.ps1 | 46 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/scripts/verify-source-clean.ps1 b/scripts/verify-source-clean.ps1 index 3f1782a88..d985c3682 100644 --- a/scripts/verify-source-clean.ps1 +++ b/scripts/verify-source-clean.ps1 @@ -24,6 +24,32 @@ $ForbiddenFilePatterns = @( "*.nupkg", "*.snupkg", "*.pcap", "*.pcapng", "*.etl", "*.binlog", "*.log", "*.tmp", "*.cache", "*.suo", "*.user", "*.rsuser", "*.pdf", "*.chm", "*.hlp" + "30e363d3e8c59f2c1319f8d73d48e3ad26db5e087951a4d7ab809c6f5401aea8", + "43d7a9de7c6a018c3dfb8a0de38ae060237b942c300db67147e1b56a953b122c", + "630ba09448af522154f38ef7685ef1f44b0f3e9430f80829a03ce24f400f3754", + "e5812a10ae5b2cb03cd875c187e116bec38348c6687df3d7ae88736f8d9ad66a", + "c378ee41c2783d081fb4f6db9fb854a46caafe455d3b64143e2239ef2cecd045", + "8813d73ee94207a67d5ef2605eb6aebc9108ab61f311e4c76f988a0c24b4ea82", + "020dae383219bf785518e1af41f1a8f5ed14db6eeb5fa8219d806846cb1129f2", + "63bbfcfa5a6df7b03a5fd45c6526f66b13fc5a46494a1ed66e4ea70d436a222a", + "c5f975b35c72cfe2da72009c9866a7ce5eafadce57af395c9c1b6c94b383ed2c", + "0e357968efdabb541e3e79fac718a1d4fd05bed3b95ccde6995589316afb250c", + "fa9ab9821527495b9c58ac84f4c00c6a9f82c01c96bfa18809a72b9d91dff228", + "8fb642282c29a0c6bf16259283ae675b892cf995485215a6ae0d5f1cfa04d2d9", + "7f92427e2c816b6d22084fd490da8fed3423cc1b9f62407f41c75a0b9a5dfcd9", + "93161c7754014286c483f69f9c603e906f471b6e3b0d2a0ab950d3db239fdc41", + "7e175017f49e9d14104795cda400d40eb26bec6a0fd7af54dfe46c67b6352132", + "c8ee81d53def32998740908ca3f9b45b74eadb61bbf7f46a13f4d6935b5a096c", + "5c373ed1848509788e445369163f3db4bc550336840f5708079068c46ab1b2e3", + "32b97c36b6a25a163cb5c420d302c382d99bb277993f42afb8449c91f42084c1", + "b00fc048863503cada26b5a56f16d6720165f2e7ab3e0cac2c6769675b2a8a4b", + "76222e0a49d24865c3be20e5516a792fe820050da090b68ced1dc991b2bdf4f7", + "a41dd0408dfd76082a0ae17338117007b217e22d6c5428654284f3077f809336", + "ceaec657ec812681d45ba17bb58814b410d4212afa9ba97cefc67179656db96b", + "1de2ae12bdc9a7e1e269321868f7f655bd166f9c4183a47b88f6b637fa23e673", + "36e26473a4e8b5ce603a8cb3458b59c39d05fdb87fff30b18b65bad35ed9640e", + "95bad119f2292fb436837f2bfe73245627cd692a5e16e6f2e73b7e16b5ce7f6d", + ) $ForbiddenIdentifierHashes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) @@ -40,7 +66,7 @@ $ForbiddenIdentifierHashes = [System.Collections.Generic.HashSet[string]]::new([ ) | ForEach-Object { [void]$ForbiddenIdentifierHashes.Add($_) } $CandidateLengths = [System.Collections.Generic.HashSet[int]]::new() -@(7, 8, 12, 22) | ForEach-Object { [void]$CandidateLengths.Add($_) } +@(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 22) | ForEach-Object { [void]$CandidateLengths.Add($_) } $ForbiddenTextPatterns = @( "C:\Users\", @@ -56,19 +82,6 @@ $TextExtensions = @( ".props", ".targets", ".sln", ".slnx", ".txt" ) -# These are first-party convergence authorities. They intentionally contain the -# external interoperability label so the acceptance contract remains discoverable. -$ApprovedConvergenceIdentifierPaths = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) -@( - ".github/workflows/iedscout-convergence-guard.yml", - ".github/workflows/smart-discovery-post-merge-production.yml", - ".github/workflows/smart-discovery-mainline-readiness.yml", - ".github/workflows/scl-interoperability-r7.yml", - "docs/IEDSCOUT_CONVERGENCE.md", - "evidence/iedscout-convergence-target.json", - "tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs" -) | ForEach-Object { [void]$ApprovedConvergenceIdentifierPaths.Add($_) } - $Problems = New-Object System.Collections.Generic.List[string] function Normalize-RelativePath { @@ -138,8 +151,7 @@ foreach ($relative in (Get-TrackedRelativePaths)) { } } - $identifierScanExempt = $ApprovedConvergenceIdentifierPaths.Contains($relative) - if (-not $identifierScanExempt -and (Test-ContainsForbiddenIdentifier $relative)) { + if (Test-ContainsForbiddenIdentifier $relative) { $Problems.Add("Forbidden external identifier in path: $relative") } @@ -147,7 +159,7 @@ foreach ($relative in (Get-TrackedRelativePaths)) { if ($TextExtensions -notcontains [IO.Path]::GetExtension($relative).ToLowerInvariant()) { continue } $content = Get-Content -LiteralPath $fullPath -Raw -ErrorAction SilentlyContinue - if (-not $identifierScanExempt -and (Test-ContainsForbiddenIdentifier $content)) { + if (Test-ContainsForbiddenIdentifier $content) { $Problems.Add("Forbidden external identifier in text: $relative") } From ca4894e2c76d4187e21f54dc857579e69c31a02f Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:12:57 +0700 Subject: [PATCH 08/80] test: bind neutral convergence authority --- .../CanonicalLiveSclExportRegressionTests.cs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs b/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs index 67364653b..fee7768af 100644 --- a/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs +++ b/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs @@ -168,7 +168,7 @@ public void SclAssistedReconnect_UsesNativeCallingIdentityAndSafeParallelValueRe [Fact] public void P0StructuralDiscoveryFreeze_LocksR9WireAndModelBudget() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); Assert.Contains("\"contractId\": \"P0-R9-STRUCTURAL\"", contract, StringComparison.Ordinal); Assert.Contains("\"status\": \"implemented-and-r9-physically-proven\"", contract, StringComparison.Ordinal); @@ -195,7 +195,7 @@ public void P0StructuralDiscoveryFreeze_LocksR9WireAndModelBudget() [Fact] public void P1ProjectionOrderRepair_LocksPhysicalRootCauseAndExactStrategy() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); Assert.Contains("\"contractId\": \"P1-CF-DO-SCOPED\"", contract, StringComparison.Ordinal); Assert.Contains("\"status\": \"physically-proven-r10\"", contract, StringComparison.Ordinal); @@ -213,7 +213,7 @@ public void P1ProjectionOrderRepair_LocksPhysicalRootCauseAndExactStrategy() [Fact] public void P2CaseSensitiveValuePipeline_LocksLosslessExactPathIdentity() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); var client = File.ReadAllText(FindRepoFile("Services/NativeIec61850Client.SclAssisted.cs")); Assert.Contains("\"contractId\": \"P2-CASE-EXACT-VALUES\"", contract, StringComparison.Ordinal); @@ -239,7 +239,7 @@ public void P2CaseSensitiveValuePipeline_LocksLosslessExactPathIdentity() [Fact] public void R9PhysicalReuse_LocksWorkingPathAndKeepsSemanticProjectionGapOpen() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); Assert.Contains("\"confirmedMmsRequests\": 323", contract, StringComparison.Ordinal); Assert.Contains("\"getVariableAccessAttributes\": 119", contract, StringComparison.Ordinal); @@ -271,7 +271,7 @@ public void R9PhysicalReuse_LocksWorkingPathAndKeepsSemanticProjectionGapOpen() [Fact] public void R10PhysicalReuse_ClosesP1P2AndLocksReportBackedRoundTrip() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); Assert.Contains("\"arsasR10\"", contract, StringComparison.Ordinal); Assert.Contains("\"initialTargets\": 709", contract, StringComparison.Ordinal); @@ -343,18 +343,18 @@ public void R7Workflow_BindsArtifactToExactSourceHeadAndReloadContracts() var buildTargets = File.ReadAllText(FindRepoFile("Directory.Build.targets")); Assert.Contains("SCL Interoperability R7 Build", buildTargets, StringComparison.Ordinal); Assert.Contains("EnableSmartDiscoveryCaptureRoute", buildTargets, StringComparison.Ordinal); - Assert.Contains("evidence/iedscout-convergence-target.json", workflow, StringComparison.Ordinal); - Assert.Contains("IEDScout convergence source contract regressed", workflow, StringComparison.Ordinal); + Assert.Contains("evidence/interoperability-convergence-target.json", workflow, StringComparison.Ordinal); + Assert.Contains("Interoperability convergence source contract regressed", workflow, StringComparison.Ordinal); Assert.Contains("TryResolveStandardSubDataObjectCdc", workflow, StringComparison.Ordinal); Assert.Contains("IsEdition2ServiceTrackingCdc", workflow, StringComparison.Ordinal); Assert.Contains("TryBuildSupplementalGetNameListSnapshotAsync", workflow, StringComparison.Ordinal); } [Fact] - public void IedScoutConvergenceContract_PhysicalRetestPassedAndMergeReady() + public void InteroperabilityConvergenceContract_PhysicalRetestPassedAndMergeReady() { - var contract = File.ReadAllText(FindRepoFile("evidence/iedscout-convergence-target.json")); - var documentation = File.ReadAllText(FindRepoFile("docs/IEDSCOUT_CONVERGENCE.md")); + var contract = File.ReadAllText(FindRepoFile("evidence/interoperability-convergence-target.json")); + var documentation = File.ReadAllText(FindRepoFile("docs/INTEROPERABILITY_CONVERGENCE.md")); Assert.Contains("\"status\": \"physical-retest-passed-merge-ready\"", contract, StringComparison.Ordinal); Assert.Contains("\"pullRequest\": 134", contract, StringComparison.Ordinal); @@ -374,24 +374,23 @@ public void IedScoutConvergenceContract_PhysicalRetestPassedAndMergeReady() Assert.Contains("Merged proven baseline", documentation, StringComparison.Ordinal); Assert.Contains("The field result, not test count alone", documentation, StringComparison.Ordinal); - var guard = File.ReadAllText(FindRepoFile(".github/workflows/iedscout-convergence-guard.yml")); - Assert.Contains("name: IEDScout Convergence Guard", guard, StringComparison.Ordinal); - Assert.Contains("name: iedscout-convergence-contract", guard, StringComparison.Ordinal); + var guard = File.ReadAllText(FindRepoFile(".github/workflows/interoperability-convergence-guard.yml")); + Assert.Contains("name: Interoperability Convergence Guard", guard, StringComparison.Ordinal); + Assert.Contains("name: interoperability-convergence-contract", guard, StringComparison.Ordinal); Assert.Contains("Merged engine authority must remain PR #134 + PR #135 with ARSAS #324 provenance", guard, StringComparison.Ordinal); - Assert.Contains("P0 structural discovery freeze regressed away from the accepted IEDScout convergence path", guard, StringComparison.Ordinal); + Assert.Contains("P0 structural discovery freeze regressed away from the accepted Interoperability convergence path", guard, StringComparison.Ordinal); Assert.Contains("Canonical IEC model / SCL semantic authority regressed", guard, StringComparison.Ordinal); } [Fact] - public void SourceClean_GuardsApprovedFirstPartyConvergenceAuthorities() + public void SourceClean_ScansConvergenceAuthoritiesWithoutIdentifierExemptions() { var source = File.ReadAllText(FindRepoFile("scripts/verify-source-clean.ps1")); - Assert.Contains("$ApprovedConvergenceIdentifierPaths", source, StringComparison.Ordinal); - Assert.Contains("docs/IEDSCOUT_CONVERGENCE.md", source, StringComparison.Ordinal); - Assert.Contains("evidence/iedscout-convergence-target.json", source, StringComparison.Ordinal); - Assert.Contains("CanonicalLiveSclExportRegressionTests.cs", source, StringComparison.Ordinal); - Assert.Contains("identifierScanExempt", source, StringComparison.Ordinal); + Assert.Contains("$ForbiddenIdentifierHashes", source, StringComparison.Ordinal); + Assert.Contains("Test-ContainsForbiddenIdentifier", source, StringComparison.Ordinal); + Assert.DoesNotContain("$ApprovedConvergenceIdentifierPaths", source, StringComparison.Ordinal); + Assert.DoesNotContain("identifierScanExempt", source, StringComparison.Ordinal); } From dd1ccc25055ca4e99ed582cfe7c6fbfd3fec2cff Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:13:10 +0700 Subject: [PATCH 09/80] test: rename RCB interoperability regression --- .../RcbInteroperabilityRegressionTests.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/ARSAS.Tests/RcbInteroperabilityRegressionTests.cs diff --git a/tests/ARSAS.Tests/RcbInteroperabilityRegressionTests.cs b/tests/ARSAS.Tests/RcbInteroperabilityRegressionTests.cs new file mode 100644 index 000000000..eda6dd303 --- /dev/null +++ b/tests/ARSAS.Tests/RcbInteroperabilityRegressionTests.cs @@ -0,0 +1,51 @@ +namespace ARSAS.Tests; + +public sealed class RcbInteroperabilityRegressionTests +{ + [Fact] + public void SourceBackedRuntimeInstances_Suppress_Logical_Scl_Duplicate() + { + var source = ReadRepoFile("MainWindow.RcbExport.cs"); + + Assert.Contains("logicalSourceReferences", source, StringComparison.Ordinal); + Assert.Contains("logicalSourceReferences.Contains(key)", source, StringComparison.Ordinal); + Assert.Contains("sourceBackedSelectionKeys", source, StringComparison.Ordinal); + } + + [Fact] + public void SourceBackedExport_Preserves_Canonical_Scl_ReportControl_Identity() + { + var source = ReadRepoFile("MainWindow.RcbExport.cs"); + + Assert.Contains("PreserveSourceReportControlIdentity = true", source, StringComparison.Ordinal); + Assert.Contains("new SclReportControlSelection(row.SourceSelectionKey, row.ExportName)", source, StringComparison.Ordinal); + } + + [Fact] + public void RcbStatus_Is_SquareOnly_YellowForClientUse_AndGreenOtherwise() + { + var model = ReadRepoFile("Models/RcbExportModels.cs"); + var xaml = ReadRepoFile("RcbExportFilterWindow.xaml"); + + Assert.Contains("Availability is MmsRcbOperationalAvailability.InUse or MmsRcbOperationalAvailability.UsedByCaller", model, StringComparison.Ordinal); + Assert.Contains("IsClientOccupied ? OccupiedIndicatorBrush : ReadyIndicatorBrush", model, StringComparison.Ordinal); + Assert.Contains("BrushFrom(234, 179, 8)", model, StringComparison.Ordinal); + Assert.Contains("BrushFrom(22, 163, 74)", model, StringComparison.Ordinal); + Assert.Contains(" Date: Sat, 19 Sep 2026 15:13:12 +0700 Subject: [PATCH 10/80] test: remove legacy RCB regression filename --- .../RcbIedScoutParityRegressionTests.cs | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 tests/ARSAS.Tests/RcbIedScoutParityRegressionTests.cs diff --git a/tests/ARSAS.Tests/RcbIedScoutParityRegressionTests.cs b/tests/ARSAS.Tests/RcbIedScoutParityRegressionTests.cs deleted file mode 100644 index 11e70c409..000000000 --- a/tests/ARSAS.Tests/RcbIedScoutParityRegressionTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace ARSAS.Tests; - -public sealed class RcbIedScoutParityRegressionTests -{ - [Fact] - public void SourceBackedRuntimeInstances_Suppress_Logical_Scl_Duplicate() - { - var source = ReadRepoFile("MainWindow.RcbExport.cs"); - - Assert.Contains("logicalSourceReferences", source, StringComparison.Ordinal); - Assert.Contains("logicalSourceReferences.Contains(key)", source, StringComparison.Ordinal); - Assert.Contains("sourceBackedSelectionKeys", source, StringComparison.Ordinal); - } - - [Fact] - public void SourceBackedExport_Preserves_Canonical_Scl_ReportControl_Identity() - { - var source = ReadRepoFile("MainWindow.RcbExport.cs"); - - Assert.Contains("PreserveSourceReportControlIdentity = true", source, StringComparison.Ordinal); - Assert.Contains("new SclReportControlSelection(row.SourceSelectionKey, row.ExportName)", source, StringComparison.Ordinal); - } - - [Fact] - public void RcbStatus_Is_SquareOnly_YellowForClientUse_AndGreenOtherwise() - { - var model = ReadRepoFile("Models/RcbExportModels.cs"); - var xaml = ReadRepoFile("RcbExportFilterWindow.xaml"); - - Assert.Contains("Availability is MmsRcbOperationalAvailability.InUse or MmsRcbOperationalAvailability.UsedByCaller", model, StringComparison.Ordinal); - Assert.Contains("IsClientOccupied ? OccupiedIndicatorBrush : ReadyIndicatorBrush", model, StringComparison.Ordinal); - Assert.Contains("BrushFrom(234, 179, 8)", model, StringComparison.Ordinal); - Assert.Contains("BrushFrom(22, 163, 74)", model, StringComparison.Ordinal); - Assert.Contains(" Date: Sat, 19 Sep 2026 15:13:22 +0700 Subject: [PATCH 11/80] test: generalize static dataset regression --- .../StaticDataSetSelectionRegressionTests.cs | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tests/ARSAS.Tests/StaticDataSetSelectionRegressionTests.cs diff --git a/tests/ARSAS.Tests/StaticDataSetSelectionRegressionTests.cs b/tests/ARSAS.Tests/StaticDataSetSelectionRegressionTests.cs new file mode 100644 index 000000000..bb5ed47d4 --- /dev/null +++ b/tests/ARSAS.Tests/StaticDataSetSelectionRegressionTests.cs @@ -0,0 +1,170 @@ +using System.Xml.Linq; +using AR.Iec61850.Scl.Engineering; +using AR.Iec61850.Scl.Workspace; +using ArIED61850Tester.Models; +using ArIED61850Tester.Services; + +namespace ARSAS.Tests; + +public sealed class StaticDataSetSelectionRegressionTests +{ + [Fact] + public void CrossLd_58Member_CrossLd_StaticDataSets_AreRepresented58Of58() + { + var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "cross_ld_58_member.cid"); + var signals = new List(); + + var merge = Iec61850DataSetSignalInventoryService.EnsureMandatorySignals(signals, model); + var snapshot = Iec61850DataSetCompletenessDiagnostic.Evaluate(model, signals); + + Assert.Equal(2, model.DataSets.Count); + Assert.Equal(58, model.DataSets.Sum(dataSet => dataSet.Members.Count)); + Assert.Equal(58, merge.MandatoryCatalogCount); + Assert.Equal(58, signals.Count); + Assert.Equal(58, snapshot.StaticMemberCount); + Assert.Equal(58, snapshot.MandatoryInventoryCount); + Assert.Equal(58, snapshot.RepresentedCount); + Assert.Equal(0, snapshot.MissingCount); + Assert.True(snapshot.IsComplete); + + Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4ADD/GGIO1.Dig01"); + Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4MEAS/MMXU1.Ana22"); + Assert.All(signals, signal => + { + Assert.DoesNotContain("[ST]", signal.DisplayReference, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain("[MX]", signal.DisplayReference, StringComparison.OrdinalIgnoreCase); + }); + } + + [Fact] + public void CrossLd_58Member_CrossLd_StaticDataSets_Survive_Real_SclMapper_Path() + { + var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "cross_ld_58_member.cid"); + var workspace = new SclIedWorkspace + { + IedName = "AA1C1F13R4", + AccessPointName = "E", + DesignModel = model + }; + + var signals = SclWorkspaceSignalMapper.BuildSignals(workspace); + var snapshot = Iec61850DataSetCompletenessDiagnostic.Evaluate(model, signals); + + Assert.Equal(58, snapshot.StaticMemberCount); + Assert.Equal(58, snapshot.MandatoryInventoryCount); + Assert.Equal(58, snapshot.RepresentedCount); + Assert.Equal(0, snapshot.MissingCount); + Assert.True(snapshot.IsComplete); + Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4ADD/GGIO1.Dig01"); + Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4MEAS/MMXU1.Ana22"); + } + + private static XDocument BuildFixture() + { + XNamespace ns = "http://www.iec.ch/61850/2003/SCL"; + + var digitalDataSet = new XElement(ns + "DataSet", new XAttribute("name", "Digital")); + foreach (var index in Enumerable.Range(1, 36)) + { + digitalDataSet.Add(new XElement(ns + "FCDA", + new XAttribute("ldInst", "ADD"), + new XAttribute("lnClass", "GGIO"), + new XAttribute("lnInst", "1"), + new XAttribute("doName", $"Dig{index:00}"), + new XAttribute("fc", "ST"))); + } + + var analogDataSet = new XElement(ns + "DataSet", new XAttribute("name", "Analog")); + foreach (var index in Enumerable.Range(1, 22)) + { + analogDataSet.Add(new XElement(ns + "FCDA", + new XAttribute("ldInst", "MEAS"), + new XAttribute("lnClass", "MMXU"), + new XAttribute("lnInst", "1"), + new XAttribute("doName", $"Ana{index:00}"), + new XAttribute("fc", "MX"))); + } + + var ggioType = new XElement(ns + "LNodeType", + new XAttribute("id", "GGIOType"), + new XAttribute("lnClass", "GGIO")); + foreach (var index in Enumerable.Range(1, 36)) + { + ggioType.Add(new XElement(ns + "DO", + new XAttribute("name", $"Dig{index:00}"), + new XAttribute("type", "SpsType"))); + } + + var mmxuType = new XElement(ns + "LNodeType", + new XAttribute("id", "MMXUType"), + new XAttribute("lnClass", "MMXU")); + foreach (var index in Enumerable.Range(1, 22)) + { + mmxuType.Add(new XElement(ns + "DO", + new XAttribute("name", $"Ana{index:00}"), + new XAttribute("type", "MvType"))); + } + + return new XDocument( + new XElement(ns + "SCL", + new XAttribute("version", "2007"), + new XAttribute("revision", "B"), + new XElement(ns + "Header", new XAttribute("id", "GENERIC_58")), + new XElement(ns + "IED", + new XAttribute("name", "AA1C1F13R4"), + new XElement(ns + "AccessPoint", + new XAttribute("name", "E"), + new XElement(ns + "Server", + new XElement(ns + "LDevice", + new XAttribute("inst", "Application"), + new XElement(ns + "LN0", + new XAttribute("lnClass", "LLN0"), + new XAttribute("lnType", "LLN0Type"), + analogDataSet, + digitalDataSet)), + new XElement(ns + "LDevice", + new XAttribute("inst", "ADD"), + new XElement(ns + "LN0", + new XAttribute("lnClass", "LLN0"), + new XAttribute("lnType", "LLN0Type")), + new XElement(ns + "LN", + new XAttribute("lnClass", "GGIO"), + new XAttribute("inst", "1"), + new XAttribute("lnType", "GGIOType"))), + new XElement(ns + "LDevice", + new XAttribute("inst", "MEAS"), + new XElement(ns + "LN0", + new XAttribute("lnClass", "LLN0"), + new XAttribute("lnType", "LLN0Type")), + new XElement(ns + "LN", + new XAttribute("lnClass", "MMXU"), + new XAttribute("inst", "1"), + new XAttribute("lnType", "MMXUType")))))), + new XElement(ns + "DataTypeTemplates", + new XElement(ns + "LNodeType", + new XAttribute("id", "LLN0Type"), + new XAttribute("lnClass", "LLN0")), + ggioType, + mmxuType, + new XElement(ns + "DOType", + new XAttribute("id", "SpsType"), + new XAttribute("cdc", "SPS"), + new XElement(ns + "DA", + new XAttribute("name", "stVal"), + new XAttribute("bType", "BOOLEAN"), + new XAttribute("fc", "ST"))), + new XElement(ns + "DOType", + new XAttribute("id", "MvType"), + new XAttribute("cdc", "MV"), + new XElement(ns + "DA", + new XAttribute("name", "mag"), + new XAttribute("bType", "Struct"), + new XAttribute("type", "AnalogueValue"), + new XAttribute("fc", "MX"))), + new XElement(ns + "DAType", + new XAttribute("id", "AnalogueValue"), + new XElement(ns + "BDA", + new XAttribute("name", "f"), + new XAttribute("bType", "FLOAT32")))))); + } +} From 8cee9e601357c1d6e5ae8956dd710c4ab3c590c0 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:13:25 +0700 Subject: [PATCH 12/80] test: remove device-specific regression filename --- ...nsStaticDataSetSelectionRegressionTests.cs | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 tests/ARSAS.Tests/SiemensStaticDataSetSelectionRegressionTests.cs diff --git a/tests/ARSAS.Tests/SiemensStaticDataSetSelectionRegressionTests.cs b/tests/ARSAS.Tests/SiemensStaticDataSetSelectionRegressionTests.cs deleted file mode 100644 index 77cd87307..000000000 --- a/tests/ARSAS.Tests/SiemensStaticDataSetSelectionRegressionTests.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Xml.Linq; -using AR.Iec61850.Scl.Engineering; -using AR.Iec61850.Scl.Workspace; -using ArIED61850Tester.Models; -using ArIED61850Tester.Services; - -namespace ARSAS.Tests; - -public sealed class SiemensStaticDataSetSelectionRegressionTests -{ - [Fact] - public void SiemensLike_58Member_CrossLd_StaticDataSets_AreRepresented58Of58() - { - var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "Siprotec_58_member.cid"); - var signals = new List(); - - var merge = Iec61850DataSetSignalInventoryService.EnsureMandatorySignals(signals, model); - var snapshot = Iec61850DataSetCompletenessDiagnostic.Evaluate(model, signals); - - Assert.Equal(2, model.DataSets.Count); - Assert.Equal(58, model.DataSets.Sum(dataSet => dataSet.Members.Count)); - Assert.Equal(58, merge.MandatoryCatalogCount); - Assert.Equal(58, signals.Count); - Assert.Equal(58, snapshot.StaticMemberCount); - Assert.Equal(58, snapshot.MandatoryInventoryCount); - Assert.Equal(58, snapshot.RepresentedCount); - Assert.Equal(0, snapshot.MissingCount); - Assert.True(snapshot.IsComplete); - - Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4ADD/GGIO1.Dig01"); - Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4MEAS/MMXU1.Ana22"); - Assert.All(signals, signal => - { - Assert.DoesNotContain("[ST]", signal.DisplayReference, StringComparison.OrdinalIgnoreCase); - Assert.DoesNotContain("[MX]", signal.DisplayReference, StringComparison.OrdinalIgnoreCase); - }); - } - - [Fact] - public void SiemensLike_58Member_CrossLd_StaticDataSets_Survive_Real_SclMapper_Path() - { - var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "Siprotec_58_member.cid"); - var workspace = new SclIedWorkspace - { - IedName = "AA1C1F13R4", - AccessPointName = "E", - DesignModel = model - }; - - var signals = SclWorkspaceSignalMapper.BuildSignals(workspace); - var snapshot = Iec61850DataSetCompletenessDiagnostic.Evaluate(model, signals); - - Assert.Equal(58, snapshot.StaticMemberCount); - Assert.Equal(58, snapshot.MandatoryInventoryCount); - Assert.Equal(58, snapshot.RepresentedCount); - Assert.Equal(0, snapshot.MissingCount); - Assert.True(snapshot.IsComplete); - Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4ADD/GGIO1.Dig01"); - Assert.Contains(signals, signal => signal.DisplayReference == "AA1C1F13R4MEAS/MMXU1.Ana22"); - } - - private static XDocument BuildFixture() - { - XNamespace ns = "http://www.iec.ch/61850/2003/SCL"; - - var digitalDataSet = new XElement(ns + "DataSet", new XAttribute("name", "Digital")); - foreach (var index in Enumerable.Range(1, 36)) - { - digitalDataSet.Add(new XElement(ns + "FCDA", - new XAttribute("ldInst", "ADD"), - new XAttribute("lnClass", "GGIO"), - new XAttribute("lnInst", "1"), - new XAttribute("doName", $"Dig{index:00}"), - new XAttribute("fc", "ST"))); - } - - var analogDataSet = new XElement(ns + "DataSet", new XAttribute("name", "Analog")); - foreach (var index in Enumerable.Range(1, 22)) - { - analogDataSet.Add(new XElement(ns + "FCDA", - new XAttribute("ldInst", "MEAS"), - new XAttribute("lnClass", "MMXU"), - new XAttribute("lnInst", "1"), - new XAttribute("doName", $"Ana{index:00}"), - new XAttribute("fc", "MX"))); - } - - var ggioType = new XElement(ns + "LNodeType", - new XAttribute("id", "GGIOType"), - new XAttribute("lnClass", "GGIO")); - foreach (var index in Enumerable.Range(1, 36)) - { - ggioType.Add(new XElement(ns + "DO", - new XAttribute("name", $"Dig{index:00}"), - new XAttribute("type", "SpsType"))); - } - - var mmxuType = new XElement(ns + "LNodeType", - new XAttribute("id", "MMXUType"), - new XAttribute("lnClass", "MMXU")); - foreach (var index in Enumerable.Range(1, 22)) - { - mmxuType.Add(new XElement(ns + "DO", - new XAttribute("name", $"Ana{index:00}"), - new XAttribute("type", "MvType"))); - } - - return new XDocument( - new XElement(ns + "SCL", - new XAttribute("version", "2007"), - new XAttribute("revision", "B"), - new XElement(ns + "Header", new XAttribute("id", "SIEMENS_58")), - new XElement(ns + "IED", - new XAttribute("name", "AA1C1F13R4"), - new XElement(ns + "AccessPoint", - new XAttribute("name", "E"), - new XElement(ns + "Server", - new XElement(ns + "LDevice", - new XAttribute("inst", "Application"), - new XElement(ns + "LN0", - new XAttribute("lnClass", "LLN0"), - new XAttribute("lnType", "LLN0Type"), - analogDataSet, - digitalDataSet)), - new XElement(ns + "LDevice", - new XAttribute("inst", "ADD"), - new XElement(ns + "LN0", - new XAttribute("lnClass", "LLN0"), - new XAttribute("lnType", "LLN0Type")), - new XElement(ns + "LN", - new XAttribute("lnClass", "GGIO"), - new XAttribute("inst", "1"), - new XAttribute("lnType", "GGIOType"))), - new XElement(ns + "LDevice", - new XAttribute("inst", "MEAS"), - new XElement(ns + "LN0", - new XAttribute("lnClass", "LLN0"), - new XAttribute("lnType", "LLN0Type")), - new XElement(ns + "LN", - new XAttribute("lnClass", "MMXU"), - new XAttribute("inst", "1"), - new XAttribute("lnType", "MMXUType")))))), - new XElement(ns + "DataTypeTemplates", - new XElement(ns + "LNodeType", - new XAttribute("id", "LLN0Type"), - new XAttribute("lnClass", "LLN0")), - ggioType, - mmxuType, - new XElement(ns + "DOType", - new XAttribute("id", "SpsType"), - new XAttribute("cdc", "SPS"), - new XElement(ns + "DA", - new XAttribute("name", "stVal"), - new XAttribute("bType", "BOOLEAN"), - new XAttribute("fc", "ST"))), - new XElement(ns + "DOType", - new XAttribute("id", "MvType"), - new XAttribute("cdc", "MV"), - new XElement(ns + "DA", - new XAttribute("name", "mag"), - new XAttribute("bType", "Struct"), - new XAttribute("type", "AnalogueValue"), - new XAttribute("fc", "MX"))), - new XElement(ns + "DAType", - new XAttribute("id", "AnalogueValue"), - new XElement(ns + "BDA", - new XAttribute("name", "f"), - new XAttribute("bType", "FLOAT32")))))); - } -} From 6367c8e78ea84972ac9a84e0499c7d17e69a5f86 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:13:54 +0700 Subject: [PATCH 13/80] docs: sync documentation hub to 1.6.38 --- docs/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index a7f294002..3386d3bbd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ This directory contains the engineering, validation, licensing, provenance, release, and operating-boundary documents for the ARSAS Windows IEC 61850 engineering workstation. -The current application version on `main` is **ARSAS 1.6.37**. Public binary identity remains tied to the latest actually published stable GitHub Release and its verified package evidence. +The current application version on `main` is **ARSAS 1.6.38**. Public binary identity remains tied to the latest actually published stable GitHub Release and its verified package evidence. ## Start here @@ -11,7 +11,8 @@ The current application version on `main` is **ARSAS 1.6.37**. Public binary ide | [Project README](../README.md) | Product overview, current-version summary, feature scope, quick start, build instructions, and public claim boundary. | | [IO List FAT Evidence Testing](IO_LIST_FAT_EVIDENCE.md) | Imported SDI test plans, OFF → ON → OFF evidence, Excel/PDF export, portable `.arsas` projects, integrity checks, and cross-laptop continuation. | | [Architecture](ARCHITECTURE.md) | Multi-IED ownership, model identity, report-first acquisition, runtime scaling, and timestamp semantics. | -| [SCL export](SCL_EXPORT.md) | Live-discovery and source-backed SCL export, including the 1.6.37 logical ReportControl versus runtime RCB-instance boundary. | +| [Interoperability convergence](INTEROPERABILITY_CONVERGENCE.md) | R10 physical discovery, trusted-SCL reuse, report-backed acquisition, exact-case value identity, and 1.6.38 release verification. | +| [SCL export](SCL_EXPORT.md) | Live-discovery and source-backed SCL export, including the 1.6.38 logical ReportControl versus runtime RCB-instance boundary. | | [COMTRADE viewer integration](COMTRADE_VIEWER_INTEGRATION.md) | Native ArdIrec bridge, in-process cursor/Phasor/Harmonics/Locus analysis, packaging, and presentation-only easing contract. | | [Windows releases](WINDOWS_RELEASES.md) | Installer and portable single-EXE packaging, exact release gates, checksums, SBOM, provenance, attestations, and publication boundary. | | [GOOSE Subscriber](GOOSE_SUBSCRIBER.md) | Read-only Npcap capture, ARIEC61850 GOOSE decoding, SCL/live-discovery DataSet binding, ordered `allData` leaf semantics, diagnostics, and field validation. | From b7441d8002ebeca5fd95311f508f2b1f35a7e13f Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:13:57 +0700 Subject: [PATCH 14/80] docs: sync SCL export contract to 1.6.38 --- docs/SCL_EXPORT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/SCL_EXPORT.md b/docs/SCL_EXPORT.md index 3596fbe12..0e7a7bc9e 100644 --- a/docs/SCL_EXPORT.md +++ b/docs/SCL_EXPORT.md @@ -14,13 +14,13 @@ Each ARSAS IED card exposes a **Save SCL** action after the application has a co The chosen profile controls root schema metadata, supported ReportControl fields, Services declarations, and default file extension. -## ReportControl identity in ARSAS 1.6.37 +## ReportControl identity in ARSAS 1.6.38 ARSAS separates the declarative engineering model from concrete runtime RCB instances. A source SCL file may describe one logical `ReportControl` together with `RptEnabled@max`. A connected IED can expose concrete client/runtime instances such as `Buffer01`, `Buffer02`, `Unbuffer01`, or `Unbuffer02`. Those online instances are valid live evidence, but they are not authority to duplicate or rename the logical source `ReportControl` in a source-backed IID/SCL export. -The v1.6.37 contract is therefore: +The v1.6.38 contract is therefore: - live RCB selection presents the concrete runtime instances actually exposed by the connected IED; - duplicate logical placeholder rows are not added beside those live instances; From e5d821b1c2208970de6c038e7d557835611148e3 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:14:01 +0700 Subject: [PATCH 15/80] docs: sync Windows release guide to 1.6.38 --- docs/WINDOWS_RELEASES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/WINDOWS_RELEASES.md b/docs/WINDOWS_RELEASES.md index 9917a9936..69f137c46 100644 --- a/docs/WINDOWS_RELEASES.md +++ b/docs/WINDOWS_RELEASES.md @@ -5,7 +5,7 @@ ARSAS publishes two Windows x64 deliverables from the same reviewed source revis - `ARSAS-Windows-x64-Portable.exe` — a real self-contained single EXE for approved no-install use. - `ARSAS-Windows-x64-Setup.exe` — the Inno Setup installer with Start Menu integration, uninstall support, and the same pinned ARIEC61850 and ArdIrec native-analysis revisions used by the release build. -The current application version on `main` is **1.6.37**. Public download metadata remains authoritative only after the corresponding tagged GitHub Release has been published and its checksums/provenance are visible. +The current application version on `main` is **1.6.38**. Public download metadata remains authoritative only after the corresponding tagged GitHub Release has been published and its checksums/provenance are visible. ## Official stable release path @@ -83,8 +83,8 @@ Prerequisites: Use the repository packaging scripts rather than hand-assembling a release folder. For example: ```powershell -.\scripts\publish-windows-portable.ps1 -Version 1.6.37 -.\scripts\build-windows-installer.ps1 -Version 1.6.37 -Runtime win-x64 +.\scripts\publish-windows-portable.ps1 -Version 1.6.38 +.\scripts\build-windows-installer.ps1 -Version 1.6.38 -Runtime win-x64 ``` Official CI additionally supplies the pinned engine projects and ArdIrec source explicitly so the build cannot silently resolve an unreviewed revision. From 516315775980fdfca5cf7cc7271ca923acad8665 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:14:05 +0700 Subject: [PATCH 16/80] docs: keep roadmap device-neutral --- ROADMAP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 14046d336..8b93f9a75 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -52,12 +52,12 @@ ARSAS is the application and workflow layer. Protocol implementation belongs in - Recursive and bounded directory browsing. - Capability discovery and transparent negative responses. - Download progress, cancellation, timeout, and retry boundaries. -- COMTRADE set recognition (`.cfg`, `.dat`, `.hdr`, `.inf`, vendor companions). +- COMTRADE set recognition (`.cfg`, `.dat`, `.hdr`, `.inf`, device companion files). - Safe local naming and duplicate handling. - Transfer evidence export and sanitized diagnostics. - Automated engine tests for directory, open, read, close, and failure paths. -**Definition of done:** fault records can be retrieved from representative multi-vendor test devices without vendor-specific logic in the ARSAS UI layer. +**Definition of done:** fault records can be retrieved from representative representative test devices without device-specific logic in the ARSAS UI layer. ## Milestone 2 — Production-grade Sampled Values From 910e559e026b585b38fc8cbe2a21c617925f3eea Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:14:20 +0700 Subject: [PATCH 17/80] docs: neutralize engine provenance wording --- engines/ARIEC61850.lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/ARIEC61850.lock.json b/engines/ARIEC61850.lock.json index 360e72a96..83f9f635f 100644 --- a/engines/ARIEC61850.lock.json +++ b/engines/ARIEC61850.lock.json @@ -18,7 +18,7 @@ "fieldProvenBaseline": { "commit": "11ab2304482600c19ba979f4fc9021ddb46b9af9", "sourcePullRequest": 111, - "purpose": "Pins the exact ARIEC61850 engine used by ARSAS while preserving the reviewed reporting/control ancestry. PR #76 preserves unresolved static DataSet members; PR #77 canonicalizes cross-logical-device SCL references; PR #78 keeps one descriptor per static DataSet member while separating the resolved runtime primary leaf from original FCDA/FCD identity; PR #79 projects generic Boolean status structures to scalar stVal while preserving quality/timestamp; PR #80 normalizes validated DataRef-enabled InformationReport ordering; PR #81 accepts valid zero OptFlds reports while quarantining unmapped canonical report metadata; PR #84 routes exact PrimaryValue residuals through dynamic reporting before MMS polling; PR #85 evaluates association capabilities before automatic dynamic mutation; PR #86 records dynamic-attempt failure/skip evidence and best-effort rollback. PR #87 restores baseline-safe static precedence. PR #88 adds a fail-closed single-member DefineNamedVariableList -> GetNamedVariableListAttributes -> DeleteNamedVariableList probation with exact invoke/request/response/routing/member/association/cleanup evidence. PR #89 quarantines automatic full dynamic DataSet activation because a successful one-member NVL probation does not guarantee association survival; it also preserves safe instMag/mag and instCVal/cVal projection while ambiguous structures remain raw. PR #90 / field-proven engine a18e550d07f7bbe4ff7753c180b02615075f6292 preserves G1/G1.1 Smart Control: signed primitive constraints, ordered SBO/SBOw-to-Operate wire evidence, StationControl origin compatibility, and explicit MMS Write DataAccessError including object-access-denied. G2 PR #91 adds qualification-only bounded multi-member DefineNamedVariableList/GetNamedVariableListAttributes/DeleteNamedVariableList evidence with exact ordered read-back, encoded request/PDU evidence and fail-closed cleanup; PR #92 adds the 1/4/8/16/32 qualification ladder, deterministic bisection and explicit EnvelopeQualified acceptance; PR #93 adds a default-disabled ExplicitCommissioning coordinator with hard attempt budget, exact-set failure localization and fresh-association stop semantics; PR #94 adds identity-bound qualification profiles and prevents ProductionEligible unless RCB activation, an actual correctly mapped InformationReport, and all G2.6 physical regression gates are proven. G2.4 engine PR #95 retains the commissioning-only transactional URCB TrgOps/OptFlds lease. P0 physically proved the corrected IEC 61850 MMS TrgOps reserved-bit mapping: bit 0 reserved, bits 1..5 dchg/qchg/dupd/integrity/GI, so dchg+GI encodes canonically as 0244; P0 also separates raw BER equality from IEC significant-bit equality and provides a one-URCB TrgOps-only micro-probe that never writes OptFlds, DatSet, Resv, RptEna, GI or any DataSet service. P1 adds a dedicated one-URCB OptFlds-only capture/write/readback/finally-restore micro-probe for reason-for-inclusion + data-set-name, canonical target 061800, using ten-bit significant-value comparison while never writing TrgOps, DatSet, Resv, RptEna, GI, Define/Delete DataSet, starting a report monitor, or changing profile state. The G2.4 Owner correction exposes the exact local TCP address of the active MMS association and fail-closed decodes a server RCB Owner as a 4-byte IPv4 or 16-byte IPv6 address; physical SIPROTEC Owner C0A851F0 decodes to 192.168.81.240 and may prove caller ownership only when it exactly matches the active local TCP endpoint. Owner mismatch or unsupported encoding remains a hard failure. Original RCB values remain captured for restore, raw BER evidence is retained, and Production automatic dynamic BRCB/URCB activation remains quarantined until a compatible ProductionEligible profile is consumed by a later G2 phase. FAT P5.3 engine PR #103 resolves intermediate structured static DataSet members such as MMXU A.phsA and PPV.phsAB only to typed descendants below the exact FCDA boundary, selects a unique semantic primary runtime leaf such as cVal.mag.f without crossing sibling phases, preserves original static membership identity, and leaves genuinely ambiguous structures unresolved rather than guessing. FAT P5.4 engine PR #106 adds fail-closed model-backed InformationReport projection for structured static DataSet members: an exact report member reference now resolves independently of sparse decoder-side report value position, while DataSet scope still prevents duplicate static memberships from collapsing; when a report omits the member reference, static DataSet index remains the unique fail-closed fallback. All schema-proven scalar descendants are fanned out without selecting a sibling phase, and schema mismatch preserves raw projection instead of guessing. ARSAS supplies the per-IED LiveDiscovery/SCL planning model at the report receive seam. PR #111 is a narrow continuation on the exact b9ee5fc ARSAS engine baseline: exact static DataSet/SCL semantic schema is attempted before generic structured-value heuristics so TotPF and similar members publish exact scalar leaves; generic projection remains the fail-closed fallback, and report q/t companions are ordered ahead of semantic scalar values. P1 hardening at 0d7525bd330900917fb9f6d15a46059dc3d7a70a also makes semantic expansion return the resolved authoritative member identity and replaces generic output by report-value position after semantic success, so an InformationReport that omits MemberReference but resolves uniquely through static DataSet index cannot leak unrooted projected-mx-pair leaves alongside exact semantic values. Physical BRCB compatibility hardening at 11ab2304482600c19ba979f4fc9021ddb46b9af9 adds a client-compatible persistent activation wrapper: when ResvTms is exposed it attempts an explicit 60-second BRCB reservation with implicit-RptEna fallback, keeps cleanup/release deterministic, and requests GI only after the persistent report session is registered." + "purpose": "Pins the exact ARIEC61850 engine used by ARSAS while preserving the reviewed reporting/control ancestry. PR #76 preserves unresolved static DataSet members; PR #77 canonicalizes cross-logical-device SCL references; PR #78 keeps one descriptor per static DataSet member while separating the resolved runtime primary leaf from original FCDA/FCD identity; PR #79 projects generic Boolean status structures to scalar stVal while preserving quality/timestamp; PR #80 normalizes validated DataRef-enabled InformationReport ordering; PR #81 accepts valid zero OptFlds reports while quarantining unmapped canonical report metadata; PR #84 routes exact PrimaryValue residuals through dynamic reporting before MMS polling; PR #85 evaluates association capabilities before automatic dynamic mutation; PR #86 records dynamic-attempt failure/skip evidence and best-effort rollback. PR #87 restores baseline-safe static precedence. PR #88 adds a fail-closed single-member DefineNamedVariableList -> GetNamedVariableListAttributes -> DeleteNamedVariableList probation with exact invoke/request/response/routing/member/association/cleanup evidence. PR #89 quarantines automatic full dynamic DataSet activation because a successful one-member NVL probation does not guarantee association survival; it also preserves safe instMag/mag and instCVal/cVal projection while ambiguous structures remain raw. PR #90 / field-proven engine a18e550d07f7bbe4ff7753c180b02615075f6292 preserves G1/G1.1 Smart Control: signed primitive constraints, ordered SBO/SBOw-to-Operate wire evidence, StationControl origin compatibility, and explicit MMS Write DataAccessError including object-access-denied. G2 PR #91 adds qualification-only bounded multi-member DefineNamedVariableList/GetNamedVariableListAttributes/DeleteNamedVariableList evidence with exact ordered read-back, encoded request/PDU evidence and fail-closed cleanup; PR #92 adds the 1/4/8/16/32 qualification ladder, deterministic bisection and explicit EnvelopeQualified acceptance; PR #93 adds a default-disabled ExplicitCommissioning coordinator with hard attempt budget, exact-set failure localization and fresh-association stop semantics; PR #94 adds identity-bound qualification profiles and prevents ProductionEligible unless RCB activation, an actual correctly mapped InformationReport, and all G2.6 physical regression gates are proven. G2.4 engine PR #95 retains the commissioning-only transactional URCB TrgOps/OptFlds lease. P0 physically proved the corrected IEC 61850 MMS TrgOps reserved-bit mapping: bit 0 reserved, bits 1..5 dchg/qchg/dupd/integrity/GI, so dchg+GI encodes canonically as 0244; P0 also separates raw BER equality from IEC significant-bit equality and provides a one-URCB TrgOps-only micro-probe that never writes OptFlds, DatSet, Resv, RptEna, GI or any DataSet service. P1 adds a dedicated one-URCB OptFlds-only capture/write/readback/finally-restore micro-probe for reason-for-inclusion + data-set-name, canonical target 061800, using ten-bit significant-value comparison while never writing TrgOps, DatSet, Resv, RptEna, GI, Define/Delete DataSet, starting a report monitor, or changing profile state. The G2.4 Owner correction exposes the exact local TCP address of the active MMS association and fail-closed decodes a server RCB Owner as a 4-byte IPv4 or 16-byte IPv6 address; physical-device Owner C0A851F0 decodes to 192.168.81.240 and may prove caller ownership only when it exactly matches the active local TCP endpoint. Owner mismatch or unsupported encoding remains a hard failure. Original RCB values remain captured for restore, raw BER evidence is retained, and Production automatic dynamic BRCB/URCB activation remains quarantined until a compatible ProductionEligible profile is consumed by a later G2 phase. FAT P5.3 engine PR #103 resolves intermediate structured static DataSet members such as MMXU A.phsA and PPV.phsAB only to typed descendants below the exact FCDA boundary, selects a unique semantic primary runtime leaf such as cVal.mag.f without crossing sibling phases, preserves original static membership identity, and leaves genuinely ambiguous structures unresolved rather than guessing. FAT P5.4 engine PR #106 adds fail-closed model-backed InformationReport projection for structured static DataSet members: an exact report member reference now resolves independently of sparse decoder-side report value position, while DataSet scope still prevents duplicate static memberships from collapsing; when a report omits the member reference, static DataSet index remains the unique fail-closed fallback. All schema-proven scalar descendants are fanned out without selecting a sibling phase, and schema mismatch preserves raw projection instead of guessing. ARSAS supplies the per-IED LiveDiscovery/SCL planning model at the report receive seam. PR #111 is a narrow continuation on the exact b9ee5fc ARSAS engine baseline: exact static DataSet/SCL semantic schema is attempted before generic structured-value heuristics so TotPF and similar members publish exact scalar leaves; generic projection remains the fail-closed fallback, and report q/t companions are ordered ahead of semantic scalar values. P1 hardening at 0d7525bd330900917fb9f6d15a46059dc3d7a70a also makes semantic expansion return the resolved authoritative member identity and replaces generic output by report-value position after semantic success, so an InformationReport that omits MemberReference but resolves uniquely through static DataSet index cannot leak unrooted projected-mx-pair leaves alongside exact semantic values. Physical BRCB compatibility hardening at 11ab2304482600c19ba979f4fc9021ddb46b9af9 adds a client-compatible persistent activation wrapper: when ResvTms is exposed it attempts an explicit 60-second BRCB reservation with implicit-RptEna fallback, keeps cleanup/release deterministic, and requests GI only after the persistent report session is registered." }, "physicalTestedCommit": "9935d6902d786cc69b299260fe36b835944d5e81", "mergedMainCommit": "648124097621046f5f127ceb1cf853fea54db730", From 4386687544fecdf0d77504aad42bb3342ac7bf17 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:14:37 +0700 Subject: [PATCH 18/80] docs: sync engine compatibility to R10 --- ENGINE_COMPATIBILITY.md | 67 +++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/ENGINE_COMPATIBILITY.md b/ENGINE_COMPATIBILITY.md index 5ca1fa149..9e9695b44 100644 --- a/ENGINE_COMPATIBILITY.md +++ b/ENGINE_COMPATIBILITY.md @@ -1,10 +1,13 @@ # ARIEC61850 Engine Compatibility -ARSAS 1.6.19 is an application-only repository. It compiles against the separately maintained ARIEC61850 source projects and uses engine-owned contracts for MMS, reporting, GOOSE, Sampled Values, file services, control, SCL workspace services, discovery, and diagnostics. +ARSAS **1.6.38** is the Windows application and workflow layer. It compiles +against the separately maintained ARIEC61850 source projects for MMS, reporting, +GOOSE, Sampled Values, file services, control, SCL workspace services, +discovery, diagnostics, and reusable protocol contracts. ## Immutable integration baseline -The reviewed engine revision used by CI and packaging is stored in: +The exact engine revision used by CI and stable packaging is stored in: ```text engines/ARIEC61850.lock.json @@ -15,24 +18,52 @@ Current baseline: ```text repository: masarray/ARIEC61850 ref: main -commit: 0f8453182957900bc6d91287fb8177c8d9762188 -source PR: #45 +merged commit: 648124097621046f5f127ceb1cf853fea54db730 +source PR: #135 +physical-tested commit: 9935d6902d786cc69b299260fe36b835944d5e81 +merged/tested source-tree SHA: 1cf7e08f333f24994625e8fe8416dbd0a16195b1 ``` -CI fetches the exact 40-character commit and checks it out detached. A temporary feature branch is not part of the reproducible build identity. +The physical-tested commit and merged engine authority resolve to the identical +source tree. ARSAS release packaging therefore consumes the reviewed merged +engine tree without substituting a different implementation. + +## ARSAS 1.6.38 acceptance + +The R10 acceptance line requires the engine/application pair to preserve: + +- one bounded structural-discovery association; +- exact LD/LN/DO/SDO/DA/FC identity; +- exact-case IEC 61850 value identity; +- DO-scoped CF hydration for multi-DO structures; +- canonical Edition 2 / Edition 1 SCL round trip; +- 32/32 trusted-SCL MMS domain reconciliation; +- zero failed planned initial Reads; +- `projectionErrors=0`; +- `cacheLoss=0`; +- 58/58 report-backed runtime points; +- zero final unresolved runtime points; +- actual InformationReport traffic; +- zero cyclic MMS process polling in the accepted report-backed path. + +The detailed physical contract is documented in +[Interoperability convergence](docs/INTEROPERABILITY_CONVERGENCE.md). ## Required engine areas -The build verifies the presence of application-consumed contracts for: +Build and release validation requires the application-consumed contracts for: - Smart Control and `CommandTermination`; - SCL workspace comparison and schema-aware export; - typed DataSet binding and stable member order; +- report-control discovery and logical/runtime identity projection; - GOOSE parsing and process-bus supervision; -- Sampled Values parsing, generic payload inspection, timebase resolution, sample-counter tracking, and Npcap capture; -- file-service, discovery, reporting, and diagnostic services referenced by the application project. +- Sampled Values parsing, generic payload inspection, timebase resolution, + sample-counter tracking, and Npcap capture; +- MMS file-service, discovery, reporting, and diagnostic services. -If a required contract is missing, CI and local builds fail explicitly rather than silently degrading the operator workflow. +If a required contract is missing, CI and local builds fail explicitly instead +of silently degrading the engineering workflow. ## Recommended sibling layout @@ -55,14 +86,24 @@ dotnet build .\ArIED61850Tester.sln -c Release --no-restore dotnet test .\tests\ARSAS.Tests\ARSAS.Tests.csproj -c Release --no-build --no-restore ``` -Another reviewed engine checkout may be selected with the existing MSBuild properties or environment variables, but release evidence must record the exact commit and must not imply compatibility with an unreviewed moving branch. +Another reviewed engine checkout may be selected with the existing MSBuild +properties or environment variables, but release evidence must record the exact +commit and must not imply compatibility with an unreviewed moving branch. ## Source and package boundary -ARSAS does not overwrite or vendor the ARIEC61850 source repository. A published Windows package may contain compiled engine assemblies as part of the combined GPL community application, while reusable protocol implementation remains owned and tested in ARIEC61850. +ARSAS does not copy the ARIEC61850 source tree into this repository. Stable +Windows packages contain the compiled engine components required by the +combined GPL community application, while reusable protocol implementation +continues to be maintained and tested in ARIEC61850. -Control operations intentionally have no generic MMS-write fallback. The native engine must provide a usable control descriptor and sequence contract before ARSAS enables command dispatch. +Control operations intentionally have no generic MMS-write fallback. The engine +must provide a usable control descriptor and sequence contract before ARSAS +enables command dispatch. ## Claim boundary -A successful build and regression suite proves compatibility with the pinned software revision. It does not establish formal IEC 61850 conformance, calibrated measurement accuracy, universal device interoperability, switching authority, cybersecurity approval, or functional-safety certification. +A successful build and regression suite proves compatibility with the pinned +software revision. It does not establish formal IEC 61850 conformance, +calibrated measurement accuracy, universal device interoperability, switching +authority, cybersecurity approval, or functional-safety certification. From db8efcad109b13f727346a6ae983675f5f1b7e2b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:14:53 +0700 Subject: [PATCH 19/80] docs: sync README to 1.6.38 R10 --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a1aa9b455..4ab2f3136 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ **Discover · Monitor · Test IO Lists · Diagnose · Generate SCL · Export Evidence** -ARSAS is an open-source Windows IEC 61850 engineering workstation for FAT, SAT, commissioning, troubleshooting, and multi-vendor integration. Start from an approved IED endpoint, an SCL file, or an IO List workbook; inspect what the device actually exposes; and preserve the result as attributable engineering evidence. +ARSAS is an open-source Windows IEC 61850 engineering workstation for FAT, SAT, commissioning, troubleshooting, and multi-device integration. Start from an approved IED endpoint, an SCL file, or an IO List workbook; inspect what the device actually exposes; and preserve the result as attributable engineering evidence. [![Build](https://github.com/masarray/arsas/actions/workflows/build.yml/badge.svg)](https://github.com/masarray/arsas/actions/workflows/build.yml) [![Pages](https://github.com/masarray/arsas/actions/workflows/pages.yml/badge.svg)](https://masarray.github.io/arsas/) @@ -33,10 +33,19 @@ ARSAS is an open-source Windows IEC 61850 engineering workstation for FAT, SAT, Choose Engineering for live IEC 61850 discovery or IO List FAT for resumable, reviewable test evidence. -> **Current ARSAS application version: v1.6.37.** The current source line aligns live RCB instance presentation with concrete runtime slots exposed by the connected IED while preserving canonical source-backed ReportControl identity, and makes in-process COMTRADE Phasor, Harmonics, and distance Locus interaction smoother without changing authoritative native engineering values. +> **Current ARSAS application version: v1.6.38.** This stable line carries the physically verified R10 discovery/SCL convergence baseline: generated Edition 2 and Edition 1 SCL reopen and reconnect with 32/32 domains, all planned initial Reads successful, `projectionErrors=0`, `cacheLoss=0`, 58/58 report-backed runtime points, actual InformationReport traffic, and zero cyclic MMS process polling on the accepted path. > > **Published-package boundary:** download version, file size, SHA-256, SBOM, provenance, and attestation claims remain tied to the latest actually published stable GitHub Release. `main` moving to a newer application version does not by itself advance public binary evidence. +## What changed in v1.6.38 + +- **Physically verified R10 convergence** — generated Edition 2 IID and Edition 1 ICD reopen in ARSAS, reconnect through trusted SCL, reconcile 32/32 MMS domains, and complete every planned initial Read. +- **Lossless trusted-SCL value pipeline** — both editions reach `projectionErrors=0` and `cacheLoss=0`; exact-case IEC 61850 paths remain distinct through projection, caching, canonical evidence, and SCL value targeting. +- **Report-backed runtime acquisition** — all 58 static DataSet members resolve to 58/58 runtime points, final unresolved points are zero, actual InformationReport traffic is observed, and cyclic MMS process polling remains zero on the accepted path. +- **Immutable engine authority** — the physical-tested engine head and merged engine commit resolve to the same source tree, preserving the tested behavior after merge. +- **Verified Windows publication** — portable and installer smoke tests pass; the stable release includes SHA-256 checksums, SPDX SBOM, provenance metadata, and artifact attestations. +- **Release scope discipline** — later save-enrichment snapshot reuse remains outside this stable release. + ## What changed in v1.6.37 - **Concrete live RCB instances** — Legacy SAS RCB selection presents concrete instances such as `Buffer01`, `Buffer02`, `Unbuffer01`, and `Unbuffer02` when the connected IED actually exposes them, without adding duplicate logical placeholder rows. @@ -69,7 +78,7 @@ ARSAS is an open-source Windows IEC 61850 engineering workstation for FAT, SAT, | Engineering problem | ARSAS response | |---|---| -| Vendor CID or ICD is missing, outdated, or rejected. | Perform complete live MMS discovery and generate a schema-aware **Edition 2 IID** or **Edition 1 ICD** with companion evidence. | +| Device CID or ICD is missing, outdated, or rejected. | Perform complete live MMS discovery and generate a schema-aware **Edition 2 IID** or **Edition 1 ICD** with companion evidence. | | Reporting setup consumes the test window before values appear. | Read an immediate MMS image, prefer verified BRCB/URCB coverage, recover bounded gaps where permitted, and keep fallback acquisition visible. | | A multi-IED test loses device ownership and diagnostics. | Maintain an independent association, model, monitoring state, events, files, control context, and diagnostics for every IED. | | A FAT team must rebuild IO List evidence manually. | Import the approved workbook, bind exact IEC 61850 references, record ordered **OFF → ON → OFF** evidence, and export Excel, native PDF, or a resumable `.arsas` project. | From bdc2a0d6fb1b21add67ba3cc4e39c3d239bab9f4 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:15:10 +0700 Subject: [PATCH 20/80] docs: record 1.6.38 stable R10 release --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6431853..1309e92f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ Notable public changes to ARSAS are recorded here. Application releases must ide ## Unreleased +## 1.6.38 — 2026-09-19 + +### Added + +- Public stable Windows release for the physically verified R10 discovery/SCL convergence baseline. +- Release evidence now includes verified installer and portable artifacts, SHA-256 checksums, SPDX 2.3 SBOM, provenance metadata, and artifact attestations. + +### Changed + +- Trusted-SCL reuse for generated Edition 2 IID and Edition 1 ICD now preserves exact-case value identity with `cacheLoss=0` on both editions. +- Multi-DO CF hydration uses exact DO-scoped structured Reads instead of cross-DO positional assumptions. +- Post-merge and mainline CI gates validate the accepted R10 physical authority and immutable engine source tree. + +### Fixed + +- Both generated editions reopen and reconnect with 32/32 MMS domains, all planned initial Reads successful, `projectionErrors=0`, 58/58 report-backed runtime points, zero final unresolved runtime points, actual InformationReport traffic, and zero cyclic MMS process polling on the accepted path. +- Release automation now publishes the verified stable package from the exact mainline release commit. + ## 1.6.37 — 2026-09-16 ### Added From 8b28d3b8f78728b2e3663290f65cd17e555d69fe Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:15:25 +0700 Subject: [PATCH 21/80] docs: sync landing release notes to R10 --- landing/release-notes.json | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/landing/release-notes.json b/landing/release-notes.json index 0b00ab1aa..1e23e2612 100644 --- a/landing/release-notes.json +++ b/landing/release-notes.json @@ -4,40 +4,44 @@ "version": "1.6.38", "title": "ARSAS 1.6.38 stable release", "titleId": "Stable release ARSAS 1.6.38", - "summary": "ARSAS 1.6.38 is the latest verified stable Windows release. Package identity, download links, checksums and publication evidence are synchronized automatically from the tagged GitHub Release.", - "summaryId": "ARSAS 1.6.38 adalah stable release Windows terverifikasi terbaru. Identitas paket, link download, checksum, dan publication evidence disinkronkan otomatis dari GitHub Release bertag.", + "summary": "ARSAS 1.6.38 is the latest verified stable Windows release, carrying the physically verified R10 discovery/SCL convergence baseline and synchronized package evidence.", + "summaryId": "ARSAS 1.6.38 adalah stable release Windows terverifikasi terbaru, membawa baseline convergence discovery/SCL R10 yang sudah tervalidasi fisik beserta evidence paket yang tersinkron.", "highlights": [ - "Polish WPF typography with embedded Inter and smooth rendering", - "convergence(discovery/scl): IEDScout-parity discovery and usable SCL", - "fix(ci): restore R10 post-merge release gates", - "The verified Windows installer is published as the stable ARSAS 1.6.38 package." + "Physically verified R10 discovery and trusted-SCL round trip for Edition 2 IID and Edition 1 ICD.", + "32/32 MMS domains reconcile on both editions, with every planned initial Read successful.", + "Both editions reach projectionErrors=0 and cacheLoss=0 while preserving exact-case IEC 61850 value identity.", + "All 58 static DataSet members resolve to 58/58 report-backed runtime points with zero final unresolved points.", + "Actual InformationReport traffic is observed and cyclic MMS process polling remains zero on the accepted path.", + "The verified Windows installer and portable package are published with SHA-256 checksums, SPDX SBOM, provenance, and attestations." ], "highlightsId": [ - "Perubahan rilis: Polish WPF typography with embedded Inter and smooth rendering", - "Perubahan rilis: convergence(discovery/scl): IEDScout-parity discovery and usable SCL", - "Perubahan rilis: fix(ci): restore R10 post-merge release gates", - "Installer Windows terverifikasi dipublikasikan sebagai paket stabil ARSAS 1.6.38." + "Round trip discovery dan trusted-SCL R10 untuk Edition 2 IID dan Edition 1 ICD sudah tervalidasi fisik.", + "Kedua edisi merekonsiliasi 32/32 domain MMS dan seluruh planned initial Read berhasil.", + "Kedua edisi mencapai projectionErrors=0 dan cacheLoss=0 sambil mempertahankan exact-case identity untuk value IEC 61850.", + "Seluruh 58 static DataSet member terpetakan menjadi 58/58 runtime point berbasis report dengan final unresolved point=0.", + "Traffic InformationReport aktual terobservasi dan cyclic MMS process polling tetap 0 pada jalur acceptance.", + "Installer Windows dan portable package terverifikasi dipublikasikan bersama SHA-256, SPDX SBOM, provenance, dan attestation." ], "improvements": [ "Stable release identity, publish date, package size and SHA-256 are sourced from verified release evidence rather than hand-maintained version text.", - "The Windows installer and portable download URLs use GitHub's releases/latest/download endpoints so the public buttons always resolve to the newest stable assets.", - "Release notes are synchronized before website deployment, preventing a newer binary release from being blocked by stale landing-page metadata.", - "The website deployment is explicitly dispatched after release synchronization so GitHub Actions token commits cannot leave Pages behind the published release." + "The physical-tested engine head and merged engine authority resolve to the identical source tree.", + "Mainline and post-merge CI enforce the R10 physical acceptance contract before publication.", + "Public release metadata and website download targets are synchronized from the tagged stable GitHub Release." ], "improvementsId": [ "Identitas stable release, tanggal publikasi, ukuran paket, dan SHA-256 diambil dari evidence release terverifikasi, bukan teks versi yang dipelihara manual.", - "URL installer Windows dan portable memakai endpoint releases/latest/download GitHub sehingga tombol publik selalu menuju asset stabil terbaru.", - "Catatan rilis disinkronkan sebelum deployment website agar binary release baru tidak terblokir metadata landing page yang tertinggal.", - "Deployment website dipicu eksplisit setelah sinkronisasi release sehingga commit dari GitHub Actions token tidak membuat Pages tertinggal dari release publik." + "Physical-tested engine head dan merged engine authority mengarah ke source tree yang identik.", + "CI mainline dan post-merge menegakkan physical acceptance contract R10 sebelum publikasi.", + "Metadata release publik dan target download website disinkronkan dari stable GitHub Release bertag." ], "knownLimitations": [ - "Physical relay validation remains necessary for vendor-specific values, report behavior and field network conditions that cannot be reproduced by CI.", + "Physical device validation remains necessary for implementation-specific values, report behavior and field network conditions that cannot be reproduced by CI.", "Windows x64 is the only packaged desktop platform in this stable release.", "The public binaries are not Authenticode code-signed; Windows SmartScreen may show an unrecognized-publisher warning.", "Raw-Ethernet GOOSE and Sampled Values workflows require an administrator-installed and approved Npcap driver, suitable capture permission and visibility of the relevant multicast traffic." ], "knownLimitationsId": [ - "Validasi relay fisik tetap diperlukan untuk value vendor-specific, perilaku report, dan kondisi network lapangan yang tidak dapat direproduksi oleh CI.", + "Validasi perangkat fisik tetap diperlukan untuk value yang implementation-specific, perilaku report, dan kondisi network lapangan yang tidak dapat direproduksi oleh CI.", "Windows x64 adalah satu-satunya platform desktop yang dipaketkan pada stable release ini.", "Binary publik belum ditandatangani dengan Authenticode; Windows SmartScreen dapat menampilkan peringatan unrecognized publisher.", "Workflow raw-Ethernet GOOSE dan Sampled Values memerlukan driver Npcap yang telah dipasang dan disetujui administrator, capture permission yang sesuai, serta visibility traffic multicast terkait." From 136c4d97e5fd5bb0ed7709700d8d26081a1b8fbc Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:16:11 +0700 Subject: [PATCH 22/80] docs: align staged release notes with 1.6.38 --- landing/release-notes-next.json | 62 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/landing/release-notes-next.json b/landing/release-notes-next.json index 7ae05697a..1e23e2612 100644 --- a/landing/release-notes-next.json +++ b/landing/release-notes-next.json @@ -1,45 +1,47 @@ { "schemaVersion": 1, "product": "ARSAS", - "version": "1.6.37", - "title": "Runtime-aligned RCB export and smoother COMTRADE interaction", - "titleId": "Export RCB selaras runtime dan interaksi COMTRADE lebih halus", - "summary": "ARSAS 1.6.37 is a focused reliability and UX update for Legacy SAS RCB export and the in-process COMTRADE workstation. Live RCB instances are presented as concrete runtime slots exposed by the connected IED while source-backed IID/SCL export keeps the canonical logical ReportControl identity, and COMTRADE Phasor, Harmonics and distance Locus presentation now follows native analysis targets smoothly without changing authoritative engineering values.", - "summaryId": "ARSAS 1.6.37 adalah pembaruan reliability dan UX yang terfokus untuk export RCB Legacy SAS dan workstation COMTRADE in-process. Instance RCB live ditampilkan sebagai slot runtime konkret yang benar-benar diekspos oleh IED terhubung sementara export IID/SCL source-backed tetap mempertahankan identity logical ReportControl yang canonical, dan presentation COMTRADE Phasor, Harmonics, serta distance Locus kini mengikuti target native analysis secara lebih halus tanpa mengubah nilai engineering yang authoritative.", + "version": "1.6.38", + "title": "ARSAS 1.6.38 stable release", + "titleId": "Stable release ARSAS 1.6.38", + "summary": "ARSAS 1.6.38 is the latest verified stable Windows release, carrying the physically verified R10 discovery/SCL convergence baseline and synchronized package evidence.", + "summaryId": "ARSAS 1.6.38 adalah stable release Windows terverifikasi terbaru, membawa baseline convergence discovery/SCL R10 yang sudah tervalidasi fisik beserta evidence paket yang tersinkron.", "highlights": [ - "Legacy SAS RCB selection now presents concrete live instances such as Buffer01/Buffer02 and Unbuffer01/Unbuffer02 when those instances are actually exposed by the connected IED, without showing a duplicate logical Buffer/Unbuffer row.", - "Source-backed RCB export preserves the canonical SCL ReportControl identity and RptEnabled indexing metadata, keeping online runtime instances separate from the saved engineering model.", - "RCB occupancy is shown with a calm square-only indicator: yellow when a client is using the RCB and green otherwise, while detailed engineering evidence remains available without alarming status captions.", - "COMTRADE Phasor and Harmonics views ease toward the latest exact native result at display cadence, with bounded settle time and exact convergence to the authoritative analysis target.", - "Distance Locus cursor presentation now uses the same latest-target principle and resets transient presentation state across PRI/SEC engineering-basis changes so unrelated unit bases are never interpolated together." + "Physically verified R10 discovery and trusted-SCL round trip for Edition 2 IID and Edition 1 ICD.", + "32/32 MMS domains reconcile on both editions, with every planned initial Read successful.", + "Both editions reach projectionErrors=0 and cacheLoss=0 while preserving exact-case IEC 61850 value identity.", + "All 58 static DataSet members resolve to 58/58 report-backed runtime points with zero final unresolved points.", + "Actual InformationReport traffic is observed and cyclic MMS process polling remains zero on the accepted path.", + "The verified Windows installer and portable package are published with SHA-256 checksums, SPDX SBOM, provenance, and attestations." ], "highlightsId": [ - "Pemilihan RCB Legacy SAS kini menampilkan instance live konkret seperti Buffer01/Buffer02 dan Unbuffer01/Unbuffer02 ketika instance tersebut benar-benar diekspos oleh IED yang terhubung, tanpa menampilkan row logical Buffer/Unbuffer duplikat.", - "Export RCB source-backed mempertahankan identity SCL ReportControl canonical dan metadata indexing RptEnabled, dengan pemisahan yang jelas antara instance runtime online dan engineering model tersimpan.", - "Occupancy RCB ditampilkan dengan indikator kotak yang tenang: kuning ketika RCB sedang digunakan client dan hijau untuk kondisi lainnya, sementara evidence engineering detail tetap tersedia tanpa caption status yang mengkhawatirkan.", - "View COMTRADE Phasor dan Harmonics melakukan easing menuju hasil native exact terbaru pada display cadence, dengan settle time terbatas dan konvergensi exact ke target analysis authoritative.", - "Presentation cursor Distance Locus kini memakai prinsip latest-target yang sama dan mereset transient presentation state saat basis engineering PRI/SEC berubah sehingga unit basis yang berbeda tidak pernah diinterpolasikan bersama." + "Round trip discovery dan trusted-SCL R10 untuk Edition 2 IID dan Edition 1 ICD sudah tervalidasi fisik.", + "Kedua edisi merekonsiliasi 32/32 domain MMS dan seluruh planned initial Read berhasil.", + "Kedua edisi mencapai projectionErrors=0 dan cacheLoss=0 sambil mempertahankan exact-case identity untuk value IEC 61850.", + "Seluruh 58 static DataSet member terpetakan menjadi 58/58 runtime point berbasis report dengan final unresolved point=0.", + "Traffic InformationReport aktual terobservasi dan cyclic MMS process polling tetap 0 pada jalur acceptance.", + "Installer Windows dan portable package terverifikasi dipublikasikan bersama SHA-256, SPDX SBOM, provenance, dan attestation." ], "improvements": [ - "The RCB source/export boundary is regression-guarded so declarative RptEnabled@max metadata never becomes authority to invent runtime RCB names.", - "Live-model-only RCB export remains concrete and unchanged; canonical source identity preservation is opt-in only for source-backed SCL export.", - "COMTRADE presentation smoothing does not add an analysis queue or periodic work after settling: cursor/native analysis remains latest-wins and authoritative.", - "The stable Windows release pipeline runs full application regressions, RCB and COMTRADE integration guards, portable and installer smoke tests, SHA-256 checksums, SPDX SBOM, provenance and GitHub attestations before publication." + "Stable release identity, publish date, package size and SHA-256 are sourced from verified release evidence rather than hand-maintained version text.", + "The physical-tested engine head and merged engine authority resolve to the identical source tree.", + "Mainline and post-merge CI enforce the R10 physical acceptance contract before publication.", + "Public release metadata and website download targets are synchronized from the tagged stable GitHub Release." ], "improvementsId": [ - "Boundary source/export RCB dikunci regression test sehingga metadata deklaratif RptEnabled@max tidak pernah menjadi authority untuk menciptakan nama RCB runtime.", - "Export RCB live-model-only tetap concrete dan tidak berubah; preservation identity source canonical hanya diaktifkan untuk export SCL source-backed.", - "Smoothing presentation COMTRADE tidak menambah analysis queue maupun kerja periodik setelah settle: cursor/native analysis tetap latest-wins dan authoritative.", - "Pipeline stable release Windows menjalankan regression test aplikasi lengkap, guard integrasi RCB dan COMTRADE, smoke test portable dan installer, checksum SHA-256, SPDX SBOM, provenance, serta GitHub attestation sebelum publikasi." + "Identitas stable release, tanggal publikasi, ukuran paket, dan SHA-256 diambil dari evidence release terverifikasi, bukan teks versi yang dipelihara manual.", + "Physical-tested engine head dan merged engine authority mengarah ke source tree yang identik.", + "CI mainline dan post-merge menegakkan physical acceptance contract R10 sebelum publikasi.", + "Metadata release publik dan target download website disinkronkan dari stable GitHub Release bertag." ], "knownLimitations": [ - "Physical relay validation remains necessary for vendor-specific values, report behavior and field network conditions that cannot be reproduced by CI.", + "Physical device validation remains necessary for implementation-specific values, report behavior and field network conditions that cannot be reproduced by CI.", "Windows x64 is the only packaged desktop platform in this stable release.", "The public binaries are not Authenticode code-signed; Windows SmartScreen may show an unrecognized-publisher warning.", "Raw-Ethernet GOOSE and Sampled Values workflows require an administrator-installed and approved Npcap driver, suitable capture permission and visibility of the relevant multicast traffic." ], "knownLimitationsId": [ - "Validasi relay fisik tetap diperlukan untuk value vendor-specific, perilaku report, dan kondisi network lapangan yang tidak dapat direproduksi oleh CI.", + "Validasi perangkat fisik tetap diperlukan untuk value yang implementation-specific, perilaku report, dan kondisi network lapangan yang tidak dapat direproduksi oleh CI.", "Windows x64 adalah satu-satunya platform desktop yang dipaketkan pada stable release ini.", "Binary publik belum ditandatangani dengan Authenticode; Windows SmartScreen dapat menampilkan peringatan unrecognized publisher.", "Workflow raw-Ethernet GOOSE dan Sampled Values memerlukan driver Npcap yang telah dipasang dan disetujui administrator, capture permission yang sesuai, serta visibility traffic multicast terkait." @@ -55,11 +57,11 @@ "src": "assets/screenshots/arsas-live-values.webp", "width": 1507, "height": 893, - "alt": "ARSAS 1.6.37 Engineering, RCB export and in-process COMTRADE workstation", - "altId": "Workstation Engineering, export RCB, dan COMTRADE in-process ARSAS 1.6.37", - "caption": "ARSAS 1.6.37 aligns live RCB selection and source-backed export semantics while keeping COMTRADE interaction smooth and native analysis authoritative.", - "captionId": "ARSAS 1.6.37 menyelaraskan pemilihan RCB live dan semantics export source-backed sambil menjaga interaksi COMTRADE tetap halus dan native analysis tetap authoritative." + "alt": "ARSAS 1.6.38 Engineering and FAT live IEC 61850 workspace", + "altId": "Workspace live IEC 61850 Engineering dan FAT ARSAS 1.6.38", + "caption": "ARSAS 1.6.38 stable Windows release with verified installer, portable package and release evidence.", + "captionId": "Stable release Windows ARSAS 1.6.38 dengan installer, portable package, dan release evidence yang terverifikasi." }, "issuesUrl": "https://github.com/masarray/arsas/issues/new/choose", - "releaseUrl": "https://github.com/masarray/arsas/releases/tag/v1.6.37" + "releaseUrl": "https://github.com/masarray/arsas/releases/tag/v1.6.38" } From 4c572a8f820d695a2732f25f2541ba2ca45a10f3 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:16:15 +0700 Subject: [PATCH 23/80] docs: add multi-device integration landing route --- .../templates/multi-device-integration.html | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 landing/templates/multi-device-integration.html diff --git a/landing/templates/multi-device-integration.html b/landing/templates/multi-device-integration.html new file mode 100644 index 000000000..5c56f74ed --- /dev/null +++ b/landing/templates/multi-device-integration.html @@ -0,0 +1,48 @@ + + + + + + Multi-Device IEC 61850 Integration — IP-to-SCL and Live Evidence | ARSAS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{> header}} +
+
IP-to-SCL · live model · reporting · GOOSE · gateway integration

Multi-device IEC 61850 integration with a recoverable path when the engineering file fails.

Use ARSAS to discover the complete live IED model, generate IID or ICD when the available engineering file is unusable, normalize bounded single-IED SCL, compare configured intent with runtime evidence, and isolate reporting or GOOSE mismatches before editing the target project.

+
Recover from IPComplete live discovery to Edition 2 IID or Edition 1 ICD
Normalize SCLBounded generic single-IED conversion with structured findings
Verify runtimeReporting, GOOSE, identity, DataSets and RCBs in live context
Export intentionallyOne verified RCB as an Edition 1 or Edition 2 CID baseline
+
Suggested workflow

Find the mismatch before changing either system.

Interoperability failures usually sit between three sources: the IED that is online, the engineering file that describes intended configuration, and the proprietary expectations of the receiving SAS or gateway.

01

Identify the available source

Start from the approved IED IP address, the available CID/ICD/IID/SCD, or both. Preserve the original file and record the intended receiving system.

02

Discover the live IED completely

Inspect physical identity, full MMS domains, hierarchy, DataSets, RCBs, type information and service behavior actually exposed by the device.

03

Generate or normalize bounded SCL

Create IID or ICD from complete live discovery, or convert the opened source into a generic single-IED output while retaining findings and provenance.

04

Compare reporting and GOOSE evidence

Check RCB names, availability, DataSet membership, trigger options, APPID, VLAN, addresses, references, sequence behavior and ordered payload.

05

Validate the receiving system deliberately

Import the bounded output, retain exact errors, and separate schema, identity, reference, reporting and proprietary importer problems before making changes.

+
Typical failure

“CID rejected” is not a root cause.

  • Physical IED identity does not match the receiving project
  • Logical Device domains were shortened or reinterpreted
  • Edition 1 and Edition 2 expectations differ
  • DataSet members or FCDA references are missing
  • An indexed RCB name was modified incorrectly
  • The receiving tool applies an undocumented proprietary rule
ARSAS evidence path

Keep each layer visible.

  • Live device identity and complete model
  • Original configured SCL intent
  • Generated or normalized bounded output
  • Read-only live RCB availability and options
  • Observed GOOSE communication and payload
  • Exact receiving-system import result
+
ARSAS selected RCB SCL export for multi-device IEC 61850 integration

Bounded selected-RCB CID export

Audit live availability, choose the intended reporting path and preserve exact RCB identity plus verified DataSet evidence before exporting the integration baseline.

+
Useful for

SAS, gateway and IED integration.

  • Recovering when a device CID or ICD cannot be used
  • Edition 1 and Edition 2 project alignment
  • Reporting DataSet and RCB troubleshooting
  • GOOSE address, reference and payload verification
  • Configured-to-live model comparison
  • Multi-IED diagnostic attribution
Boundary

Evidence is not universal conformance.

A successful connection, generation or import does not prove full IEC 61850 conformance, interoperability with every implementation, project correctness or acceptance by another proprietary tool. Final FAT/SAT engineering validation remains required.

+
Related product areas

Inspect the evidence behind the integration.

+ {{> download-cta}} +
+ {{> footer}} + + From f53f55401b1dba188d00f1196c8c0294308b8ed1 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:16:19 +0700 Subject: [PATCH 24/80] docs: add multi-device Indonesian landing route --- .../integrasi-multi-perangkat-iec61850.html | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 landing/templates/integrasi-multi-perangkat-iec61850.html diff --git a/landing/templates/integrasi-multi-perangkat-iec61850.html b/landing/templates/integrasi-multi-perangkat-iec61850.html new file mode 100644 index 000000000..1f1c90deb --- /dev/null +++ b/landing/templates/integrasi-multi-perangkat-iec61850.html @@ -0,0 +1,27 @@ + + + + + Integrasi Multi-Perangkat IEC 61850 — IP-to-SCL dan Live Evidence | ARSAS + + + + + + + + + {{> header}} +
+
IP-to-SCL · live model · reporting · GOOSE · integrasi gateway

Integrasi multi-perangkat IEC 61850 dengan jalur recovery saat engineering file gagal.

Gunakan ARSAS untuk discovery complete live IED model, generate IID atau ICD ketika engineering file tidak usable, normalize bounded single-IED SCL, membandingkan configured intent dengan runtime evidence, dan mengisolasi mismatch reporting atau GOOSE sebelum mengubah project tujuan.

+
Recovery dari IPComplete live discovery menjadi IID Edition 2 atau ICD Edition 1
Normalize SCLBounded generic single-IED conversion dengan structured findings
Verifikasi runtimeReporting, GOOSE, identity, DataSet dan RCB dalam live context
Export sengajaSatu verified RCB sebagai baseline CID Edition 1 atau Edition 2
+
Workflow yang disarankan

Temukan mismatch sebelum mengubah salah satu sistem.

Failure interoperability biasanya berada di antara tiga sumber: IED yang online, engineering file yang menggambarkan intended configuration, dan proprietary expectation SAS atau gateway penerima.

01

Identifikasi source yang tersedia

Mulai dari alamat IP IED, CID/ICD/IID/SCD yang tersedia, atau keduanya. Pertahankan original file dan catat receiving system yang dituju.

02

Discovery IED live secara lengkap

Periksa physical identity, full MMS domain, hierarchy, DataSet, RCB, type information dan service behavior aktual.

03

Generate atau normalize bounded SCL

Buat IID/ICD dari complete live discovery, atau convert opened source menjadi generic single-IED output sambil mempertahankan findings dan provenance.

04

Bandingkan reporting dan GOOSE evidence

Periksa RCB name, availability, DataSet membership, trigger options, APPID, VLAN, address, reference, sequence behavior dan ordered payload.

05

Validasi receiving system secara sengaja

Import bounded output, pertahankan exact error, lalu pisahkan masalah schema, identity, reference, reporting dan proprietary importer sebelum melakukan perubahan.

+
Failure yang umum

“CID rejected” bukan root cause.

  • Physical IED identity tidak cocok dengan project penerima
  • Logical Device domain dipotong atau ditafsirkan ulang
  • Expectation Edition 1 dan Edition 2 berbeda
  • DataSet member atau FCDA reference hilang
  • Indexed RCB name dimodifikasi secara salah
  • Receiving tool menerapkan proprietary rule yang tidak terdokumentasi
Evidence path ARSAS

Pertahankan setiap layer tetap terlihat.

  • Live device identity dan complete model
  • Original configured SCL intent
  • Generated atau normalized bounded output
  • Read-only live RCB availability serta options
  • Observed GOOSE communication dan payload
  • Exact receiving-system import result
+
Export SCL RCB terpilih untuk integrasi multi-perangkat IEC 61850

Bounded selected-RCB CID export

Audit live availability, pilih reporting path dan pertahankan exact RCB identity serta verified DataSet evidence sebelum export baseline integrasi.

+
Berguna untuk

Integrasi SAS, gateway dan IED.

  • Recovery saat CID perangkat atau ICD tidak dapat dipakai
  • Alignment project Edition 1 dan Edition 2
  • Troubleshooting DataSet dan RCB reporting
  • Verifikasi address, reference dan payload GOOSE
  • Perbandingan configured-to-live model
  • Attribution diagnostic multi-IED
Batas engineering

Evidence bukan universal conformance.

Koneksi, generation atau import yang berhasil belum membuktikan conformance penuh, interoperability dengan semua implementasi, kebenaran project atau acceptance oleh proprietary tool lain. Final validation FAT/SAT tetap diperlukan.

+
Area produk terkait

Periksa evidence di balik integrasi.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 391b8665d8163ed06b5ab3d30093c6fb0474cfb1 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:16:31 +0700 Subject: [PATCH 25/80] docs: update landing routes to device-neutral slugs --- landing/site.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/landing/site.json b/landing/site.json index 391a48f45..e16c36823 100644 --- a/landing/site.json +++ b/landing/site.json @@ -37,8 +37,8 @@ {"path": "apa-itu-iec61850.html", "template": "apa-itu-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "what-is-iec61850.html", "id": "apa-itu-iec61850.html", "x-default": "what-is-iec61850.html"}, "changefreq": "monthly", "priority": "0.96", "lastmod": "2026-07-29"}, {"path": "connect-ied-ip-arsas.html", "template": "connect-ied-ip-arsas.html", "language": "en", "contentType": "learning", "alternates": {"en": "connect-ied-ip-arsas.html", "id": "cara-hubungkan-ied-ip-arsas.html", "x-default": "connect-ied-ip-arsas.html"}, "changefreq": "monthly", "priority": "0.96", "lastmod": "2026-07-29"}, {"path": "cara-hubungkan-ied-ip-arsas.html", "template": "cara-hubungkan-ied-ip-arsas.html", "language": "id", "contentType": "localized", "alternates": {"en": "connect-ied-ip-arsas.html", "id": "cara-hubungkan-ied-ip-arsas.html", "x-default": "connect-ied-ip-arsas.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-29"}, - {"path": "faq.html", "template": "faq.html", "language": "en", "contentType": "faq", "alternates": {"en": "faq.html", "id": "faq-arsas.html", "x-default": "faq.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-27"}, - {"path": "faq-arsas.html", "template": "faq-arsas.html", "language": "id", "contentType": "localized", "alternates": {"en": "faq.html", "id": "faq-arsas.html", "x-default": "faq.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-27"}, + {"path": "faq.html", "template": "faq.html", "language": "en", "contentType": "faq", "alternates": {"en": "faq.html", "id": "faq-arsas.html", "x-default": "faq.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-09-19"}, + {"path": "faq-arsas.html", "template": "faq-arsas.html", "language": "id", "contentType": "localized", "alternates": {"en": "faq.html", "id": "faq-arsas.html", "x-default": "faq.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-09-19"}, {"path": "compatibility.html", "template": "compatibility.html", "language": "en", "contentType": "evidence", "alternates": {"en": "compatibility.html", "id": "bukti-kompatibilitas.html", "x-default": "compatibility.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-22"}, {"path": "bukti-kompatibilitas.html", "template": "bukti-kompatibilitas.html", "language": "id", "contentType": "localized", "alternates": {"en": "compatibility.html", "id": "bukti-kompatibilitas.html", "x-default": "compatibility.html"}, "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-22"}, {"path": "demo.html", "template": "demo.html", "language": "en", "contentType": "demo", "alternates": {"en": "demo.html", "id": "demo-arsas.html", "x-default": "demo.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-22"}, @@ -53,8 +53,8 @@ {"path": "pengujian-sat-iec61850.html", "template": "pengujian-sat-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "sat-testing.html", "id": "pengujian-sat-iec61850.html", "x-default": "sat-testing.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, {"path": "commissioning.html", "template": "commissioning.html", "language": "en", "alternates": {"en": "commissioning.html", "id": "commissioning-iec61850.html", "x-default": "commissioning.html"}, "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, {"path": "commissioning-iec61850.html", "template": "commissioning-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "commissioning.html", "id": "commissioning-iec61850.html", "x-default": "commissioning.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, - {"path": "multi-vendor-integration.html", "template": "multi-vendor-integration.html", "language": "en", "alternates": {"en": "multi-vendor-integration.html", "id": "integrasi-multi-vendor-iec61850.html", "x-default": "multi-vendor-integration.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-27"}, - {"path": "integrasi-multi-vendor-iec61850.html", "template": "integrasi-multi-vendor-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "multi-vendor-integration.html", "id": "integrasi-multi-vendor-iec61850.html", "x-default": "multi-vendor-integration.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-27"}, + {"path": "multi-device-integration.html", "template": "multi-device-integration.html", "language": "en", "alternates": {"en": "multi-device-integration.html", "id": "integrasi-multi-perangkat-iec61850.html", "x-default": "multi-device-integration.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-09-19"}, + {"path": "integrasi-multi-perangkat-iec61850.html", "template": "integrasi-multi-perangkat-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "multi-device-integration.html", "id": "integrasi-multi-perangkat-iec61850.html", "x-default": "multi-device-integration.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-09-19"}, {"path": "guides.html", "template": "guides.html", "language": "en", "contentType": "guide-hub", "alternates": {"en": "guides.html", "id": "panduan.html", "x-default": "guides.html"}, "changefreq": "weekly", "priority": "0.95", "lastmod": "2026-07-28"}, {"path": "panduan.html", "template": "panduan.html", "language": "id", "contentType": "localized", "alternates": {"en": "guides.html", "id": "panduan.html", "x-default": "guides.html"}, "changefreq": "weekly", "priority": "0.92", "lastmod": "2026-07-28"}, {"path": "reporting-silent.html", "template": "reporting-silent.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, @@ -64,20 +64,20 @@ {"path": "port-102-connection-failed.html", "template": "port-102-connection-failed.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, {"path": "comtrade-download.html", "template": "comtrade-download.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, {"path": "goose-sequence.html", "template": "goose-sequence.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, - {"path": "cid-rejected.html", "template": "cid-rejected.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-27"}, - {"path": "live-model-vs-scl.html", "template": "live-model-vs-scl.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-27"}, + {"path": "cid-rejected.html", "template": "cid-rejected.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-09-19"}, + {"path": "live-model-vs-scl.html", "template": "live-model-vs-scl.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-09-19"}, {"path": "direct-vs-sbo.html", "template": "direct-vs-sbo.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.87", "lastmod": "2026-07-20"}, {"path": "commandtermination-addcause.html", "template": "commandtermination-addcause.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.87", "lastmod": "2026-07-20"}, - {"path": "mms-client.html", "template": "mms-client.html", "language": "en", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-27"}, - {"path": "mms-client-iec61850.html", "template": "mms-client-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-27"}, + {"path": "mms-client.html", "template": "mms-client.html", "language": "en", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-09-19"}, + {"path": "mms-client-iec61850.html", "template": "mms-client-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-09-19"}, {"path": "smart-reporting.html", "template": "smart-reporting.html", "language": "en", "alternates": {"en": "smart-reporting.html", "id": "smart-reporting-iec61850.html", "x-default": "smart-reporting.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-20"}, {"path": "smart-reporting-iec61850.html", "template": "smart-reporting-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "smart-reporting.html", "id": "smart-reporting-iec61850.html", "x-default": "smart-reporting.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, {"path": "goose-analyzer.html", "template": "goose-analyzer.html", "language": "en", "alternates": {"en": "goose-analyzer.html", "id": "analyzer-goose-iec61850.html", "x-default": "goose-analyzer.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, {"path": "analyzer-goose-iec61850.html", "template": "analyzer-goose-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "goose-analyzer.html", "id": "analyzer-goose-iec61850.html", "x-default": "goose-analyzer.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, {"path": "file-transfer.html", "template": "file-transfer.html", "language": "en", "alternates": {"en": "file-transfer.html", "id": "transfer-file-comtrade-iec61850.html", "x-default": "file-transfer.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, {"path": "transfer-file-comtrade-iec61850.html", "template": "transfer-file-comtrade-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "file-transfer.html", "id": "transfer-file-comtrade-iec61850.html", "x-default": "file-transfer.html"}, "changefreq": "monthly", "priority": "0.88", "lastmod": "2026-07-20"}, - {"path": "scl-workspace.html", "template": "scl-workspace.html", "language": "en", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.96", "lastmod": "2026-07-27"}, - {"path": "workspace-scl-iec61850.html", "template": "workspace-scl-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-27"}, + {"path": "scl-workspace.html", "template": "scl-workspace.html", "language": "en", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.96", "lastmod": "2026-09-19"}, + {"path": "workspace-scl-iec61850.html", "template": "workspace-scl-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-09-19"}, {"path": "control.html", "template": "control.html", "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, {"path": "multi-ied-monitoring.html", "template": "multi-ied-monitoring.html", "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, {"path": "architecture.html", "template": "architecture.html", "changefreq": "monthly", "priority": "0.82", "lastmod": "2026-07-20"}, From 2310b4201267e75eddf3c69bdb77f544465ef8ef Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:18:32 +0700 Subject: [PATCH 26/80] docs: keep public landing copy implementation-neutral --- landing/templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/index.html b/landing/templates/index.html index 39bfbdec9..102df587d 100644 --- a/landing/templates/index.html +++ b/landing/templates/index.html @@ -53,7 +53,7 @@
Learn while you work

Every feature page should answer “what is it, when do I use it, and what can go wrong?”

The Learning Center connects protocol concepts to the actual ARSAS workflow, then links directly to troubleshooting when the expected result does not appear.

-
Project solutions

Use ARSAS across the IEC 61850 project lifecycle.

Move from learning and first connection to repeatable FAT, site validation, commissioning and multi-vendor integration.

Factory plan

IO List FAT Evidence

Execute imported SDI points and carry reviewable results to another workstation.

Factory engineering

General FAT Testing

Validate models, reports, GOOSE, control outcomes, file access and integration output.

Site

SAT and Commissioning

Reconfirm endpoints, reporting, station-bus behavior, fault records and command completion.

Integration

Multi-Vendor IEC 61850

Generate or normalize bounded SCL and isolate identity, DataSet, RCB and GOOSE mismatches.

+
Project solutions

Use ARSAS across the IEC 61850 project lifecycle.

Move from learning and first connection to repeatable FAT, site validation, commissioning and multi-device integration.

Factory plan

IO List FAT Evidence

Execute imported SDI points and carry reviewable results to another workstation.

Factory engineering

General FAT Testing

Validate models, reports, GOOSE, control outcomes, file access and integration output.

Site

SAT and Commissioning

Reconfirm endpoints, reporting, station-bus behavior, fault records and command completion.

Integration

Multi-Device IEC 61850

Generate or normalize bounded SCL and isolate identity, DataSet, RCB and GOOSE mismatches.

Real application evidence

Inspect the working Windows product—not a marketing mockup.

Current screenshots show independent IED sessions, live values, GOOSE supervision and diagnostics. The tutorials explain what to look for in each workspace.

ARSAS connected to multiple IEC 61850 IEDs

01Independent multi-IED sessions

Each device retains its own association, model, monitoring state and diagnostic history.

ARSAS multi-IED IEC 61850 live-value workspace

02Attributable live values

Device identity, reference, value, quality, timestamp and acquisition source remain together.

ARSAS IEC 61850 GOOSE analyzer

03GOOSE supervision

Inspect stream identity, sequence, retransmission and ordered payload evidence.

ARSAS IEC 61850 diagnostics workspace

04Copyable diagnostics

Keep failures attributable before changing the IED, network or project file.

From e6071f64abf28fc23c8610230e7676776ae445e7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:18:36 +0700 Subject: [PATCH 27/80] docs: keep public landing copy implementation-neutral --- landing/templates/id.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/id.html b/landing/templates/id.html index ed746827e..58ec8ec97 100644 --- a/landing/templates/id.html +++ b/landing/templates/id.html @@ -52,7 +52,7 @@
Belajar sambil bekerja

Setiap halaman fitur harus menjawab “apa ini, kapan digunakan, dan apa yang bisa gagal?”

Pusat Belajar menghubungkan konsep protokol dengan workflow ARSAS yang nyata, lalu mengarahkan langsung ke troubleshooting saat hasil yang diharapkan tidak muncul.

-
Solusi proyek

Gunakan ARSAS sepanjang lifecycle proyek IEC 61850.

Bergerak dari belajar dan koneksi pertama menuju FAT repeatable, validasi site, commissioning dan integrasi multi-vendor.

Factory plan

Evidence FAT IO List

Jalankan SDI hasil import dan bawa result yang dapat direview ke workstation lain.

Factory engineering

Pengujian FAT General

Validasi model, report, GOOSE, control outcome, file access dan integration output.

Site

SAT dan Commissioning

Konfirmasi endpoint, reporting, station-bus behavior, fault record dan command completion.

Integrasi

Multi-Vendor IEC 61850

Generate atau normalize SCL bounded dan isolasi mismatch identity, DataSet, RCB serta GOOSE.

+
Solusi proyek

Gunakan ARSAS sepanjang lifecycle proyek IEC 61850.

Bergerak dari belajar dan koneksi pertama menuju FAT repeatable, validasi site, commissioning dan integrasi multi-perangkat.

Factory plan

Evidence FAT IO List

Jalankan SDI hasil import dan bawa result yang dapat direview ke workstation lain.

Factory engineering

Pengujian FAT General

Validasi model, report, GOOSE, control outcome, file access dan integration output.

Site

SAT dan Commissioning

Konfirmasi endpoint, reporting, station-bus behavior, fault record dan command completion.

Integrasi

Multi-Perangkat IEC 61850

Generate atau normalize SCL bounded dan isolasi mismatch identity, DataSet, RCB serta GOOSE.

Evidence aplikasi nyata

Periksa produk Windows yang bekerja, bukan marketing mockup.

Screenshot publik menunjukkan session IED independen, live value, supervisi GOOSE dan diagnostic. Tutorial menjelaskan apa yang perlu diperhatikan pada setiap workspace.

ARSAS terhubung ke beberapa IED IEC 61850

Session multi-IED independen

Setiap device mempertahankan association, model dan diagnostic history sendiri.

Workspace live value multi-IED ARSAS

Live value attributable

Identity, reference, value, quality, timestamp dan acquisition source tetap bersama.

Analyzer GOOSE IEC 61850 ARSAS

Supervisi GOOSE

Periksa identity stream, sequence, retransmission dan ordered payload evidence.

Workspace diagnostik IEC 61850 ARSAS

Diagnostic yang dapat disalin

Pertahankan failure attributable sebelum mengubah IED, network atau project file.

From 4fc4c7afd05f320f4d6afaf599cf67b4daced13d Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:18:39 +0700 Subject: [PATCH 28/80] docs: keep public landing copy implementation-neutral --- landing/partials/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/partials/footer.html b/landing/partials/footer.html index 51d17d447..00d42f3ce 100644 --- a/landing/partials/footer.html +++ b/landing/partials/footer.html @@ -15,7 +15,7 @@
From d391f0787257294e9e02ea0beb8065e87519883f Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:18:54 +0700 Subject: [PATCH 29/80] docs: keep public landing copy implementation-neutral --- landing/templates/panduan.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/panduan.html b/landing/templates/panduan.html index 0dca80827..5f21047ec 100644 --- a/landing/templates/panduan.html +++ b/landing/templates/panduan.html @@ -50,7 +50,7 @@
Workflow produk dalam Bahasa Indonesia

Lanjutkan dengan konteks capability yang tepat.

-
Batas engineering

Gunakan evidence untuk mempersempit penyebab.

Kesimpulan akhir tetap bergantung pada approved design, vendor documentation, test procedure, network access dan independent verification.

+
Batas engineering

Gunakan evidence untuk mempersempit penyebab.

Kesimpulan akhir tetap bergantung pada approved design, device documentation, test procedure, network access dan independent verification.

{{> download-cta-id}} {{> footer}} From e337969cc9e12823aeb11403b4687d297af4fda6 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:18:57 +0700 Subject: [PATCH 30/80] docs: keep public landing copy implementation-neutral --- landing/templates/compatibility.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/landing/templates/compatibility.html b/landing/templates/compatibility.html index cd23938f7..59417a514 100644 --- a/landing/templates/compatibility.html +++ b/landing/templates/compatibility.html @@ -4,7 +4,7 @@ ARSAS IEC 61850 Compatibility Evidence — Service-Level Matrix - + @@ -19,7 +19,7 @@ - + @@ -34,17 +34,17 @@ {{> header}}
-
Status · conditions · evidence date · public record

Compatibility is a service-level evidence claim—not a vendor logo.

ARSAS publishes only bounded evidence that can be tied to a protocol behavior, application version, condition and public engineering record. There are currently no publicly named vendor/model entries.

+
Status · conditions · evidence date · public record

Compatibility is a service-level evidence claim—not a device implementation logo.

ARSAS publishes only bounded evidence that can be tied to a protocol behavior, application version, condition and public engineering record. There are currently no publicly named device implementation/model entries.

Status vocabulary

Read the status together with its conditions.

VerifiedConditionalObservedNot testedFailed with known issue

“Verified” means repeated evidence exists for the declared service scope. It is not IEC 61850 conformance certification and does not approve every firmware, Logical Device or project configuration.

-
Evidence date · 2026-07

Anonymized field profile A — MMS file-service transport

Vendor, model, installation and firmware are not published. The public evidence concerns protocol behavior, not product endorsement.

ServiceStatus
MMS associationObserved
FileDirectoryVerified
File transferConditional
Live model, reporting, GOOSE, controlNot tested

Conditions

  • Nested FileDirectory entries and rooted file paths were observed.
  • FileOpen could return a signed Integer32 FRSM, including negative values.
  • Large FileRead responses required segmented COTP reassembly.
  • Transfer success does not prove record completeness or COMTRADE scaling.
+
Evidence date · 2026-07

Anonymized field profile A — MMS file-service transport

device implementation, model, installation and firmware are not published. The public evidence concerns protocol behavior, not product endorsement.

ServiceStatus
MMS associationObserved
FileDirectoryVerified
File transferConditional
Live model, reporting, GOOSE, controlNot tested

Conditions

  • Nested FileDirectory entries and rooted file paths were observed.
  • FileOpen could return a signed Integer32 FRSM, including negative values.
  • Large FileRead responses required segmented COTP reassembly.
  • Transfer success does not prove record completeness or COMTRADE scaling.
-
Evidence date · 2026-07

Anonymized field profile B — RCB and SCL export evidence

Vendor, model, installation and firmware are not published. Evidence remains read-only until an approved export action is requested.

ServiceStatus
MMS association and live modelObserved
Reporting evidenceConditional
Selected-RCB exportVerified
Files, GOOSE, controlNot tested

Conditions

  • Live RCB names, TriggerOptions, OptionalFields, BufTm and IntgPd were preserved.
  • Selected-RCB export retained the exact live MMS RCB identity.
  • Exported SCL remains an engineering baseline requiring independent validation.
  • No runtime RCB reservation or write is implied by this profile.
+
Evidence date · 2026-07

Anonymized field profile B — RCB and SCL export evidence

device implementation, model, installation and firmware are not published. Evidence remains read-only until an approved export action is requested.

ServiceStatus
MMS association and live modelObserved
Reporting evidenceConditional
Selected-RCB exportVerified
Files, GOOSE, controlNot tested

Conditions

  • Live RCB names, TriggerOptions, OptionalFields, BufTm and IntgPd were preserved.
  • Selected-RCB export retained the exact live MMS RCB identity.
  • Exported SCL remains an engineering baseline requiring independent validation.
  • No runtime RCB reservation or write is implied by this profile.
-
Machine-readable source

Evidence profiles are governed in JSON.

The public device-evidence.json registry contains the same profile IDs, service statuses, conditions and public links shown here. CI rejects unsupported status values, missing links or a named-device claim without explicit evidence metadata.

Contribute a new profile

Report the service behavior—not only the brand.

Provide the ARSAS version, operating system, service under test, sanitized diagnostics, firmware when disclosure is allowed and a reproducible result. Confidential identifiers and credentials must be removed.

+
Machine-readable source

Evidence profiles are governed in JSON.

The public device-evidence.json registry contains the same profile IDs, service statuses, conditions and public links shown here. CI rejects unsupported status values, missing links or a identified-device claim without explicit evidence metadata.

Contribute a new profile

Report the service behavior—not only the product label.

Provide the ARSAS version, operating system, service under test, sanitized diagnostics, firmware when disclosure is allowed and a reproducible result. Confidential identifiers and credentials must be removed.

{{> download-cta}}
From 5e1be6c7a660342838fcc3f52ef5b98dab808831 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:19:00 +0700 Subject: [PATCH 31/80] docs: keep public landing copy implementation-neutral --- landing/templates/bukti-kompatibilitas.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/landing/templates/bukti-kompatibilitas.html b/landing/templates/bukti-kompatibilitas.html index 6e99cb3fd..211a0f603 100644 --- a/landing/templates/bukti-kompatibilitas.html +++ b/landing/templates/bukti-kompatibilitas.html @@ -4,7 +4,7 @@ Evidence Kompatibilitas ARSAS IEC 61850 — Matrix per Service - + @@ -19,7 +19,7 @@ - + @@ -34,17 +34,17 @@ {{> header}}
-
Status · kondisi · tanggal evidence · public record

Compatibility adalah klaim evidence per service—bukan sekadar logo vendor.

ARSAS hanya mempublikasikan evidence yang dapat dikaitkan dengan behavior protokol, versi aplikasi, kondisi dan engineering record publik. Saat ini belum ada entry vendor/model yang dipublikasikan dengan nama.

+
Status · kondisi · tanggal evidence · public record

Compatibility adalah klaim evidence per service—bukan sekadar label produk.

ARSAS hanya mempublikasikan evidence yang dapat dikaitkan dengan behavior protokol, versi aplikasi, kondisi dan engineering record publik. Saat ini belum ada entry implementasi perangkat/model yang dipublikasikan dengan nama.

Status vocabulary

Baca status bersama kondisi pengujiannya.

VerifiedConditionalObservedNot testedFailed with known issue

“Verified” berarti ada repeated evidence untuk scope service yang dinyatakan. Status ini bukan sertifikasi conformity IEC 61850 dan tidak mengesahkan seluruh firmware, Logical Device atau konfigurasi proyek.

-
Tanggal evidence · 2026-07

Profile field anonim A — transport MMS file service

Vendor, model, instalasi dan firmware tidak dipublikasikan. Evidence publik membahas behavior protokol, bukan endorsement produk.

ServiceStatus
MMS associationObserved
FileDirectoryVerified
File transferConditional
Live model, reporting, GOOSE, controlNot tested

Kondisi

  • Nested FileDirectory entry dan rooted file path teramati.
  • FileOpen dapat mengembalikan signed Integer32 FRSM termasuk nilai negatif.
  • Respons FileRead besar memerlukan segmented COTP reassembly.
  • Transfer berhasil tidak membuktikan record lengkap atau scaling COMTRADE.
+
Tanggal evidence · 2026-07

Profile field anonim A — transport MMS file service

implementasi perangkat, model, instalasi dan firmware tidak dipublikasikan. Evidence publik membahas behavior protokol, bukan endorsement produk.

ServiceStatus
MMS associationObserved
FileDirectoryVerified
File transferConditional
Live model, reporting, GOOSE, controlNot tested

Kondisi

  • Nested FileDirectory entry dan rooted file path teramati.
  • FileOpen dapat mengembalikan signed Integer32 FRSM termasuk nilai negatif.
  • Respons FileRead besar memerlukan segmented COTP reassembly.
  • Transfer berhasil tidak membuktikan record lengkap atau scaling COMTRADE.
-
Tanggal evidence · 2026-07

Profile field anonim B — evidence RCB dan export SCL

Vendor, model, instalasi dan firmware tidak dipublikasikan. Evidence tetap read-only sampai action export yang disetujui dilakukan.

ServiceStatus
MMS association dan live modelObserved
Evidence reportingConditional
Selected-RCB exportVerified
File, GOOSE, controlNot tested

Kondisi

  • Nama RCB live, TriggerOptions, OptionalFields, BufTm dan IntgPd dipertahankan.
  • Export selected-RCB mempertahankan exact identity MMS RCB live.
  • SCL hasil export tetap baseline engineering yang membutuhkan independent validation.
  • Profile ini tidak menyiratkan runtime RCB reservation atau write.
+
Tanggal evidence · 2026-07

Profile field anonim B — evidence RCB dan export SCL

implementasi perangkat, model, instalasi dan firmware tidak dipublikasikan. Evidence tetap read-only sampai action export yang disetujui dilakukan.

ServiceStatus
MMS association dan live modelObserved
Evidence reportingConditional
Selected-RCB exportVerified
File, GOOSE, controlNot tested

Kondisi

  • Nama RCB live, TriggerOptions, OptionalFields, BufTm dan IntgPd dipertahankan.
  • Export selected-RCB mempertahankan exact identity MMS RCB live.
  • SCL hasil export tetap baseline engineering yang membutuhkan independent validation.
  • Profile ini tidak menyiratkan runtime RCB reservation atau write.
-
Machine-readable source

Profile evidence di-govern melalui JSON.

Registry publik device-evidence.json memuat profile ID, status service, kondisi dan public link yang sama dengan halaman ini. CI menolak status tidak dikenal, link hilang atau named-device claim tanpa metadata evidence.

Kontribusi profile baru

Laporkan behavior service, bukan hanya merek.

Sertakan versi ARSAS, operating system, service yang diuji, diagnostic sanitized, firmware bila boleh dibuka dan hasil yang reproducible. Credential serta identifier rahasia harus dihapus.

+
Machine-readable source

Profile evidence di-govern melalui JSON.

Registry publik device-evidence.json memuat profile ID, status service, kondisi dan public link yang sama dengan halaman ini. CI menolak status tidak dikenal, link hilang atau identified-device claim tanpa metadata evidence.

Kontribusi profile baru

Laporkan behavior service, bukan hanya label produk.

Sertakan versi ARSAS, operating system, service yang diuji, diagnostic sanitized, firmware bila boleh dibuka dan hasil yang reproducible. Credential serta identifier rahasia harus dihapus.

{{> download-cta-id}}
From 2a2dc4c07431de351b4f12bc08ecb13c71a073c7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:19:16 +0700 Subject: [PATCH 32/80] docs: keep public guidance implementation-neutral --- landing/templates/solutions.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/landing/templates/solutions.html b/landing/templates/solutions.html index 957ae0b1e..53d8b0b90 100644 --- a/landing/templates/solutions.html +++ b/landing/templates/solutions.html @@ -4,7 +4,7 @@ IEC 61850 IO FAT, SAT, Commissioning and Integration Solutions | ARSAS - + @@ -27,7 +27,7 @@ - + @@ -35,16 +35,16 @@ {{> header}}
IO List FAT · general FAT · SAT · commissioning · integration

IEC 61850 workflows that turn approved inputs into project evidence and reusable output.

Start from an approved ARSAS IO List when a digital-indication test plan already exists, or start from an endpoint/SCL for general engineering. ARSAS keeps device identity, live behavior, evidence and bounded project output connected.

-
Start from the IO ListStrict SDI plan, one-IED execution and ordered transition evidence
Start from the endpointComplete live model discovery without requiring usable vendor SCL
Export evidenceExcel result, native PDF and verified portable .arsas project
Carry context forwardFAT, SAT, commissioning and integration remain attributable per IED
+
Start from the IO ListStrict SDI plan, one-IED execution and ordered transition evidence
Start from the endpointComplete live model discovery without requiring usable device SCL
Export evidenceExcel result, native PDF and verified portable .arsas project
Carry context forwardFAT, SAT, commissioning and integration remain attributable per IED
Project workflows

Choose the starting condition and stage you are preparing for.

The IO List workspace is a read-only test-plan layer for imported SDI points. General FAT and engineering workflows continue to cover discovery, reporting, GOOSE, files, SCL and guarded control.

Factory test plan

IO List FAT evidence

Import approved digital indications, bind to the intended IED, require OFF → ON → OFF, and export Excel, native PDF or a resumable .arsas project.

Factory engineering

General IEC 61850 FAT testing

Validate the complete live model, selected signals, reports, GOOSE, control outcomes, disturbance-record access and integration output before shipment.

Site

IEC 61850 SAT testing

Reconfirm endpoint access, reporting, station-bus behavior, generated or supplied SCL and downstream system integration after installation.

Energization

IEC 61850 commissioning

Investigate live behavior, sequence evidence, communication quality, file services and command completion during controlled commissioning.

-
Integration

Multi-vendor interoperability

Recover from unusable vendor files, generate or normalize bounded SCL and isolate identity, DataSet, RCB, reporting and GOOSE mismatches.

+
Integration

Multi-device interoperability

Recover from unusable engineering files, generate or normalize bounded SCL and isolate identity, DataSet, RCB, reporting and GOOSE mismatches.

ARSAS multi-IED IEC 61850 engineering workspace for FAT SAT and commissioning

One product, two controlled starting paths

Use General IEC 61850 Testing for endpoint-led engineering. Use FAT / IO List Testing for imported SDI plans and durable transition evidence. Both retain the originating IED and explicit evidence boundaries.

-
Practical value

Start from the approved evidence source.

Use an IO List for repeatable indication proof, or use the endpoint and SCL context to investigate broader IEC 61850 services. Preserve what happened and what still requires review.

Engineering boundary

ARSAS supports the test process.

It does not replace approved procedures, calibrated test equipment, protection-setting verification, switching authority, witness acceptance, formal signatures, target-system import validation or vendor responsibility.

+
Practical value

Start from the approved evidence source.

Use an IO List for repeatable indication proof, or use the endpoint and SCL context to investigate broader IEC 61850 services. Preserve what happened and what still requires review.

Engineering boundary

ARSAS supports the test process.

It does not replace approved procedures, calibrated test equipment, protection-setting verification, switching authority, witness acceptance, formal signatures, target-system import validation or device-owner responsibility.

{{> download-cta}}
{{> footer}} From 1c9329cf7255d86cfe2e196c9b4a812cfc7b14f7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:19:20 +0700 Subject: [PATCH 33/80] docs: keep public guidance implementation-neutral --- landing/templates/faq.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/faq.html b/landing/templates/faq.html index 9d6a1e01b..23b919606 100644 --- a/landing/templates/faq.html +++ b/landing/templates/faq.html @@ -29,7 +29,7 @@ - + {{> header}} @@ -42,7 +42,7 @@
Why can Windows SmartScreen show a warning?
The current public Windows binaries are not Authenticode-signed. Verify the exact SHA-256 published in the Download Center before use. The warning is not hidden or presented as proof of malware.
What is the difference between Installer and Portable ZIP?
The IEC 61850 feature scope is the same. Installer provides Windows registration, Start menu and uninstall behavior. Portable ZIP runs from an approved writable folder and is updated by replacing the package.
Is Npcap required?
Npcap is required for raw-Ethernet GOOSE and Sampled Values capture. Ordinary MMS association, model discovery, reads, reporting, SCL generation and file-service workflows do not inherently require Npcap.
-
Does ARSAS support every IEC 61850 vendor?
No universal vendor claim is made. Generated SCL is vendor-neutral, typed and schema-aware, but compatibility is service-specific and proprietary importers may add rules beyond the standard. Validate every receiving system.
+
Does ARSAS support every IEC 61850 implementation?
No universal implementation claim is made. Generated SCL is implementation-neutral, typed and schema-aware, but compatibility is service-specific and proprietary importers may add rules beyond the standard. Validate every receiving system.
Can ARSAS create or write DataSets and RCBs?
Existing configuration and read-only evidence are preferred. Specific recovery workflows may use writes only where supported, enabled and authorized. A successful write is not proof that the project configuration is correct.
Can ARSAS operate control objects?
Guarded Direct and Select-Before-Operate workflows exist. Active control requires an approved procedure, plant authority, correct interlocks and independent safety checks. Never use a software button as the sole authorization.
Is ARSAS safe for an operational network?
No software alone can declare an operational network safe. Use ARSAS only inside an approved laboratory, FAT, SAT or commissioning boundary with controlled routing, permissions and recovery procedures.
From a2d2053b7d1517b39c80453f81922c802e5ded99 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:19:24 +0700 Subject: [PATCH 34/80] docs: keep public guidance implementation-neutral --- landing/templates/faq-arsas.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/faq-arsas.html b/landing/templates/faq-arsas.html index da4053efa..8c8fbf596 100644 --- a/landing/templates/faq-arsas.html +++ b/landing/templates/faq-arsas.html @@ -29,7 +29,7 @@ - + {{> header}} @@ -42,7 +42,7 @@
Mengapa Windows SmartScreen dapat memberi warning?
Binary Windows publik saat ini belum Authenticode-signed. Cocokkan exact SHA-256 di Download Center sebelum digunakan. Warning tidak disembunyikan dan juga bukan bukti otomatis malware.
Apa perbedaan Installer dan Portable ZIP?
Scope fitur IEC 61850 sama. Installer memberi registrasi Windows, Start menu dan uninstall. Portable berjalan dari folder writable yang disetujui dan diperbarui dengan mengganti package.
Apakah Npcap wajib?
Npcap diperlukan untuk capture raw-Ethernet GOOSE dan Sampled Values. Association MMS, model discovery, read, reporting, generate SCL dan file service biasa tidak secara inheren memerlukan Npcap.
-
Apakah ARSAS mendukung semua vendor IEC 61850?
Tidak ada klaim universal vendor. Generated SCL bersifat vendor-neutral, typed dan schema-aware, tetapi compatibility tetap service-specific dan proprietary importer dapat memiliki rule di luar standard. Setiap receiving system harus divalidasi.
+
Apakah ARSAS mendukung semua implementasi IEC 61850?
Tidak ada klaim universal untuk seluruh implementasi. Generated SCL bersifat implementation-neutral, typed dan schema-aware, tetapi compatibility tetap service-specific dan proprietary importer dapat memiliki rule di luar standard. Setiap receiving system harus divalidasi.
Apakah ARSAS dapat membuat atau menulis DataSet dan RCB?
Konfigurasi existing dan evidence read-only diprioritaskan. Workflow recovery tertentu hanya memakai write bila supported, enabled dan authorized. Write berhasil tidak membuktikan konfigurasi proyek benar.
Apakah ARSAS dapat melakukan control?
Workflow Direct dan Select-Before-Operate tersedia secara guarded. Active control memerlukan procedure, plant authority, interlock yang benar dan independent safety check.
Apakah ARSAS aman untuk jaringan operasional?
Tidak ada software yang sendirian dapat menyatakan jaringan aman. Gunakan hanya dalam boundary laboratorium, FAT, SAT atau commissioning yang disetujui dengan routing, permission dan recovery procedure terkontrol.
From 7038475d8a869d15ff2381bd430ac77fdbd1862a Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:19:57 +0700 Subject: [PATCH 35/80] chore: remove superseded public artifact --- .../workflows/iedscout-convergence-guard.yml | 325 ------------------ 1 file changed, 325 deletions(-) delete mode 100644 .github/workflows/iedscout-convergence-guard.yml diff --git a/.github/workflows/iedscout-convergence-guard.yml b/.github/workflows/iedscout-convergence-guard.yml deleted file mode 100644 index 9e977475f..000000000 --- a/.github/workflows/iedscout-convergence-guard.yml +++ /dev/null @@ -1,325 +0,0 @@ -name: IEDScout Convergence Guard - -on: - pull_request: - paths: - - "Services/NativeIec61850Client*.cs" - - "Services/SclAssistedConnectionPreparation.cs" - - "Services/CanonicalSclReloadValidator.cs" - - "MainWindow.xaml.cs" - - "Directory.Build.targets" - - "scripts/enable-smart-discovery-capture.ps1" - - "engines/ARIEC61850.lock.json" - - "evidence/iedscout-convergence-target.json" - - "docs/IEDSCOUT_CONVERGENCE.md" - - "tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs" - - ".github/workflows/iedscout-convergence-guard.yml" - - ".github/workflows/scl-interoperability-r7.yml" - workflow_dispatch: - -concurrency: - group: iedscout-convergence-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - iedscout-convergence-contract: - name: iedscout-convergence-contract - runs-on: windows-latest - - steps: - - name: Checkout exact ARSAS revision - shell: pwsh - env: - ARSAS_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - run: | - if ($env:ARSAS_SOURCE_SHA -notmatch '^[0-9a-f]{40}$') { - throw "Invalid ARSAS source SHA: $env:ARSAS_SOURCE_SHA" - } - git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:GITHUB_REPOSITORY.git" ARSAS - git -C .\ARSAS fetch --quiet --depth 1 origin $env:ARSAS_SOURCE_SHA - git -C .\ARSAS checkout --quiet --detach $env:ARSAS_SOURCE_SHA - $actual = (git -C .\ARSAS rev-parse HEAD).Trim() - if ($actual -ne $env:ARSAS_SOURCE_SHA) { - throw "ARSAS SHA mismatch. Expected $env:ARSAS_SOURCE_SHA, got $actual." - } - - - name: Verify single convergence authority - shell: pwsh - run: | - $lock = Get-Content .\ARSAS\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json - $target = Get-Content .\ARSAS\evidence\iedscout-convergence-target.json -Raw | ConvertFrom-Json - - if ($target.status -ne 'physical-retest-passed-merge-ready') { - throw "Convergence status changed unexpectedly: $($target.status)" - } - if ([int]$target.activeStack.enginePerformance.pullRequest -ne 134 -or - [int]$target.activeStack.engineModelAndScl.pullRequest -ne 135 -or - [int]$target.activeStack.engineModelAndScl.basePullRequest -ne 134 -or - [int]$target.activeStack.consumerIntegration.pullRequest -ne 324) { - throw 'Merged engine authority must remain PR #134 + PR #135 with ARSAS #324 provenance.' - } - if ($lock.commit -ne $target.activeStack.engineModelAndScl.head -or - [int]$lock.sourcePullRequest -ne 135) { - throw 'Engine lock drifted from the convergence single source of truth.' - } - if ([bool]$target.promotion.productionPromoted -or - [bool]$target.promotion.mergeAllowedBeforePhysicalRetest -or - [bool]$target.promotion.physicalRetestRequired -or - -not [bool]$target.promotion.mergeAllowedAfterPhysicalRetest -or - -not [bool]$target.promotion.physicalRetestPassed) { - throw 'R10 physical-retest/merge gate drifted.' - } - if ([int]$target.physicalReference.iedScoutCapture.associations -ne 1 -or - [int]$target.physicalReference.iedScoutCapture.getVariableAccessAttributes -ne 119 -or - [int]$target.physicalReference.rejectedLegacyArsasCapture.associations -ne 2 -or - [int]$target.physicalReference.rejectedLegacyArsasCapture.getVariableAccessAttributes -lt 20000) { - throw 'Physical IEDScout/legacy regression reference changed unexpectedly.' - } - - - $p0 = $target.p0StructuralDiscoveryFreeze - if ($p0.contractId -ne 'P0-R9-STRUCTURAL' -or - $p0.status -ne 'implemented-and-r9-physically-proven' -or - -not [bool]$p0.sourcePolicy.smartRouteRequired -or - -not [bool]$p0.sourcePolicy.associationScopedSingleFlightRequired -or - -not [bool]$p0.sourcePolicy.applicationMmsGateExclusive -or - -not [bool]$p0.sourcePolicy.logicalNodeRootTypeStrategyRequired -or - -not [bool]$p0.sourcePolicy.eagerInitialFcReadsForbidden -or - -not [bool]$p0.sourcePolicy.supplementalLegacyBrowseForbidden -or - -not [bool]$p0.sourcePolicy.secondFullGetNameListSweepForbidden -or - -not [bool]$p0.sourcePolicy.recursivePerLeafGvaStormForbidden -or - -not [bool]$p0.sourcePolicy.saveTimeAndTrustedSclValueReadsRemainSeparate) { - throw 'P0 structural-discovery freeze policy was weakened.' - } - - if ([int]$p0.frozenOptions.maxConcurrentChains -ne 8 -or - [int]$p0.frozenOptions.unknownPeerMaxConcurrentChains -ne 4 -or - [int]$p0.frozenOptions.maxDomains -ne 256 -or - [int]$p0.frozenOptions.maxVariableNamesPerDomain -ne 20000 -or - [int]$p0.frozenOptions.maxVariableListNamesPerDomain -ne 4096 -or - [int]$p0.frozenOptions.maxNameListPages -ne 64 -or - -not [bool]$p0.frozenOptions.probeReportAttributes -or - [int]$p0.frozenOptions.maxReportAttributeProbes -ne 64 -or - -not [bool]$p0.frozenOptions.readDataSetDirectories -or - [int]$p0.frozenOptions.maxDataSetDirectoryReads -ne 64) { - throw 'P0 frozen smart-discovery options drifted.' - } - - $p0Relay = $p0.aa1e1f06r4Acceptance - if ([int]$p0Relay.associations -ne 1 -or - [int]$p0Relay.referenceConfirmedMmsRequests -ne 323 -or - [int]$p0Relay.maximumConfirmedMmsRequests -ne 417 -or - [int]$p0Relay.referenceGetNameList -ne 138 -or - [int]$p0Relay.referenceGetVariableAccessAttributes -ne 119 -or - [int]$p0Relay.maximumGetVariableAccessAttributes -ne 119 -or - [int]$p0Relay.referenceGetNamedVariableListAttributes -ne 2 -or - [int]$p0Relay.referenceReads -ne 64 -or - [int]$p0Relay.maximumReads -ne 156 -or - [int]$p0Relay.logicalDevices -ne 32 -or - [int]$p0Relay.logicalNodes -ne 119 -or - [int]$p0Relay.topLevelDataObjects -ne 860 -or - [int]$p0Relay.dataObjectsIncludingSdo -ne 906 -or - [int]$p0Relay.scalarLeaves -ne 4925 -or - [int]$p0Relay.dataSets -ne 2 -or - [int]$p0Relay.fcda -ne 58 -or - [int]$p0Relay.logicalReportControls -ne 32 -or - [int]$p0Relay.settingControls -ne 1) { - throw 'P0 AA1E1F06R4 discovery acceptance contract drifted.' - } - - $p1 = $target.p1ProjectionOrderRepair - if ($p1.contractId -ne 'P1-CF-DO-SCOPED' -or - $p1.status -ne 'physically-proven-r10' -or - [int]$p1.physicalR10Outcome.edition2.projectionErrors -ne 0 -or - [int]$p1.physicalR10Outcome.edition1.projectionErrors -ne 0 -or - [int]$p1.physicalR10Outcome.edition2.failedReads -ne 0 -or - [int]$p1.physicalR10Outcome.edition1.failedReads -ne 0 -or - [int]$p1.physicalR10Outcome.reportBackedRuntimePoints -ne 58 -or - [int]$p1.physicalR10Outcome.unresolvedRuntimePoints -ne 0 -or - -not [bool]$p1.physicalR10Outcome.actualInformationReportObserved) { - throw 'P1 physical projection-order proof regressed.' - } - - $p2 = $target.p2CaseSensitiveValuePipeline - if ($p2.contractId -ne 'P2-CASE-EXACT-VALUES' -or - $p2.status -ne 'physically-proven-r10' -or - -not [bool]$p2.preservesP0StructuralDiscovery -or - -not [bool]$p2.preservesP1CfScopedHydration -or - [int]$p2.physicalR9Evidence.observedCacheLoss -ne 11 -or - [int]$p2.expectedPhysicalOutcome.edition2CacheLoss -ne 0 -or - [int]$p2.expectedPhysicalOutcome.edition1CacheLoss -ne 0 -or - -not [bool]$p2.expectedPhysicalOutcome.ltrkLowercaseTAndUppercaseTRemainDistinct -or - -not [bool]$p2.expectedPhysicalOutcome.noAdditionalDiscoveryGva -or - -not [bool]$p2.expectedPhysicalOutcome.sameAssociation -or - [int]$p2.physicalR10Outcome.edition2.cacheLoss -ne 0 -or - [int]$p2.physicalR10Outcome.edition1.cacheLoss -ne 0 -or - [int]$p2.physicalR10Outcome.edition2.projectedUniqueValues -ne [int]$p2.physicalR10Outcome.edition2.initialValueCache -or - [int]$p2.physicalR10Outcome.edition1.projectedUniqueValues -ne [int]$p2.physicalR10Outcome.edition1.initialValueCache -or - $p2.sourceInvariants.trustedSclValueCacheComparer -ne 'StringComparer.Ordinal' -or - $p2.sourceInvariants.typeSpecificationMemberComparison -ne 'StringComparison.Ordinal' -or - $p2.sourceInvariants.sclDataObjectInstanceTargetComparison -ne 'StringComparison.Ordinal') { - throw 'P2 exact-case value pipeline contract was weakened.' - } - - $r10 = $target.physicalEvidence.arsasR10 - if ($r10.testedArtifact.appHead -ne 'eb8eb13d491f9aa265205852b8a4bab07af440ff' -or - $r10.testedArtifact.engineTestedHead -ne '9935d6902d786cc69b299260fe36b835944d5e81' -or - $r10.testedArtifact.engineMergedMain -ne '648124097621046f5f127ceb1cf853fea54db730' -or - [int]$r10.discovery.associations -ne 1 -or - [int]$r10.discovery.confirmedMmsRequests -ne 323 -or - [int]$r10.discovery.getVariableAccessAttributes -ne 119 -or - [int]$r10.reuseEdition2.matchedDomains -ne 32 -or - [int]$r10.reuseEdition2.initialTargets -ne 709 -or - [int]$r10.reuseEdition2.successfulReads -ne 709 -or - [int]$r10.reuseEdition2.failedReads -ne 0 -or - [int]$r10.reuseEdition2.projectionErrors -ne 0 -or - [int]$r10.reuseEdition2.cacheLoss -ne 0 -or - [int]$r10.reuseEdition2.reportBackedRuntimePoints -ne 58 -or - [int]$r10.reuseEdition2.unresolvedRuntimePoints -ne 0 -or - -not [bool]$r10.reuseEdition2.actualInformationReportObserved -or - [int]$r10.reuseEdition1.matchedDomains -ne 32 -or - [int]$r10.reuseEdition1.initialTargets -ne 708 -or - [int]$r10.reuseEdition1.successfulReads -ne 708 -or - [int]$r10.reuseEdition1.failedReads -ne 0 -or - [int]$r10.reuseEdition1.projectionErrors -ne 0 -or - [int]$r10.reuseEdition1.cacheLoss -ne 0 -or - [int]$r10.reuseEdition1.reportBackedRuntimePoints -ne 58 -or - [int]$r10.reuseEdition1.unresolvedRuntimePoints -ne 0 -or - -not [bool]$r10.reuseEdition1.actualInformationReportObserved) { - throw 'R10 physical SCL reuse acceptance evidence regressed.' - } - - $r9 = $target.physicalEvidence.arsasR9 - if ([int]$r9.discovery.associations -ne 1 -or - [int]$r9.discovery.confirmedMmsRequests -ne 323 -or - [int]$r9.discovery.getVariableAccessAttributes -ne 119 -or - [int]$r9.discovery.topLevelDataObjects -ne 860 -or - [int]$r9.discovery.dataObjectsIncludingSdo -ne 906 -or - [int]$r9.discovery.scalarLeaves -ne 4925) { - throw 'R9 physical discovery/model acceptance evidence drifted.' - } - - if ([int]$r9.reuseEdition2.expectedDomains -ne 32 -or - [int]$r9.reuseEdition2.matchedDomains -ne 32 -or - [int]$r9.reuseEdition2.successfulReads -ne [int]$r9.reuseEdition2.fcRoots -or - [int]$r9.reuseEdition2.failedReads -ne 0 -or - [int]$r9.reuseEdition2.reportBackedRuntimePoints -ne 58 -or - [int]$r9.reuseEdition2.unresolvedRuntimePoints -ne 0 -or - -not [bool]$r9.reuseEdition2.actualInformationReportObserved -or - [int]$r9.reuseEdition1.expectedDomains -ne 32 -or - [int]$r9.reuseEdition1.matchedDomains -ne 32 -or - [int]$r9.reuseEdition1.successfulReads -ne [int]$r9.reuseEdition1.fcRoots -or - [int]$r9.reuseEdition1.failedReads -ne 0 -or - [int]$r9.reuseEdition1.reportBackedRuntimePoints -ne 58 -or - [int]$r9.reuseEdition1.unresolvedRuntimePoints -ne 0 -or - -not [bool]$r9.reuseEdition1.actualInformationReportObserved) { - throw 'R9 trusted-SCL reconnect/reporting acceptance evidence regressed.' - } - - if ([int]$target.openGaps.trustedSclProjectionParity.targetProjectionErrors -ne 0 -or - [int]$target.openGaps.trustedSclProjectionParity.observedEdition2 -ne [int]$r9.reuseEdition2.projectionErrors -or - [int]$target.openGaps.trustedSclProjectionParity.observedEdition1 -ne [int]$r9.reuseEdition1.projectionErrors -or - [int]$target.openGaps.trustedSclProjectionParity.r10ObservedEdition2 -ne 0 -or - [int]$target.openGaps.trustedSclProjectionParity.r10ObservedEdition1 -ne 0 -or - [int]$target.openGaps.edition2CaseDistinctInitialValueCache.observedProjectedMinusCached -ne 11 -or - [int]$target.openGaps.edition2CaseDistinctInitialValueCache.r10ObservedProjectedMinusCached -ne 0 -or - [int]$target.openGaps.preallocatedAddReportControls.observedWithoutDataSet -ne 30 -or - [int]$target.openGaps.preallocatedAddReportControls.r10FatalMissingDatasetErrors -ne 0) { - throw 'R9 history or R10 resolved semantic evidence drifted.' - } - - "ENGINE_REPOSITORY=$($lock.repository)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append - "ENGINE_COMMIT=$($lock.commit)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append - - - name: Checkout exact convergence engine - shell: pwsh - run: | - git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:ENGINE_REPOSITORY.git" ARIEC61850 - git -C .\ARIEC61850 fetch --quiet --depth 1 origin $env:ENGINE_COMMIT - git -C .\ARIEC61850 checkout --quiet --detach $env:ENGINE_COMMIT - $actual = (git -C .\ARIEC61850 rev-parse HEAD).Trim() - if ($actual -ne $env:ENGINE_COMMIT) { - throw "Engine SHA mismatch. Expected $env:ENGINE_COMMIT, got $actual." - } - - - name: Verify discovery and SCL anti-regression contracts - shell: pwsh - run: | - $capture = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryCapture.cs -Raw - $sclAssisted = Get-Content .\ARSAS\Services\NativeIec61850Client.SclAssisted.cs -Raw - $canonicalModel = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\LiveIedCanonicalModel.cs -Raw - $typeHierarchy = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\LiveIedVariableTypeHierarchy.cs -Raw - $canonicalExporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\CanonicalLiveIedSclExporter.cs -Raw - $targets = Get-Content .\ARSAS\Directory.Build.targets -Raw - $smart = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscovery.cs -Raw - $singleFlight = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscoverySingleFlight.cs -Raw - $smartGva = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartVariableAccessAttributes.cs -Raw - $lnParts = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850ReferenceParts.cs -Raw - $exporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs -Raw - $registry = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850StandardModelRegistry.cs -Raw - $cdc = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\CdcInferenceEngine.cs -Raw - - $valueCacheIndex = $sclAssisted.IndexOf('_trustedSclInitialValues =', [System.StringComparison]::Ordinal) - if ($valueCacheIndex -lt 0) { - throw 'P2 trusted-SCL value cache declaration is missing.' - } - $valueCacheLength = [Math]::Min(260, $sclAssisted.Length - $valueCacheIndex) - $valueCacheSegment = $sclAssisted.Substring($valueCacheIndex, $valueCacheLength) - if ($valueCacheSegment -notmatch 'StringComparer\.Ordinal' -or - $valueCacheSegment -match 'StringComparer\.OrdinalIgnoreCase' -or - $sclAssisted -notmatch 'projectedUniqueValues' -or - $sclAssisted -notmatch 'initialValueCacheLoss' -or - $sclAssisted -notmatch 'cacheLoss=\{initialValueCacheLoss\}' -or - $canonicalModel -notmatch 'new HashSet\(StringComparer\.Ordinal\)' -or - $typeHierarchy -notmatch 'current\.Children\[index\]\.Name, part, StringComparison\.Ordinal\)' -or - $canonicalExporter -notmatch 'dataObjectName\.Trim\(\),\s*[\s\S]*?StringComparison\.Ordinal\)') { - throw 'P2 case-sensitive value path implementation regressed.' - } - - if ($targets -notmatch 'SCL Interoperability R7 Build' -or - $targets -notmatch 'EnableSmartDiscoveryCaptureRoute' -or - $capture -notmatch 'SmartDiscoveryStructuralFreezeContract = "P0-R9-STRUCTURAL"' -or - $capture -notmatch 'CreateP0FrozenSmartDiscoveryOptions' -or - $capture -notmatch 'MaxConcurrentChains = 8' -or - $capture -notmatch 'UnknownPeerMaxConcurrentChains = 4' -or - $capture -notmatch 'MaxNameListPages = 64' -or - $capture -notmatch 'ProbeReportAttributes = true' -or - $capture -notmatch 'MaxReportAttributeProbes = 64' -or - $capture -notmatch 'ReadDataSetDirectories = true' -or - $capture -notmatch 'MaxDataSetDirectoryReads = 64' -or - $capture -notmatch 'GetOrCreateSmartDiscoveryAssociationFlight' -or - $capture -notmatch 'DiscoverSmartSingleFlightAsync' -or - $capture -notmatch 'ProbeSmartAsync' -or - $capture -notmatch 'initialFcRoots=deferred' -or - $capture -notmatch 'InitialFcReadExecutionResult\? initialRead = null' -or - $capture -match '_session\.DiscoverAsync\(' -or - $capture -match 'DiscoverDomainVariableNamesAsync' -or - $capture -match 'TryBuildSupplementalGetNameListSnapshotAsync' -or - $capture -match 'DiscoverDomainVariableTypeTreeNamesAsync' -or - $capture -match 'AddAdaptiveLogicalNodeSiblingProbeSignalsAsync' -or - $capture -match 'EnrichEngineeringUnitsAsync' -or - $capture -match 'InitialFcReadPlanner\.FromSclModel' -or - $capture -match 'ExecuteInitialFcReadPlanSmartAsync' -or - $smart -notmatch 'DiscoverSmartAsync' -or - $singleFlight -notmatch 'DiscoverSmartSingleFlightAsync' -or - $singleFlight -notmatch 'DiscoverSmartAsync\(options, CancellationToken\.None\)' -or - $smartGva -notmatch 'GetVariableAccessAttributesSmartAsync') { - throw 'P0 structural discovery freeze regressed away from the accepted IEDScout convergence path.' - } - - if ($lnParts -notmatch 'instanceStart' -or - $lnParts -notmatch 'IsFourLetterLnClass' -or - $exporter -notmatch 'TryResolveStandardSubDataObjectCdc' -or - $exporter -notmatch 'DataObjectReferencePaths' -or - $exporter -notmatch 'IsEdition2ServiceTrackingCdc' -or - $registry -notmatch 'Key\("TCTR", "ARtg"\)' -or - $registry -notmatch 'Key\("TVTR", "VRtg"\)' -or - $registry -notmatch 'Key\("LTIM", "TmChgDT"\)' -or - $registry -notmatch 'Key\("LTRK", "BrcbTrk"\)' -or - $registry -notmatch 'Key\("XCBR", "EEName"\)' -or - $registry -notmatch 'Key\("LLN0", "MltLev"\)' -or - $cdc -notmatch '"CST".*"BTS".*"UTS".*"STS".*"CTS"') { - throw 'Canonical IEC model / SCL semantic authority regressed.' - } - - Write-Host 'IEDScout convergence contract PASS.' From a75ebdb5f810b82046fa9c16568c9e56b37c4bf9 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:20:13 +0700 Subject: [PATCH 36/80] chore: remove superseded public artifact --- docs/IEDSCOUT_CONVERGENCE.md | 160 ----------------------------------- 1 file changed, 160 deletions(-) delete mode 100644 docs/IEDSCOUT_CONVERGENCE.md diff --git a/docs/IEDSCOUT_CONVERGENCE.md b/docs/IEDSCOUT_CONVERGENCE.md deleted file mode 100644 index 673294a00..000000000 --- a/docs/IEDSCOUT_CONVERGENCE.md +++ /dev/null @@ -1,160 +0,0 @@ -# IEDScout Convergence Contract - -## Product target - -ARSAS targets IEDScout-equivalent IEC 61850 engineering semantics with lower wire cost where possible: - -1. one accepted MMS association and bounded structure-first discovery; -2. a complete canonical model with exact LN/DO/SDO/DA/FC identity; -3. Edition 2 IID / Edition 1 ICD that can be reopened by ARSAS, reconnect to the same relay, hydrate values without full discovery, and run configured static reporting. - -The machine-readable authority is `evidence/iedscout-convergence-target.json`. - -## Merged proven baseline - -The physical R10 baseline was tested with: - -- ARSAS `eb8eb13d491f9aa265205852b8a4bab07af440ff`; -- ARIEC61850 tested head `9935d6902d786cc69b299260fe36b835944d5e81`; -- ARIEC61850 merged-main commit `648124097621046f5f127ceb1cf853fea54db730`. - -The tested engine head and merged-main commit have the identical tree SHA -`1cf7e08f333f24994625e8fe8416dbd0a16195b1`. - -Merged engine provenance: - -- PR #134 → main merge `e6779ff74e5716af4fcfc3dc926dae0567b3cdb0`: Smart Discovery performance authority. -- PR #135 → main merge `648124097621046f5f127ceb1cf853fea54db730`: canonical model, SCL interoperability, P1/P2 value pipeline. -- ARSAS PR #324: consumer integration and physical R10 proof. - -## P0 — structural discovery freeze - -Contract: `P0-R9-STRUCTURAL`. - -AA1E1F06R4 physical reference is locked at one association, 323 confirmed MMS -requests, 138 GetNameList, 119 GetVariableAccessAttributes, 2 -GetNamedVariableListAttributes and 64 Reads, while preserving 32 LD / 119 LN / -860 top-level DO / 906 DO+SDO / 4925 scalar leaves / 2 DataSets / 58 FCDA / 32 -logical ReportControls / 1 SettingControl. - -The same-relay IEDScout comparison remains about 417 confirmed requests, 119 GVA -and 156 Reads. ARSAS must not add traffic merely to imitate IEDScout. - -Forbidden regressions include a second discovery association, legacy supplemental -browse, a second full GetNameList sweep, recursive per-leaf GVA, and eager FC-root -value hydration on the discovery critical path. - -## P1 — CF projection-order repair: physically proven - -Contract: `P1-CF-DO-SCOPED`. - -R9 exposed 46 projection errors because SCL LNodeType DO order was incorrectly used -as MMS CF-root child order. P1 reads multi-DO CF data through exact `LN$CF$DO` -references and batches those structured reads. - -R10 physical reuse closes P1: - -- Ed2: 709 initial targets, 525 FC-root targets, 184 DO-scoped targets, - 709/709 successful, 0 failed, `projectionErrors=0`. -- Ed1: 708 initial targets, 524 FC-root targets, 184 DO-scoped targets, - 708/708 successful, 0 failed, `projectionErrors=0`. - -No extra discovery GVA or second association was introduced. - -## P2 — exact-case value pipeline: physically proven - -Contract: `P2-CASE-EXACT-VALUES`. - -R9 Ed2 lost 11 projected values because a consumer cache used case-insensitive -identity. P2 uses exact-case identity through TypeSpecification mapping, initial -projection, trusted-SCL caching, canonical evidence and SCL instance-value targeting. - -R10 physical reuse closes P2: - -- Ed2: `projectedUniqueValues=4441`, `initialValueCache=4441`, `cacheLoss=0`. -- Ed1: `projectedUniqueValues=4246`, `initialValueCache=4246`, `cacheLoss=0`. - -## R10 round-trip/reporting acceptance - -Both generated editions reopen as trusted SCL and reconnect without full discovery: - -- expected/observed/matched MMS domains: 32/32/32; -- DataSets: 2, members: 58, missing members: 0; -- configured report plans: Digital BRCB 36 members + Analog URCB 22 members; -- report-covered runtime points: 58; -- final unresolved runtime points: 0; -- cyclic MMS process polling: 0; -- actual InformationReport traffic observed on both editions. - -The early UI field `Primary unresolved=2` is not an operational loss: the exact -static DataSet schema resolves all 58 runtime points before reporting starts. - -## Save-time instance values - -R10 physically proves bounded Save SCL enrichment while fast discovery stays -unchanged: - -- canonical instance evidence: 3854 leaves; -- Ed2 exported `Val`: 3202; -- Ed1 exported `Val`: 3106. - -IEDScout's golden file has about 1529 `Val` elements. A larger count is not -automatically better; the remaining task is semantic path/value comparison, not -count chasing. - -## RCB lock - -The accepted representation remains: - -- 34 runtime RCB objects → 32 logical SCL ReportControls; -- `Services/ConfReportControl max=34`; -- Buffer/Digital and Unbuffer/Analog remain the two configured report authorities; -- preallocated ADD slots without DataSet never receive an invented `datSet`. - -## Next improvements — do not disturb the proven wire/reuse path - -### 1. Template interning - -R10 Ed2 is semantically correct but verbose: - -- ARSAS: 119 LNodeType / 906 DOType / 752 DAType / about 731 KB; -- IEDScout reference: about 38 / 60 / 17 / about 247 KB. - -Next work may intern only templates with identical ordered semantic fingerprints. -Expanded model counts, FC ownership, values, DataSets/RCBs and round-trip behavior -must remain unchanged. - -### 2. Reuse one save-enrichment snapshot across Ed2 and Ed1 - -When Ed2 and Ed1 are saved in the same unchanged live association/model generation, -both currently derive the same 3854 instance-evidence leaves. A later optimization -may reuse that bounded snapshot across serializers, but never across reconnect, -model-generation change or explicit refresh. - -### 3. Semantic Val diff - -Compare ARSAS vs IEDScout by exact -`LD/LN/DO/SDO/DA/BDA/FC/bType/value` path, not raw XML position and not total -`Val` count. - -## Regression signatures that are forbidden - -A build is rejected if it restores any of these patterns: - -- legacy `DiscoverAsync` as the public discovery route; -- a second supplemental discovery association; -- recursive per-leaf GVA expansion; -- speculative thousands of Reads on the discovery path; -- cross-DO positional CF projection; -- case-insensitive IEC 61850 member/value identity; -- WYE/DEL/SEQ SDO flattening; -- Edition 2 tracking CDCs in Edition 1; -- invented DataSet bindings for unassigned RCB slots; -- silent canonical-save success without reopen/association validation. - -## Promotion state - -The R10 physical retest has passed and merge is allowed. Production promotion is -still a separate release decision. - -The field result, not test count alone, decides interoperability acceptance. From 2f5bdadc6bdc0b792eeaa9160f6eb516bd92848a Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:20:16 +0700 Subject: [PATCH 37/80] chore: remove superseded public artifact --- evidence/iedscout-convergence-target.json | 500 ---------------------- 1 file changed, 500 deletions(-) delete mode 100644 evidence/iedscout-convergence-target.json diff --git a/evidence/iedscout-convergence-target.json b/evidence/iedscout-convergence-target.json deleted file mode 100644 index a853fd0ea..000000000 --- a/evidence/iedscout-convergence-target.json +++ /dev/null @@ -1,500 +0,0 @@ -{ - "schemaVersion": 1, - "name": "IEDScout discovery and SCL convergence", - "status": "physical-retest-passed-merge-ready", - "target": { - "discovery": "Reach IEDScout-like MMS discovery efficiency without sacrificing exact IEC 61850 model evidence.", - "model": "Build the canonical IEC 61850 model from structure-first MMS evidence with correct LN identity, DO/SDO hierarchy, FC ownership, DataSet/RCB semantics, and schema-aware CDC handling.", - "scl": "Save Edition 2 IID / Edition 1 ICD that reopens in ARSAS, reconnects to the relay, and is usable as trusted SCL." - }, - "activeStack": { - "enginePerformance": { - "repository": "masarray/ARIEC61850", - "pullRequest": 134, - "head": "a31e396f0bbc9507215a8c9d125e910555ed6f0a", - "status": "merged", - "testedHead": "a31e396f0bbc9507215a8c9d125e910555ed6f0a", - "mainMerge": "e6779ff74e5716af4fcfc3dc926dae0567b3cdb0" - }, - "engineModelAndScl": { - "repository": "masarray/ARIEC61850", - "pullRequest": 135, - "basePullRequest": 134, - "head": "648124097621046f5f127ceb1cf853fea54db730", - "status": "merged", - "testedHead": "9935d6902d786cc69b299260fe36b835944d5e81", - "mainMerge": "648124097621046f5f127ceb1cf853fea54db730" - }, - "consumerIntegration": { - "repository": "masarray/arsas", - "pullRequest": 324, - "branch": "test/smart-ied-discovery-pr134", - "status": "physical-proven-merge-ready" - } - }, - "physicalReference": { - "relay": "AA1E1F06R4", - "logicalDevices": 32, - "logicalNodes": 119, - "dataSets": 2, - "runtimeReportControls": 34, - "logicalSclReportControls": 32, - "iedScoutCapture": { - "associations": 1, - "confirmedMmsRequests": 417, - "getVariableAccessAttributes": 119, - "reads": 156, - "note": "Reference capture supplied by the physical comparison; values are acceptance reference, not CI-simulated proof." - }, - "rejectedLegacyArsasCapture": { - "associations": 2, - "confirmedMmsRequests": 30552, - "getVariableAccessAttributes": 23724, - "reads": 6548, - "note": "Regression signature. A future field build showing this pattern is rejected." - }, - "iedScoutSclReference": { - "lDevices": 32, - "logicalNodes": 119, - "dataSets": 2, - "fcda": 58, - "logicalReportControls": 32, - "expandedTopLevelDataObjects": 860, - "expandedDataObjectsIncludingSdo": 906, - "expandedScalarLeaves": 4925 - } - }, - "discoveryAcceptance": { - "exactlyOneAssociation": true, - "supplementalLegacyAssociationForbidden": true, - "recursivePerLeafGvaStormForbidden": true, - "secondFullGetNameListSweepForbidden": true, - "eagerInitialFcValueReadDuringStructuralDiscovery": false, - "typeStrategy": "LN/root structure first, bounded leaf fallback only when exact structure evidence is unavailable.", - "physicalPerformanceGate": "Compare request count, GVA count, Read count, peak outstanding calls, TTFI and total discovery time against IEDScout on the same relay." - }, - "modelAcceptance": { - "prefixedLogicalNodeIdentityExamples": [ - "RPRE_MMXU1 => prefix=RPRE_, lnClass=MMXU, lnInst=1", - "FPRE_MMXN1 => prefix=FPRE_, lnClass=MMXN, lnInst=1", - "I01ATCTR1 => prefix=I01A, lnClass=TCTR, lnInst=1" - ], - "standardSdoProjection": [ - "WYE", - "DEL", - "SEQ" - ], - "descendantFunctionalConstraintOwnership": true, - "standardRegistryAuthority": [ - "TCTR", - "TVTR", - "LTIM", - "EEName", - "MltLev" - ], - "edition2ServiceTrackingCdc": [ - "CST", - "BTS", - "UTS", - "STS", - "CTS" - ], - "edition1TrackingDowngrade": "omit with explicit warning; never emit invalid Edition 1 SCL", - "mmsTypeDeclarationOrder": "LN-root TypeSpecification declaration order is authoritative through model, SCL and FC-root projection.", - "settingFunctionalConstraints": "SG/SE are setting data; only actual SGCB produces SettingControl.", - "mhaiThdProjection": "MHAI ThdA/ThdPhV are WYE with CMV phase SDOs." - }, - "sclAcceptance": { - "profile": "full-model", - "reopenInArsasRequired": true, - "exactAssociationRebuildRequired": true, - "nativeCallingIdentityRequired": true, - "initialTrustedSclReads": "safe bounded SCL-guided reads after trusted-SCL reconnect; multi-DO CF groups use DO-scoped structured Reads because SCL LNodeType order is not authoritative for MMS FC-root DO order.", - "rcbProjection": "34 runtime RCB -> 32 logical ReportControl; ConfReportControl max=34", - "zeroSilentModelDeletion": true, - "physicalReconnectRequired": true, - "reuseTransportAndReportingLock": "Both Edition 2 and Edition 1 must match 32/32 MMS domains, complete all planned safe FC-root Reads without transport failures, preserve both static DataSets, arm exactly the configured Analog/Digital report plans, resolve all 58 runtime points, and observe actual InformationReport traffic.", - "semanticProjectionTarget": "projectionErrors=0 and projected leaf cache has no case-collapse loss." - }, - "promotion": { - "productionPromoted": false, - "mergeAllowedBeforePhysicalRetest": false, - "physicalRetestRequired": false, - "requiredEvidence": [ - "new ARSAS PCAP", - "new generated Ed2 IID", - "new generated Ed1 ICD", - "diagnostic report", - "Ed2 SCL-assisted reuse diagnostic with projectionErrorSamples", - "Ed1 SCL-assisted reuse diagnostic with projectionErrorSamples", - "same-relay comparison against IEDScout" - ], - "mergeAllowedAfterPhysicalRetest": true, - "physicalRetestPassed": true, - "physicalRetestDate": "2026-09-19", - "note": "R10 physical discovery/save/reopen evidence passed for the exact ARSAS/engine source tree. Merge is allowed; production promotion remains a separate release decision." - }, - "legacyPolicy": { - "rule": "Historical PRs may remain as provenance only; they must not be used as new branch bases after an active convergence authority supersedes them.", - "knownInvalidAuthority": [ - "PR #125 calling identity profile" - ], - "activeAuthorityOnly": [ - "ARIEC61850 #134", - "ARIEC61850 #135", - "ARSAS #324" - ] - }, - "physicalEvidence": { - "arsasR9": { - "relay": "AA1E1F06R4", - "testedArtifact": { - "appHead": "a89d6ef230951fde98f2b35e38dbc2dc84b6382f", - "engineHead": "3e12fb9d84ae27ddbebb2389ed2eb58d0fde6d90" - }, - "discovery": { - "associations": 1, - "confirmedMmsRequests": 323, - "getNameList": 138, - "getVariableAccessAttributes": 119, - "getNamedVariableListAttributes": 2, - "reads": 64, - "topLevelDataObjects": 860, - "dataObjectsIncludingSdo": 906, - "scalarLeaves": 4925, - "dataSets": 2, - "fcda": 58, - "logicalReportControls": 32, - "settingControls": 1 - }, - "reuseEdition2": { - "expectedDomains": 32, - "matchedDomains": 32, - "fcRoots": 563, - "successfulReads": 563, - "failedReads": 0, - "projectedLeaves": 4250, - "initialValueCache": 4239, - "projectionErrors": 46, - "staticDataSets": 2, - "staticReportControls": 32, - "reportBackedRuntimePoints": 58, - "unresolvedRuntimePoints": 0, - "actualInformationReportObserved": true - }, - "reuseEdition1": { - "expectedDomains": 32, - "matchedDomains": 32, - "fcRoots": 562, - "successfulReads": 562, - "failedReads": 0, - "projectedLeaves": 4055, - "initialValueCache": 4055, - "projectionErrors": 46, - "staticDataSets": 2, - "staticReportControls": 32, - "reportBackedRuntimePoints": 58, - "unresolvedRuntimePoints": 0, - "actualInformationReportObserved": true - } - }, - "arsasR10": { - "relay": "AA1E1F06R4", - "testedArtifact": { - "appHead": "eb8eb13d491f9aa265205852b8a4bab07af440ff", - "engineTestedHead": "9935d6902d786cc69b299260fe36b835944d5e81", - "engineMergedMain": "648124097621046f5f127ceb1cf853fea54db730", - "engineTree": "1cf7e08f333f24994625e8fe8416dbd0a16195b1" - }, - "discovery": { - "associations": 1, - "confirmedMmsRequests": 323, - "getNameList": 138, - "getVariableAccessAttributes": 119, - "getNamedVariableListAttributes": 2, - "reads": 64, - "topLevelDataObjects": 860, - "dataObjectsIncludingSdo": 906, - "scalarLeaves": 4925, - "dataSets": 2, - "fcda": 58, - "logicalReportControls": 32, - "settingControls": 1 - }, - "saveScl": { - "instanceEvidence": 3854, - "edition2ValCount": 3202, - "edition1ValCount": 3106, - "edition2RoundTrip": { - "logicalDevices": 32, - "logicalNodes": 119, - "dataSets": 2, - "logicalReportControls": 32 - }, - "edition1RoundTrip": { - "logicalDevices": 32, - "logicalNodes": 119, - "dataSets": 2, - "logicalReportControls": 32 - } - }, - "reuseEdition2": { - "expectedDomains": 32, - "observedDomains": 32, - "matchedDomains": 32, - "missingDomains": 0, - "extraDomains": 0, - "logicalDevices": 32, - "logicalNodes": 119, - "topLevelDataObjects": 860, - "dataAttributes": 5677, - "initialTargets": 709, - "fcRootTargets": 525, - "doScopedTargets": 184, - "successfulReads": 709, - "failedReads": 0, - "projectedLeaves": 4441, - "projectedUniqueValues": 4441, - "initialValueCache": 4441, - "cacheLoss": 0, - "projectionErrors": 0, - "staticDataSets": 2, - "staticMembers": 58, - "staticReportControls": 32, - "reportBackedRuntimePoints": 58, - "unresolvedRuntimePoints": 0, - "primaryUnresolvedAtSelection": 2, - "actualInformationReportObserved": true, - "cyclicMmsProcessPolling": 0 - }, - "reuseEdition1": { - "expectedDomains": 32, - "observedDomains": 32, - "matchedDomains": 32, - "missingDomains": 0, - "extraDomains": 0, - "logicalDevices": 32, - "logicalNodes": 119, - "topLevelDataObjects": 845, - "dataAttributes": 5470, - "initialTargets": 708, - "fcRootTargets": 524, - "doScopedTargets": 184, - "successfulReads": 708, - "failedReads": 0, - "projectedLeaves": 4246, - "projectedUniqueValues": 4246, - "initialValueCache": 4246, - "cacheLoss": 0, - "projectionErrors": 0, - "staticDataSets": 2, - "staticMembers": 58, - "staticReportControls": 32, - "reportBackedRuntimePoints": 58, - "unresolvedRuntimePoints": 0, - "primaryUnresolvedAtSelection": 2, - "actualInformationReportObserved": true, - "cyclicMmsProcessPolling": 0 - } - } - }, - "openGaps": { - "trustedSclProjectionParity": { - "targetProjectionErrors": 0, - "observedEdition2": 46, - "observedEdition1": 46, - "status": "resolved-r10-physical", - "rule": "Do not classify SCL reuse as semantically converged until projectionErrors reaches zero on both editions. Successful MMS Reads alone are insufficient.", - "rootCause": "R9 PCAP + Ed2 IID reproduction matched all 46 physical errors: every mismatch is in CF, across 18 FC roots / 191 affected SCL leaves, caused by cross-DO positional ordering differences between SCL LNodeType order and the MMS CF structure. No SCL count= arrays are present.", - "sourceRepair": "For multi-DO CF groups, InitialFcReadPlanner now emits exact LN$CF$DO targets. InitialFcValueProjector projects each DO value directly, eliminating cross-DO positional mapping while retaining batching.", - "r10ObservedEdition2": 0, - "r10ObservedEdition1": 0, - "note": "R9 historical 46/46 evidence is retained for provenance; R10 physically proves the repair on both editions." - }, - "edition2CaseDistinctInitialValueCache": { - "targetLoss": 0, - "observedProjectedMinusCached": 11, - "status": "resolved-r10-physical", - "rule": "LTRK t and T and any other case-distinct IEC 61850 paths must remain separate through TypeSpecification mapping, initial projection, ARSAS cache, canonical instance evidence, DAI/Val export and reopen. cacheLoss must be zero.", - "sourceRepair": "ARSAS trusted-SCL initial-value cache now uses StringComparer.Ordinal. Engine canonical value dedup, TypeSpecification member resolution, and canonical SCL instance-value targeting are exact-case. Diagnostics expose projectedUniqueValues and cacheLoss.", - "r10ObservedProjectedMinusCached": 0, - "r10Edition2ProjectedUniqueValues": 4441, - "r10Edition2InitialValueCache": 4441, - "note": "R9 historical loss=11 is retained for provenance; R10 physically proves exact-case cache preservation." - }, - "preallocatedAddReportControls": { - "observedWithoutDataSet": 30, - "status": "resolved-r10-export-and-reuse", - "rule": "Do not invent datSet. rptID-backed singleton runtime names ending 01 must project to indexed logical ReportControl max=1; an unassigned indexed slot is a warning, not a fatal missing-DataSet error.", - "r10FatalMissingDatasetErrors": 0, - "r10LogicalReportControls": 32, - "note": "Unassigned indexed ADD slots remain without invented datSet and no longer block reuse/reporting." - }, - "saveTimeInstanceValues": { - "observedR9ExportValCount": 0, - "referenceIedScoutValCount": 1529, - "status": "mechanism-physically-proven-semantic-diff-open", - "rule": "Keep structural discovery read-free; acquire bounded safe FC-root values only during explicit Save SCL and verify resulting instance evidence physically.", - "r10InstanceEvidence": 3854, - "r10Edition2ValCount": 3202, - "r10Edition1ValCount": 3106, - "note": "Save-time value enrichment is physically proven. More Val elements than IEDScout is not treated as automatically better; exact semantic path/value comparison remains open." - }, - "templateReuse": { - "status": "next-improvement", - "rule": "Reduce duplicate LNodeType/DOType/DAType templates only after wire and semantic reuse parity is stable.", - "r10Edition2": { - "lNodeType": 119, - "doType": 906, - "daType": 752, - "fileBytes": 731266 - }, - "iedScoutReference": { - "lNodeType": 38, - "doType": 60, - "daType": 17, - "fileBytesApprox": 247000 - }, - "acceptance": "Intern only structurally and semantically identical templates. Expanded 32/119/860/906/4925 model, FC ownership, values and round-trip behavior must remain unchanged." - }, - "saveEnrichmentReuse": { - "status": "next-optimization", - "observation": "Ed2 and Ed1 saves in one unchanged live session both produced instanceEvidence=3854; the bounded enrichment snapshot can be reused across edition serializers when association/model generation is unchanged.", - "rule": "Do not cache across reconnect, model-generation change, or explicit refresh. Optimization must not alter P0 discovery." - } - }, - "p0StructuralDiscoveryFreeze": { - "contractId": "P0-R9-STRUCTURAL", - "status": "implemented-and-r9-physically-proven", - "scope": "AA1E1F06R4 physical acceptance plus source/CI freeze. Counts are evidence and gates for this relay, not generic hardcoded runtime behavior for other IEDs.", - "sourcePolicy": { - "smartRouteRequired": true, - "associationScopedSingleFlightRequired": true, - "applicationMmsGateExclusive": true, - "logicalNodeRootTypeStrategyRequired": true, - "eagerInitialFcReadsForbidden": true, - "supplementalLegacyBrowseForbidden": true, - "secondFullGetNameListSweepForbidden": true, - "recursivePerLeafGvaStormForbidden": true, - "saveTimeAndTrustedSclValueReadsRemainSeparate": true - }, - "frozenOptions": { - "maxConcurrentChains": 8, - "unknownPeerMaxConcurrentChains": 4, - "maxDomains": 256, - "maxVariableNamesPerDomain": 20000, - "maxVariableListNamesPerDomain": 4096, - "maxNameListPages": 64, - "probeReportAttributes": true, - "maxReportAttributeProbes": 64, - "readDataSetDirectories": true, - "maxDataSetDirectoryReads": 64 - }, - "aa1e1f06r4Acceptance": { - "associations": 1, - "referenceConfirmedMmsRequests": 323, - "maximumConfirmedMmsRequests": 417, - "referenceGetNameList": 138, - "referenceGetVariableAccessAttributes": 119, - "maximumGetVariableAccessAttributes": 119, - "referenceGetNamedVariableListAttributes": 2, - "referenceReads": 64, - "maximumReads": 156, - "logicalDevices": 32, - "logicalNodes": 119, - "topLevelDataObjects": 860, - "dataObjectsIncludingSdo": 906, - "scalarLeaves": 4925, - "dataSets": 2, - "fcda": 58, - "logicalReportControls": 32, - "settingControls": 1 - }, - "rule": "Do not optimize or enrich structural discovery further while SCL semantic convergence is still open. Any required instance-value acquisition belongs to explicit Save SCL or trusted-SCL reconnect phases." - }, - "p1ProjectionOrderRepair": { - "contractId": "P1-CF-DO-SCOPED", - "status": "physically-proven-r10", - "preservesP0StructuralDiscovery": true, - "physicalR9Reproduction": { - "projectionErrors": 46, - "affectedFcRoots": 18, - "affectedSclLeaves": 191, - "functionalConstraint": "CF", - "sclArrayCountAttributes": 0 - }, - "rule": "Never use SCL LNodeType cross-DO order as authority for an MMS multi-DO CF structure. Read each CF DataObject by exact MMS object name and batch those references with the existing bounded Read executor.", - "expectedPhysicalOutcome": { - "projectionErrors": 0, - "noSilentCrossDoValueSwap": true, - "sameAssociation": true, - "noAdditionalDiscoveryGva": true - }, - "physicalR10Outcome": { - "edition2": { - "initialTargets": 709, - "doScopedTargets": 184, - "successfulReads": 709, - "failedReads": 0, - "projectionErrors": 0 - }, - "edition1": { - "initialTargets": 708, - "doScopedTargets": 184, - "successfulReads": 708, - "failedReads": 0, - "projectionErrors": 0 - }, - "reportBackedRuntimePoints": 58, - "unresolvedRuntimePoints": 0, - "actualInformationReportObserved": true - } - }, - "p2CaseSensitiveValuePipeline": { - "contractId": "P2-CASE-EXACT-VALUES", - "status": "physically-proven-r10", - "preservesP0StructuralDiscovery": true, - "preservesP1CfScopedHydration": true, - "physicalR9Evidence": { - "edition2ProjectedLeaves": 4250, - "edition2InitialValueCache": 4239, - "observedCacheLoss": 11, - "edition1ProjectedLeaves": 4055, - "edition1InitialValueCache": 4055, - "edition1ObservedCacheLoss": 0 - }, - "sourceInvariants": { - "trustedSclValueCacheComparer": "StringComparer.Ordinal", - "canonicalInstanceValueDedupComparer": "StringComparer.Ordinal", - "typeSpecificationMemberComparison": "StringComparison.Ordinal", - "sclDataObjectInstanceTargetComparison": "StringComparison.Ordinal", - "sclDaBdaSdoComponentComparison": "StringComparison.Ordinal", - "normalizationMayNotFoldCase": true - }, - "diagnosticContract": [ - "projectedUniqueValues", - "initialValueCache", - "cacheLoss" - ], - "expectedPhysicalOutcome": { - "edition2CacheLoss": 0, - "edition1CacheLoss": 0, - "ltrkLowercaseTAndUppercaseTRemainDistinct": true, - "noAdditionalDiscoveryGva": true, - "sameAssociation": true - }, - "physicalR10Outcome": { - "edition2": { - "projectedUniqueValues": 4441, - "initialValueCache": 4441, - "cacheLoss": 0 - }, - "edition1": { - "projectedUniqueValues": 4246, - "initialValueCache": 4246, - "cacheLoss": 0 - }, - "actualInformationReportObserved": true - } - } -} From 8f9d2db6de3f448a3e1807943e36bc53aaed1b29 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:20:20 +0700 Subject: [PATCH 38/80] chore: remove superseded public artifact --- .../templates/multi-vendor-integration.html | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 landing/templates/multi-vendor-integration.html diff --git a/landing/templates/multi-vendor-integration.html b/landing/templates/multi-vendor-integration.html deleted file mode 100644 index 8d16c1a47..000000000 --- a/landing/templates/multi-vendor-integration.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Multi-Vendor IEC 61850 Integration — IP-to-SCL and Live Evidence | ARSAS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{> header}} -
-
IP-to-SCL · live model · reporting · GOOSE · gateway integration

Multi-vendor IEC 61850 integration with a recoverable path when the vendor file fails.

Use ARSAS to discover the complete live IED model, generate IID or ICD when the available vendor file is unusable, normalize bounded single-IED SCL, compare configured intent with runtime evidence, and isolate reporting or GOOSE mismatches before editing the target project.

-
Recover from IPComplete live discovery to Edition 2 IID or Edition 1 ICD
Normalize SCLBounded generic single-IED conversion with structured findings
Verify runtimeReporting, GOOSE, identity, DataSets and RCBs in live context
Export intentionallyOne verified RCB as an Edition 1 or Edition 2 CID baseline
-
Suggested workflow

Find the mismatch before changing either system.

Interoperability failures usually sit between three sources: the IED that is online, the engineering file that describes intended configuration, and the proprietary expectations of the receiving SAS or gateway.

01

Identify the available source

Start from the approved IED IP address, the available CID/ICD/IID/SCD, or both. Preserve the original file and record the intended receiving system.

02

Discover the live IED completely

Inspect physical identity, full MMS domains, hierarchy, DataSets, RCBs, type information and service behavior actually exposed by the device.

03

Generate or normalize bounded SCL

Create IID or ICD from complete live discovery, or convert the opened source into a generic single-IED output while retaining findings and provenance.

04

Compare reporting and GOOSE evidence

Check RCB names, availability, DataSet membership, trigger options, APPID, VLAN, addresses, references, sequence behavior and ordered payload.

05

Validate the receiving system deliberately

Import the bounded output, retain exact errors, and separate schema, identity, reference, reporting and proprietary importer problems before making changes.

-
Typical failure

“CID rejected” is not a root cause.

  • Physical IED identity does not match the receiving project
  • Logical Device domains were shortened or reinterpreted
  • Edition 1 and Edition 2 expectations differ
  • DataSet members or FCDA references are missing
  • An indexed RCB name was modified incorrectly
  • The receiving tool applies an undocumented proprietary rule
ARSAS evidence path

Keep each layer visible.

  • Live device identity and complete model
  • Original configured SCL intent
  • Generated or normalized bounded output
  • Read-only live RCB availability and options
  • Observed GOOSE communication and payload
  • Exact receiving-system import result
-
ARSAS selected RCB SCL export for multi-vendor IEC 61850 integration

Bounded selected-RCB CID export

Audit live availability, choose the intended reporting path and preserve exact RCB identity plus verified DataSet evidence before exporting the integration baseline.

-
Useful for

SAS, gateway and IED integration.

  • Recovering when a vendor CID or ICD cannot be used
  • Edition 1 and Edition 2 project alignment
  • Reporting DataSet and RCB troubleshooting
  • GOOSE address, reference and payload verification
  • Configured-to-live model comparison
  • Multi-IED diagnostic attribution
Boundary

Evidence is not universal conformance.

A successful connection, generation or import does not prove full IEC 61850 conformance, interoperability with every vendor, project correctness or acceptance by another proprietary tool. Final FAT/SAT engineering validation remains required.

-
Related product areas

Inspect the evidence behind the integration.

- {{> download-cta}} -
- {{> footer}} - - From 0782d4c6f238bb3d24e73bc6a2940f7b46515efb Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:20:23 +0700 Subject: [PATCH 39/80] chore: remove superseded public artifact --- .../integrasi-multi-vendor-iec61850.html | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 landing/templates/integrasi-multi-vendor-iec61850.html diff --git a/landing/templates/integrasi-multi-vendor-iec61850.html b/landing/templates/integrasi-multi-vendor-iec61850.html deleted file mode 100644 index 8af67ba57..000000000 --- a/landing/templates/integrasi-multi-vendor-iec61850.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Integrasi Multi-Vendor IEC 61850 — IP-to-SCL dan Live Evidence | ARSAS - - - - - - - - - {{> header}} -
-
IP-to-SCL · live model · reporting · GOOSE · integrasi gateway

Integrasi multi-vendor IEC 61850 dengan jalur recovery saat vendor file gagal.

Gunakan ARSAS untuk discovery complete live IED model, generate IID atau ICD ketika vendor file tidak usable, normalize bounded single-IED SCL, membandingkan configured intent dengan runtime evidence, dan mengisolasi mismatch reporting atau GOOSE sebelum mengubah project tujuan.

-
Recovery dari IPComplete live discovery menjadi IID Edition 2 atau ICD Edition 1
Normalize SCLBounded generic single-IED conversion dengan structured findings
Verifikasi runtimeReporting, GOOSE, identity, DataSet dan RCB dalam live context
Export sengajaSatu verified RCB sebagai baseline CID Edition 1 atau Edition 2
-
Workflow yang disarankan

Temukan mismatch sebelum mengubah salah satu sistem.

Failure interoperability biasanya berada di antara tiga sumber: IED yang online, engineering file yang menggambarkan intended configuration, dan proprietary expectation SAS atau gateway penerima.

01

Identifikasi source yang tersedia

Mulai dari alamat IP IED, CID/ICD/IID/SCD yang tersedia, atau keduanya. Pertahankan original file dan catat receiving system yang dituju.

02

Discovery IED live secara lengkap

Periksa physical identity, full MMS domain, hierarchy, DataSet, RCB, type information dan service behavior aktual.

03

Generate atau normalize bounded SCL

Buat IID/ICD dari complete live discovery, atau convert opened source menjadi generic single-IED output sambil mempertahankan findings dan provenance.

04

Bandingkan reporting dan GOOSE evidence

Periksa RCB name, availability, DataSet membership, trigger options, APPID, VLAN, address, reference, sequence behavior dan ordered payload.

05

Validasi receiving system secara sengaja

Import bounded output, pertahankan exact error, lalu pisahkan masalah schema, identity, reference, reporting dan proprietary importer sebelum melakukan perubahan.

-
Failure yang umum

“CID rejected” bukan root cause.

  • Physical IED identity tidak cocok dengan project penerima
  • Logical Device domain dipotong atau ditafsirkan ulang
  • Expectation Edition 1 dan Edition 2 berbeda
  • DataSet member atau FCDA reference hilang
  • Indexed RCB name dimodifikasi secara salah
  • Receiving tool menerapkan proprietary rule yang tidak terdokumentasi
Evidence path ARSAS

Pertahankan setiap layer tetap terlihat.

  • Live device identity dan complete model
  • Original configured SCL intent
  • Generated atau normalized bounded output
  • Read-only live RCB availability serta options
  • Observed GOOSE communication dan payload
  • Exact receiving-system import result
-
Export SCL RCB terpilih untuk integrasi multi-vendor IEC 61850

Bounded selected-RCB CID export

Audit live availability, pilih reporting path dan pertahankan exact RCB identity serta verified DataSet evidence sebelum export baseline integrasi.

-
Berguna untuk

Integrasi SAS, gateway dan IED.

  • Recovery saat vendor CID atau ICD tidak dapat dipakai
  • Alignment project Edition 1 dan Edition 2
  • Troubleshooting DataSet dan RCB reporting
  • Verifikasi address, reference dan payload GOOSE
  • Perbandingan configured-to-live model
  • Attribution diagnostic multi-IED
Batas engineering

Evidence bukan universal conformance.

Koneksi, generation atau import yang berhasil belum membuktikan conformance penuh, interoperability dengan semua vendor, kebenaran project atau acceptance oleh proprietary tool lain. Final validation FAT/SAT tetap diperlukan.

-
Area produk terkait

Periksa evidence di balik integrasi.

- {{> download-cta-id}} -
- {{> footer}} - - From f98b42a6587597d2cd6afc37239c4cd29ef5decd Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:10 +0700 Subject: [PATCH 40/80] docs: update integration route references --- landing/templates/cid-rejected.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/cid-rejected.html b/landing/templates/cid-rejected.html index 59d83a63c..477f74a76 100644 --- a/landing/templates/cid-rejected.html +++ b/landing/templates/cid-rejected.html @@ -1 +1 @@ -IEC 61850 CID or SCL File Rejected — IP-to-SCL Recovery{{> header}}
Schema · identity · references · edition · live recovery

A rejected CID file does not automatically mean the relay is not interoperable.

First determine whether the failure is XML syntax, SCL structure, physical identity, edition handling, vendor-private content, broken references or a mismatch between the file and the running IED. When the source cannot be repaired safely, complete live discovery can provide a new bounded baseline.

Immediate answer

Preserve the original file and capture the exact import error.

Do not begin by deleting arbitrary XML sections. Record the parser location or rejected reference, the receiving tool and edition expectation, then compare the file with live IED evidence.

Typical symptoms
  • schema or namespace error during import
  • unknown or unsupported edition element
  • physical IED identity or MMS domain mismatch
  • unresolved DataSet, FCDA, RCB or communication reference
  • vendor-private section rejected by another tool
  • file imports but reporting remains silent
Diagnostic path

Separate file validity from integration usefulness.

A file can be valid XML but unsuitable for a target configurator. It can also import successfully while representing an outdated device model, incorrect identity or incomplete reporting assignment.

01

Record the receiving-system evidence

Keep the original filename, edition, namespace, exact error message, reported location and receiving software version.

02

Validate identity and critical references

Trace physical IED name, AccessPoint, server, full Logical Device domains, DataSets, FCDA members, RCBs and communication references without blind deletion.

03

Compare with the live IED

Confirm endpoint, complete typed model, DataSet directory, concrete RCB names and options currently exposed by the relay.

04

Choose the recovery path

Repair or normalize the opened source when its relationships remain trustworthy. Otherwise generate Edition 2 IID or Edition 1 ICD from complete live discovery.

05

Reduce only when the receiving workflow requires it

For a bounded gateway or legacy SAS path, audit availability and export exactly one verified RCB as an Edition 1 or Edition 2 CID baseline.

Live recovery can preserve

Relationships commonly damaged by manual cleanup.

  • physical identity separate from MMS Logical Device domains
  • complete typed hierarchy and available type information
  • DataSet membership and member order
  • exact indexed runtime RCB names
  • available trigger, optional-field and timing evidence
  • source, endpoint and export provenance
Still requires validation

The receiving importer remains authoritative for acceptance.

Generated SCL is vendor-neutral and schema-aware, but proprietary tools can apply rules beyond the standard. Retest the exact importer and preserve its result as project evidence.

ARSAS selected RCB SCL Edition 1 and Edition 2 export workflow

Build a bounded integration baseline

Select the intended RCB, retain verified DataSet members and choose the required SCL edition instead of removing unrelated XML through trial and error.

Related guides

Check whether the file still matches the running relay.

Continue with live-model comparison, the IP-to-SCL workspace or the complete multi-vendor integration workflow.

{{> guide-boundary}}{{> download-cta}}
{{> footer}} \ No newline at end of file +IEC 61850 CID or SCL File Rejected — IP-to-SCL Recovery{{> header}}
Schema · identity · references · edition · live recovery

A rejected CID file does not automatically mean the relay is not interoperable.

First determine whether the failure is XML syntax, SCL structure, physical identity, edition handling, implementation-private content, broken references or a mismatch between the file and the running IED. When the source cannot be repaired safely, complete live discovery can provide a new bounded baseline.

Immediate answer

Preserve the original file and capture the exact import error.

Do not begin by deleting arbitrary XML sections. Record the parser location or rejected reference, the receiving tool and edition expectation, then compare the file with live IED evidence.

Typical symptoms
  • schema or namespace error during import
  • unknown or unsupported edition element
  • physical IED identity or MMS domain mismatch
  • unresolved DataSet, FCDA, RCB or communication reference
  • implementation-private section rejected by another tool
  • file imports but reporting remains silent
Diagnostic path

Separate file validity from integration usefulness.

A file can be valid XML but unsuitable for a target configurator. It can also import successfully while representing an outdated device model, incorrect identity or incomplete reporting assignment.

01

Record the receiving-system evidence

Keep the original filename, edition, namespace, exact error message, reported location and receiving software version.

02

Validate identity and critical references

Trace physical IED name, AccessPoint, server, full Logical Device domains, DataSets, FCDA members, RCBs and communication references without blind deletion.

03

Compare with the live IED

Confirm endpoint, complete typed model, DataSet directory, concrete RCB names and options currently exposed by the relay.

04

Choose the recovery path

Repair or normalize the opened source when its relationships remain trustworthy. Otherwise generate Edition 2 IID or Edition 1 ICD from complete live discovery.

05

Reduce only when the receiving workflow requires it

For a bounded gateway or legacy SAS path, audit availability and export exactly one verified RCB as an Edition 1 or Edition 2 CID baseline.

Live recovery can preserve

Relationships commonly damaged by manual cleanup.

  • physical identity separate from MMS Logical Device domains
  • complete typed hierarchy and available type information
  • DataSet membership and member order
  • exact indexed runtime RCB names
  • available trigger, optional-field and timing evidence
  • source, endpoint and export provenance
Still requires validation

The receiving importer remains authoritative for acceptance.

Generated SCL is implementation-neutral and schema-aware, but proprietary tools can apply rules beyond the standard. Retest the exact importer and preserve its result as project evidence.

ARSAS selected RCB SCL Edition 1 and Edition 2 export workflow

Build a bounded integration baseline

Select the intended RCB, retain verified DataSet members and choose the required SCL edition instead of removing unrelated XML through trial and error.

Related guides

Check whether the file still matches the running relay.

Continue with live-model comparison, the IP-to-SCL workspace or the complete multi-device integration workflow.

{{> guide-boundary}}{{> download-cta}}
{{> footer}} \ No newline at end of file From 072d741c385618e32f7590bbb6f6363be12f7339 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:12 +0700 Subject: [PATCH 41/80] docs: update integration route references --- landing/templates/commissioning-iec61850.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/commissioning-iec61850.html b/landing/templates/commissioning-iec61850.html index c6c9cc3d1..3fb24ee73 100644 --- a/landing/templates/commissioning-iec61850.html +++ b/landing/templates/commissioning-iec61850.html @@ -18,7 +18,7 @@
Workflow yang disarankan

Investigasi live path sebelum mengubah konfigurasi.

Tekanan commissioning sering mendorong perubahan terlalu cepat. Mulailah dengan mempertahankan evidence yang sudah tersedia dari IED dan network.

01

Tetapkan konteks perangkat

Konfirmasi endpoint, active interface, device identity dan live MMS model sebelum menginterpretasi hasil.

02

Amati value dan event

Review selected value, quality, process timestamp, reason-for-inclusion dan semantic event history.

03

Periksa reporting dan GOOSE

Periksa RCB state, coverage DataSet, stream identity, sequence counter, retransmission dan payload evidence.

04

Jalankan hanya operasi yang disetujui

Gunakan Direct atau SBO bertipe hanya saat procedure, role dan kondisi plant mengizinkan active control.

05

Pertahankan negative evidence

Unsupported service, rejected write, report gap, timeout dan command failure tetap terlihat untuk root-cause analysis.

Event log IEC 61850 ARSAS untuk troubleshooting commissioning

Event evidence dengan konteks perangkat

Pisahkan continuous value dari perubahan bermakna sambil mempertahankan timestamp, quality, source dan IED attribution.

Berguna untuk

Investigasi live-system yang terkontrol.

  • Troubleshooting reporting dan quality
  • Pemeriksaan GOOSE sequence dan subscription
  • Review control completion dan feedback
  • Pengambilan fault record setelah event
  • Diagnostik komunikasi multi-IED
Batas engineering

Kewenangan commissioning tetap berada di luar aplikasi.

ARSAS tidak memberikan switching permission, menentukan keselamatan plant, menggantikan protection testing, menyetujui setting atau meniadakan site procedure, cybersecurity control dan independent verification.

-
Workflow terkait

Hubungkan commissioning dengan lifecycle proyek.

+
Workflow terkait

Hubungkan commissioning dengan lifecycle proyek.

{{> download-cta-id}} {{> footer}} From 2db36630b8541e71d00394f8b0e0ab76d53396c7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:15 +0700 Subject: [PATCH 42/80] docs: update integration route references --- landing/templates/commissioning.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/commissioning.html b/landing/templates/commissioning.html index a389424ca..02a2bec72 100644 --- a/landing/templates/commissioning.html +++ b/landing/templates/commissioning.html @@ -46,7 +46,7 @@
Useful for

Controlled live-system investigation.

  • Reporting and quality troubleshooting
  • GOOSE sequence and subscription checks
  • Control completion and feedback review
  • Fault-record retrieval after events
  • Multi-IED communication diagnostics
Boundary

Commissioning authority remains external.

ARSAS does not grant switching permission, determine plant safety, replace protection testing, approve settings or override site procedures, cybersecurity controls and independent verification.

-
Related workflows

Connect commissioning evidence to the project lifecycle.

+
Related workflows

Connect commissioning evidence to the project lifecycle.

{{> download-cta}} From 9c744219da60b2402b2926624a94a7cb6f38417f Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:21 +0700 Subject: [PATCH 43/80] docs: update integration route references --- landing/templates/fat-testing.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/fat-testing.html b/landing/templates/fat-testing.html index 06bc8d3fe..350e41118 100644 --- a/landing/templates/fat-testing.html +++ b/landing/templates/fat-testing.html @@ -39,8 +39,8 @@
Choose FAT / IO List Testing

The test procedure already contains the digital indications.

  • Import the approved ARSAS workbook
  • Show only imported IEDs and SDI points
  • Bind each point to live IEC 61850 evidence
  • Reject initial ON and reconnect images as edges
  • Export Excel, PDF or portable .arsas
Choose General IEC 61850 Testing

The task requires broader service investigation.

  • Discover the complete live MMS model
  • Select values and validate BRCB/URCB acquisition
  • Inspect GOOSE and station-bus evidence
  • Retrieve disturbance records
  • Review guarded control and SCL output
General FAT workflow

Move from endpoint access to witnessed service evidence.

Adapt the sequence to the approved FAT procedure and project responsibilities.

01

Connect and discover

Add the approved endpoint, establish MMS association and inspect the live hierarchy, DataSets, RCBs and control model.

02

Select the FAT signals

Choose status, measurement, quality and timestamp points required by the test sheet instead of mapping the entire IED blindly.

03

Validate acquisition

Prefer configured reporting, verify exact DataSet membership and keep unsupported or unavailable coverage visible.

04

Inspect GOOSE and control

Check stream identity and sequence evidence, then execute only approved Direct or SBO operations with visible completion results.

05

Confirm files and diagnostics

Probe available fault records and retain communication, report, frame and service outcomes with the originating IED.

ARSAS IEC 61850 live values used during factory acceptance testing

Keep evidence attributable to the tested IED

Whether the session starts from an IO List or manual signal selection, review identity, object reference, value, quality, timestamp and acquisition source together.

-
Useful for

Protection and substation automation FAT.

  • SDI IO List indication proof
  • Relay and bay-controller communication checks
  • BRCB and URCB behavior
  • GOOSE publishing and subscription evidence
  • Direct and SBO control validation
  • Disturbance-record file access
Boundary

FAT acceptance remains procedural.

ARSAS does not replace calibrated injection equipment, protection-function testing, approved drawings, settings review, cybersecurity controls, witness sign-off, digital signatures or vendor responsibility. IO List FAT is available on current mainline; verify the stable release scope before use.

-
Related workflows

Continue from factory to site.

+
Useful for

Protection and substation automation FAT.

  • SDI IO List indication proof
  • Relay and bay-controller communication checks
  • BRCB and URCB behavior
  • GOOSE publishing and subscription evidence
  • Direct and SBO control validation
  • Disturbance-record file access
Boundary

FAT acceptance remains procedural.

ARSAS does not replace calibrated injection equipment, protection-function testing, approved drawings, settings review, cybersecurity controls, witness sign-off, digital signatures or implementation responsibility. IO List FAT is available on current mainline; verify the stable release scope before use.

+
Related workflows

Continue from factory to site.

{{> download-cta}} {{> footer}} From 8f409fe67484b7eae9b695614972181ae90495be Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:24 +0700 Subject: [PATCH 44/80] docs: update integration route references --- landing/templates/goose-sequence.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/goose-sequence.html b/landing/templates/goose-sequence.html index b42bad372..397cbec51 100644 --- a/landing/templates/goose-sequence.html +++ b/landing/templates/goose-sequence.html @@ -1 +1 @@ -IEC 61850 GOOSE stNum and sqNum — Troubleshooting{{> header}}
stNum · sqNum · TAL · retransmission

GOOSE sequence values explain whether the publisher changed state or retransmitted the same state.

Interpret sequence evidence together with goCBRef, DataSet, APPID, VLAN, source MAC, TAL and payload. One counter alone is not enough to identify the stream.

stNum

State number

A new published state is normally associated with a state-number change. Confirm that the ordered payload or relevant status also changed as expected.

sqNum

Sequence number

Retransmissions of the current state normally advance sequence evidence. Gaps, resets or unexpected patterns require context from capture continuity and publisher restart behavior.

Diagnostic path

Supervise the whole stream identity.

First prove that consecutive frames belong to the same publisher and control block. Then examine state, sequence, retransmission interval and TAL behavior.

01

Bind the stream identity

Use source and destination MAC, APPID, VLAN, goCBRef and DataSet reference.

02

Track state transitions

Compare stNum with the ordered allData payload and the expected process event.

03

Track retransmission

Inspect sqNum progression and frame timing while the state remains unchanged.

04

Supervise TAL and capture gaps

Separate a publisher that exceeded its advertised lifetime from a local capture interruption or interface problem.

ARSAS GOOSE analyzer showing APPID stNum sqNum TAL and payload evidence

Observe sequence and payload together

ARSAS keeps stream identity, flags, TAL, counters and ordered payload visible in one GOOSE session.

A counter reset is not automatically a device fault.

Publisher restart, configuration activation or capture discontinuity can change the observed pattern. Correlate the timing with device and project events.

A healthy sequence does not prove correct semantics.

The stream can be timely and internally consistent while the DataSet order, scaling or subscriber mapping is wrong.

Related pages

Continue with GOOSE workflow evidence.

Review the analyzer capability and multi-vendor integration path.

{{> guide-boundary}}{{> download-cta}}
{{> footer}} \ No newline at end of file +IEC 61850 GOOSE stNum and sqNum — Troubleshooting{{> header}}
stNum · sqNum · TAL · retransmission

GOOSE sequence values explain whether the publisher changed state or retransmitted the same state.

Interpret sequence evidence together with goCBRef, DataSet, APPID, VLAN, source MAC, TAL and payload. One counter alone is not enough to identify the stream.

stNum

State number

A new published state is normally associated with a state-number change. Confirm that the ordered payload or relevant status also changed as expected.

sqNum

Sequence number

Retransmissions of the current state normally advance sequence evidence. Gaps, resets or unexpected patterns require context from capture continuity and publisher restart behavior.

Diagnostic path

Supervise the whole stream identity.

First prove that consecutive frames belong to the same publisher and control block. Then examine state, sequence, retransmission interval and TAL behavior.

01

Bind the stream identity

Use source and destination MAC, APPID, VLAN, goCBRef and DataSet reference.

02

Track state transitions

Compare stNum with the ordered allData payload and the expected process event.

03

Track retransmission

Inspect sqNum progression and frame timing while the state remains unchanged.

04

Supervise TAL and capture gaps

Separate a publisher that exceeded its advertised lifetime from a local capture interruption or interface problem.

ARSAS GOOSE analyzer showing APPID stNum sqNum TAL and payload evidence

Observe sequence and payload together

ARSAS keeps stream identity, flags, TAL, counters and ordered payload visible in one GOOSE session.

A counter reset is not automatically a device fault.

Publisher restart, configuration activation or capture discontinuity can change the observed pattern. Correlate the timing with device and project events.

A healthy sequence does not prove correct semantics.

The stream can be timely and internally consistent while the DataSet order, scaling or subscriber mapping is wrong.

Related pages

Continue with GOOSE workflow evidence.

Review the analyzer capability and multi-device integration path.

{{> guide-boundary}}{{> download-cta}}
{{> footer}} \ No newline at end of file From 51cdde13a870f5da62ff9f0ef21c1a79199065ab Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:27 +0700 Subject: [PATCH 45/80] docs: update integration route references --- landing/templates/pengujian-fat-iec61850.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/pengujian-fat-iec61850.html b/landing/templates/pengujian-fat-iec61850.html index eb2fa5d35..bff1849ef 100644 --- a/landing/templates/pengujian-fat-iec61850.html +++ b/landing/templates/pengujian-fat-iec61850.html @@ -18,8 +18,8 @@
Pilih FAT / IO List Testing

Test procedure sudah memiliki digital indication.

  • Import workbook ARSAS yang disetujui
  • Tampilkan hanya IED dan SDI hasil import
  • Bind setiap point ke evidence IEC 61850 live
  • Tolak initial ON dan image reconnect sebagai edge
  • Export Excel, PDF atau .arsas portable
Pilih General IEC 61850 Testing

Task membutuhkan investigasi service yang lebih luas.

  • Discovery complete live MMS model
  • Pilih value dan validasi acquisition BRCB/URCB
  • Periksa GOOSE dan station-bus evidence
  • Ambil disturbance record
  • Review guarded control dan output SCL
Workflow FAT general

Dari akses endpoint menuju service evidence yang dapat disaksikan.

Sesuaikan urutan dengan FAT procedure dan tanggung jawab proyek yang sudah disetujui.

01

Connect dan discovery

Tambahkan endpoint, bangun association MMS dan periksa hierarchy live, DataSet, RCB serta control model.

02

Pilih signal FAT

Pilih status, measurement, quality dan timestamp yang dibutuhkan test sheet tanpa mapping seluruh IED secara buta.

03

Validasi acquisition

Prioritaskan configured reporting, periksa membership DataSet secara tepat dan pertahankan coverage yang tidak tersedia tetap terlihat.

04

Periksa GOOSE dan control

Review identity serta sequence stream, lalu jalankan Direct atau SBO hanya bila diizinkan dengan hasil completion yang terlihat.

05

Konfirmasi file dan diagnostik

Probe fault record dan pertahankan outcome komunikasi, report, frame serta service bersama IED asal.

Live value IEC 61850 ARSAS untuk pengujian FAT

Pertahankan evidence attributable ke IED yang diuji

Baik session dimulai dari IO List maupun manual signal selection, review identity, object reference, value, quality, timestamp dan acquisition source bersama-sama.

-
Berguna untuk

FAT proteksi dan substation automation.

  • Proof indication SDI dari IO List
  • Communication check relay dan bay controller
  • Perilaku BRCB dan URCB
  • Evidence publishing dan subscription GOOSE
  • Validasi Direct dan SBO control
  • Akses disturbance-record file
Batas engineering

Acceptance FAT tetap bersifat prosedural.

ARSAS tidak menggantikan calibrated injection equipment, pengujian fungsi proteksi, approved drawing, settings review, cybersecurity control, witness sign-off, digital signature atau tanggung jawab vendor. IO List FAT tersedia pada mainline saat ini; verifikasi scope stable release sebelum penggunaan.

-
Workflow terkait

Lanjutkan dari factory ke site.

+
Berguna untuk

FAT proteksi dan substation automation.

  • Proof indication SDI dari IO List
  • Communication check relay dan bay controller
  • Perilaku BRCB dan URCB
  • Evidence publishing dan subscription GOOSE
  • Validasi Direct dan SBO control
  • Akses disturbance-record file
Batas engineering

Acceptance FAT tetap bersifat prosedural.

ARSAS tidak menggantikan calibrated injection equipment, pengujian fungsi proteksi, approved drawing, settings review, cybersecurity control, witness sign-off, digital signature atau tanggung jawab implementation. IO List FAT tersedia pada mainline saat ini; verifikasi scope stable release sebelum penggunaan.

+
Workflow terkait

Lanjutkan dari factory ke site.

{{> download-cta-id}} {{> footer}} From 1ce7b86008685f1cc3c5b709eb57afeaa9422f46 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:31 +0700 Subject: [PATCH 46/80] docs: update integration route references --- landing/templates/pengujian-sat-iec61850.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/pengujian-sat-iec61850.html b/landing/templates/pengujian-sat-iec61850.html index 00fce0de1..94cbf5fe0 100644 --- a/landing/templates/pengujian-sat-iec61850.html +++ b/landing/templates/pengujian-sat-iec61850.html @@ -18,7 +18,7 @@
Workflow yang disarankan

Konfirmasi apa yang berubah antara factory dan site.

Instalasi, addressing, gateway mapping, switch configuration dan final project file dapat menimbulkan perbedaan yang tidak terlihat saat FAT.

01

Verifikasi akses endpoint

Konfirmasi IP address, interface dan association MMS independen untuk perangkat yang sudah terpasang.

02

Bandingkan configured dan live context

Gunakan SCL sebagai project intent sambil memeriksa model dan service yang benar-benar ditawarkan IED.

03

Validasi report dan value

Konfirmasi coverage DataSet, state RCB, quality, timestamp dan reason-for-inclusion.

04

Periksa perilaku GOOSE

Bandingkan APPID, VLAN, destination MAC, sequence counter, retransmission dan ordered payload dengan site design.

05

Isolasi kegagalan integrasi

Gunakan diagnostik per IED untuk membedakan association, report, frame, gateway, file-service dan command-completion problem.

ARSAS memonitor beberapa IED IEC 61850 saat pengujian SAT

Session independen untuk perangkat terpasang

Model, monitoring, reporting, event, file dan diagnostik tetap dapat dikaitkan ke setiap IED saat sistem gardu diuji.

Berguna untuk

Validasi site end-to-end.

  • Connectivity relay dan bay controller terpasang
  • Investigasi signal path SCADA dan gateway
  • GOOSE subscription dan station-bus check
  • Evidence control completion dan feedback
  • Akses disturbance record
Batas engineering

SAT tetap merupakan aktivitas proyek terkontrol.

ARSAS tidak menggantikan approved site procedure, authorization keamanan network, injection test, wiring check, protection-setting verification, switching control atau customer acceptance record.

-
Workflow terkait

Lanjutkan ke commissioning dan integrasi.

+
Workflow terkait

Lanjutkan ke commissioning dan integrasi.

{{> download-cta-id}} {{> footer}} From ef555344590f08aaa008348d87156747c2152b3c Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:34 +0700 Subject: [PATCH 47/80] docs: update integration route references --- landing/templates/sat-testing.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/sat-testing.html b/landing/templates/sat-testing.html index 22b2ca465..4b2706e14 100644 --- a/landing/templates/sat-testing.html +++ b/landing/templates/sat-testing.html @@ -46,7 +46,7 @@
Useful for

End-to-end site validation.

  • Installed relay and bay-controller connectivity
  • SCADA and gateway signal-path investigation
  • GOOSE subscription and station-bus checks
  • Control completion and feedback evidence
  • Disturbance-record accessibility
Boundary

SAT remains a controlled project activity.

ARSAS does not replace approved site procedures, network-security authorization, injection testing, wiring checks, protection-setting verification, switching control or customer acceptance records.

-
Related workflows

Continue with commissioning and integration.

+
Related workflows

Continue with commissioning and integration.

{{> download-cta}} From 8015e87d184f018abf73ad020eff85c4ff7e95b6 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:39 +0700 Subject: [PATCH 48/80] docs: update integration route references --- landing/templates/scl-workspace.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/landing/templates/scl-workspace.html b/landing/templates/scl-workspace.html index ca832572d..5413ca240 100644 --- a/landing/templates/scl-workspace.html +++ b/landing/templates/scl-workspace.html @@ -15,13 +15,13 @@
Practical tutorial · ICD · CID · SCD · IID · live model

Understand the SCL file, compare it with the IED and export only what the next system needs.

ARSAS helps engineers open configured SCL, discover a live IED model from an approved IP address, preserve identity and relationships, and prepare bounded IID, ICD or CID output. The useful outcome is not simply “valid XML”—it is a traceable baseline that the receiving SAS, gateway or engineering tool can independently validate.

-
30-second takeaway

SCL is the engineering description that connects device capability, project configuration and system integration.

ICD commonly describes what an IED can support. CID commonly describes the configuration delivered to one IED. SCD represents the integrated substation project. IID is commonly used for an instantiated IED description exchanged during engineering updates. These are practical roles, not permission to ignore edition, schema, project-profile or vendor rules.

+
30-second takeaway

SCL is the engineering description that connects device capability, project configuration and system integration.

ICD commonly describes what an IED can support. CID commonly describes the configuration delivered to one IED. SCD represents the integrated substation project. IID is commonly used for an instantiated IED description exchanged during engineering updates. These are practical roles, not permission to ignore edition, schema, project-profile or implementation rules.

SCL file types in plain language

Choose the file by its job, not only its extension.

ICD — IED Capability Description

A capability-oriented description supplied for system engineering. It commonly represents the functions, data and communication features an IED family or configuration can offer.

CID — Configured IED Description

A device-focused configured description intended for one IED. It may include communication, DataSets, reports, GOOSE and project-specific settings.

SCD — Substation Configuration Description

The wider integrated system model produced by system configuration engineering, including multiple IEDs, communication relationships and substation context.

IID — Instantiated IED Description

An instantiated IED description used in supported engineering exchange or update workflows, especially where Edition 2-oriented tooling expects device-specific system context.

SSD — System Specification Description

A specification-oriented view of substation functions and topology before all IED implementation details are assigned.

Edition and profile matter

The same extension does not guarantee identical schema edition, namespace, optional content or importer behavior. Confirm the receiving contract.

-
When to use SCL Workspace

Use it when configured intent and live evidence must be compared or exchanged.

Missing or stale file

Build a typed baseline from live discovery

Use an approved IED endpoint when the available vendor file is missing, outdated or cannot explain the online server model.

Importer rejection

Reduce the input to a bounded device scope

Preserve exact identity, model, DataSet and RCB evidence while avoiding an oversized multi-IED project file for a limited receiver.

Integration review

Compare live model and configured intent

Keep differences visible instead of silently replacing the source file or assuming the running IED exactly matches the project SCL.

Reporting handover

Export one verified RCB path

Prepare a selected-RCB CID baseline when the receiving SAS or gateway needs one explicit reporting path.

+
When to use SCL Workspace

Use it when configured intent and live evidence must be compared or exchanged.

Missing or stale file

Build a typed baseline from live discovery

Use an approved IED endpoint when the available implementation file is missing, outdated or cannot explain the online server model.

Importer rejection

Reduce the input to a bounded device scope

Preserve exact identity, model, DataSet and RCB evidence while avoiding an oversized multi-IED project file for a limited receiver.

Integration review

Compare live model and configured intent

Keep differences visible instead of silently replacing the source file or assuming the running IED exactly matches the project SCL.

Reporting handover

Export one verified RCB path

Prepare a selected-RCB CID baseline when the receiving SAS or gateway needs one explicit reporting path.

-
Before you start

Ask what the receiving system actually accepts.

  • Required file type and IEC 61850 edition
  • Expected physical IED name and AccessPoint identity
  • Allowed namespace, schema and vendor profile
  • Required DataSets, RCBs, GOOSE or communication sections
  • Whether a full project, one IED or one selected RCB is required
Source boundary

Configured intent and live evidence are different sources.

An SCL file can be stale or incomplete. A live IED model can omit wider project relationships. Preserve both, record provenance and make differences reviewable rather than declaring either source universally correct.

+
Before you start

Ask what the receiving system actually accepts.

  • Required file type and IEC 61850 edition
  • Expected physical IED name and AccessPoint identity
  • Allowed namespace, schema and implementation profile
  • Required DataSets, RCBs, GOOSE or communication sections
  • Whether a full project, one IED or one selected RCB is required
Source boundary

Configured intent and live evidence are different sources.

An SCL file can be stale or incomplete. A live IED model can omit wider project relationships. Preserve both, record provenance and make differences reviewable rather than declaring either source universally correct.

Step-by-step in ARSAS

Create a bounded SCL baseline without losing the source context.

01

Define the receiving-system requirement

Record the target tool, edition, expected IED identity, required services and whether it needs IID, ICD, CID or another approved SCL scope.

02

Choose live discovery or an existing SCL source

Connect to an approved IED for complete MMS discovery, or open the available SCD, CID, ICD, IID, SSD or compatible XML. Keep the original file unchanged.

03

Verify physical identity and model hierarchy

Separate the physical IED name from MMS domains and Logical Devices. Review Logical Nodes, Data Objects, Data Attributes, types and Functional Constraints.

04

Review DataSets, RCBs and communication context

Check member order, exact runtime RCB names, trigger options, optional fields, report IDs, configuration revision, endpoint and available GOOSE or communication identity.

05

Choose the bounded target output

Generate Edition 2 IID or Edition 1 ICD from complete live discovery where appropriate, normalize a single-IED source, or select one verified RCB for a bounded CID baseline.

06

Validate the output in the receiving system

Review SCL, companion evidence and findings, then import into the intended SAS, gateway or engineering tool. Record every rejection or modification instead of editing identity by trial and error.

@@ -37,7 +37,7 @@
What is the difference between ICD, CID, SCD and IID?
ICD commonly describes IED capability, CID one configured IED, SCD the integrated substation project and IID an instantiated IED exchange or update description. Verify the exact edition and project profile.
Can a live IED model replace the project SCD?
No. Live discovery shows what one server exposes now. The SCD can contain wider system, subscriber and communication relationships that one IED cannot reveal.
Does valid XML guarantee the SCL will import correctly?
No. The receiver may apply identity, edition, namespace, DataSet, RCB and proprietary profile rules. Review the resulting imported model.
-
Next lesson

Compare the configured stream with observed GOOSE traffic.

Use GOOSE Analyzer to verify publisher identity, sequence, TAL and payload order, while keeping subscriber operation and end-to-end protection acceptance as separate tests.

+
Next lesson

Compare the configured stream with observed GOOSE traffic.

Use GOOSE Analyzer to verify publisher identity, sequence, TAL and payload order, while keeping subscriber operation and end-to-end protection acceptance as separate tests.

{{> download-cta}}
{{> footer}} From 4623248c019f3222f7fa508edc526359ede6725d Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:44 +0700 Subject: [PATCH 49/80] docs: update integration route references --- landing/templates/workspace-scl-iec61850.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/landing/templates/workspace-scl-iec61850.html b/landing/templates/workspace-scl-iec61850.html index 4a507a9e0..f39dd93eb 100644 --- a/landing/templates/workspace-scl-iec61850.html +++ b/landing/templates/workspace-scl-iec61850.html @@ -15,13 +15,13 @@
Tutorial praktis · ICD · CID · SCD · IID · live model

Pahami file SCL, bandingkan dengan IED dan export hanya yang dibutuhkan sistem berikutnya.

ARSAS membantu engineer membuka configured SCL, discovery live model dari alamat IP IED yang disetujui, mempertahankan identity serta relationship, lalu menyiapkan bounded output IID, ICD atau CID. Hasil yang berguna bukan hanya “XML valid”, tetapi baseline traceable yang dapat diverifikasi ulang oleh SAS, gateway atau engineering tool penerima.

-
Takeaway 30 detik

SCL adalah deskripsi engineering yang menghubungkan capability perangkat, konfigurasi proyek dan integrasi sistem.

ICD umumnya menjelaskan capability IED. CID umumnya menjelaskan konfigurasi untuk satu IED. SCD mewakili project gardu yang terintegrasi. IID lazim digunakan sebagai instantiated IED description pada workflow exchange atau update engineering. Peran praktis ini tidak menghapus aturan edition, schema, project profile atau vendor.

+
Takeaway 30 detik

SCL adalah deskripsi engineering yang menghubungkan capability perangkat, konfigurasi proyek dan integrasi sistem.

ICD umumnya menjelaskan capability IED. CID umumnya menjelaskan konfigurasi untuk satu IED. SCD mewakili project gardu yang terintegrasi. IID lazim digunakan sebagai instantiated IED description pada workflow exchange atau update engineering. Peran praktis ini tidak menghapus aturan edition, schema, project profile atau implementation.

Tipe file SCL dengan bahasa sederhana

Pilih file berdasarkan tugasnya, bukan hanya extension.

ICD — IED Capability Description

Deskripsi berorientasi capability untuk system engineering. Umumnya berisi fungsi, data dan communication feature yang dapat ditawarkan sebuah IED atau keluarga produk.

CID — Configured IED Description

Deskripsi configured yang berfokus pada satu IED. Dapat membawa communication, DataSet, report, GOOSE dan setting yang spesifik terhadap proyek.

SCD — Substation Configuration Description

Model sistem terintegrasi dari proses system configuration, termasuk banyak IED, communication relationship dan konteks substation.

IID — Instantiated IED Description

Deskripsi instantiated IED untuk workflow exchange atau update engineering yang didukung, terutama pada toolchain berorientasi Edition 2.

SSD — System Specification Description

View berorientasi specification terhadap fungsi dan topologi substation sebelum seluruh detail implementasi IED ditetapkan.

Edition dan profile tetap penting

Extension yang sama tidak menjamin edition schema, namespace, optional content atau behavior importer yang sama. Konfirmasi contract receiving system.

-
Kapan memakai Workspace SCL

Gunakan saat configured intent dan live evidence harus dibandingkan atau dipertukarkan.

File hilang atau stale

Buat typed baseline dari live discovery

Gunakan endpoint IED yang disetujui saat vendor file hilang, outdated atau tidak dapat menjelaskan online server model.

Importer menolak file

Kurangi input menjadi bounded device scope

Pertahankan exact identity, model, DataSet dan RCB evidence tanpa mengirim project multi-IED yang terlalu besar ke receiver terbatas.

Review integrasi

Bandingkan live model dengan configured intent

Biarkan perbedaan terlihat. Jangan diam-diam mengganti source file atau berasumsi running IED identik dengan project SCL.

Handover reporting

Export satu verified RCB path

Siapkan baseline CID selected-RCB ketika SAS atau gateway penerima hanya membutuhkan satu reporting path yang eksplisit.

+
Kapan memakai Workspace SCL

Gunakan saat configured intent dan live evidence harus dibandingkan atau dipertukarkan.

File hilang atau stale

Buat typed baseline dari live discovery

Gunakan endpoint IED yang disetujui saat implementation file hilang, outdated atau tidak dapat menjelaskan online server model.

Importer menolak file

Kurangi input menjadi bounded device scope

Pertahankan exact identity, model, DataSet dan RCB evidence tanpa mengirim project multi-IED yang terlalu besar ke receiver terbatas.

Review integrasi

Bandingkan live model dengan configured intent

Biarkan perbedaan terlihat. Jangan diam-diam mengganti source file atau berasumsi running IED identik dengan project SCL.

Handover reporting

Export satu verified RCB path

Siapkan baseline CID selected-RCB ketika SAS atau gateway penerima hanya membutuhkan satu reporting path yang eksplisit.

-
Sebelum mulai

Tanyakan apa yang benar-benar diterima receiving system.

  • Tipe file dan IEC 61850 edition yang diperlukan
  • Physical IED name dan AccessPoint identity yang diharapkan
  • Namespace, schema dan vendor profile yang diperbolehkan
  • DataSet, RCB, GOOSE atau communication section yang dibutuhkan
  • Apakah receiver memerlukan full project, satu IED atau satu selected RCB
Batas source

Configured intent dan live evidence adalah dua sumber berbeda.

SCL dapat stale atau incomplete. Live IED model dapat tidak membawa relationship proyek yang lebih luas. Pertahankan keduanya, catat provenance dan buat perbedaan dapat direview.

+
Sebelum mulai

Tanyakan apa yang benar-benar diterima receiving system.

  • Tipe file dan IEC 61850 edition yang diperlukan
  • Physical IED name dan AccessPoint identity yang diharapkan
  • Namespace, schema dan implementation profile yang diperbolehkan
  • DataSet, RCB, GOOSE atau communication section yang dibutuhkan
  • Apakah receiver memerlukan full project, satu IED atau satu selected RCB
Batas source

Configured intent dan live evidence adalah dua sumber berbeda.

SCL dapat stale atau incomplete. Live IED model dapat tidak membawa relationship proyek yang lebih luas. Pertahankan keduanya, catat provenance dan buat perbedaan dapat direview.

Langkah di ARSAS

Buat baseline SCL yang bounded tanpa kehilangan source context.

01

Tentukan requirement receiving system

Catat target tool, edition, expected IED identity, service yang dibutuhkan dan apakah memerlukan IID, ICD, CID atau scope SCL lain yang disetujui.

02

Pilih live discovery atau source SCL existing

Hubungkan IED yang disetujui untuk complete MMS discovery, atau buka SCD, CID, ICD, IID, SSD maupun XML kompatibel yang tersedia. File source asli tetap tidak berubah.

03

Verifikasi physical identity dan model hierarchy

Pisahkan physical IED name dari MMS domain serta Logical Device. Review Logical Node, Data Object, Data Attribute, type dan Functional Constraint.

04

Review DataSet, RCB dan communication context

Periksa urutan member, exact runtime RCB name, trigger option, optional field, report ID, configuration revision, endpoint serta identity GOOSE/communication yang tersedia.

05

Pilih bounded target output

Generate IID Edition 2 atau ICD Edition 1 dari complete live discovery jika sesuai, normalize source menjadi single-IED, atau pilih satu verified RCB untuk baseline CID yang bounded.

06

Validasi output pada receiving system

Review SCL, companion evidence dan findings, lalu import ke SAS, gateway atau engineering tool yang dituju. Catat setiap rejection atau modification, jangan mengubah identity dengan trial and error.

@@ -37,7 +37,7 @@
Apa perbedaan ICD, CID, SCD dan IID?
ICD umumnya menjelaskan capability IED, CID satu IED configured, SCD project gardu terintegrasi dan IID deskripsi instantiated IED untuk exchange atau update. Verifikasi edition dan project profile yang berlaku.
Apakah live model IED dapat menggantikan project SCD?
Tidak. Live discovery memperlihatkan apa yang ditawarkan satu server sekarang. SCD dapat membawa system, subscriber dan communication relationship yang tidak dapat diungkap satu IED.
Apakah XML valid menjamin SCL dapat diimport dengan benar?
Tidak. Receiver dapat menerapkan aturan identity, edition, namespace, DataSet, RCB dan proprietary profile. Review model hasil import.
-
Pelajaran berikutnya

Bandingkan configured stream dengan traffic GOOSE yang diamati.

Gunakan Analyzer GOOSE untuk memverifikasi publisher identity, sequence, TAL dan payload order, sementara subscriber operation serta acceptance proteksi end-to-end tetap menjadi test terpisah.

+
Pelajaran berikutnya

Bandingkan configured stream dengan traffic GOOSE yang diamati.

Gunakan Analyzer GOOSE untuk memverifikasi publisher identity, sequence, TAL dan payload order, sementara subscriber operation serta acceptance proteksi end-to-end tetap menjadi test terpisah.

{{> download-cta-id}}
{{> footer}} From 7e851caacf5464183472884eeb20d5e6d221961b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:22:52 +0700 Subject: [PATCH 50/80] ci: validate neutral localization routes --- scripts/validate-product-source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/validate-product-source.py b/scripts/validate-product-source.py index f78a870df..6ef8ed3f7 100644 --- a/scripts/validate-product-source.py +++ b/scripts/validate-product-source.py @@ -34,7 +34,7 @@ "file-transfer.html": "transfer-file-comtrade-iec61850.html", "scl-workspace.html": "workspace-scl-iec61850.html", "io-list-fat-evidence.html": "bukti-fat-iolist-iec61850.html", "fat-testing.html": "pengujian-fat-iec61850.html", "sat-testing.html": "pengujian-sat-iec61850.html", - "commissioning.html": "commissioning-iec61850.html", "multi-vendor-integration.html": "integrasi-multi-vendor-iec61850.html", + "commissioning.html": "commissioning-iec61850.html", "multi-device-integration.html": "integrasi-multi-perangkat-iec61850.html", } KNOWN_TOKENS = { "ARSAS_VERSION", "PRODUCT_NAME", "CANONICAL_ROOT", "REPOSITORY_URL", "ENGINE_REPOSITORY_URL", From 67e9b37d8f4b9a11312e2a6452a832fbe7dea5b7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:23:47 +0700 Subject: [PATCH 51/80] docs: remove implementation-specific branding language --- landing/search-authority.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/landing/search-authority.json b/landing/search-authority.json index b0db79359..34fd39347 100644 --- a/landing/search-authority.json +++ b/landing/search-authority.json @@ -39,8 +39,8 @@ "pengujian-sat-iec61850.html": {"cluster": "commissioning", "role": "solution", "label": {"en": "IEC 61850 SAT in Indonesian", "id": "Pengujian SAT IEC 61850"}, "summary": {"en": "Indonesian SAT evidence workflow.", "id": "Workflow evidence SAT dalam Bahasa Indonesia."}, "primaryQuery": "pengujian SAT IEC 61850"}, "commissioning.html": {"cluster": "commissioning", "role": "solution", "label": {"en": "IEC 61850 Commissioning", "id": "Commissioning IEC 61850"}, "summary": {"en": "Traceable commissioning evidence from connection to final operation.", "id": "Evidence commissioning yang traceable dari koneksi sampai final operation."}, "primaryQuery": "IEC 61850 commissioning tool"}, "commissioning-iec61850.html": {"cluster": "commissioning", "role": "solution", "label": {"en": "IEC 61850 Commissioning in Indonesian", "id": "Commissioning IEC 61850"}, "summary": {"en": "Indonesian commissioning evidence workflow.", "id": "Workflow evidence commissioning dalam Bahasa Indonesia."}, "primaryQuery": "commissioning IEC 61850"}, - "multi-vendor-integration.html": {"cluster": "scl", "role": "solution", "label": {"en": "Multi-vendor IEC 61850 Integration", "id": "Integrasi Multi-vendor IEC 61850"}, "summary": {"en": "Separate configured intent, live behavior and vendor-specific boundaries.", "id": "Pisahkan configured intent, behavior live dan batas vendor-specific."}, "primaryQuery": "multi vendor IEC 61850 integration"}, - "integrasi-multi-vendor-iec61850.html": {"cluster": "scl", "role": "solution", "label": {"en": "Multi-vendor Integration in Indonesian", "id": "Integrasi Multi-vendor IEC 61850"}, "summary": {"en": "Indonesian multi-vendor evidence workflow.", "id": "Workflow evidence multi-vendor dalam Bahasa Indonesia."}, "primaryQuery": "integrasi multi vendor IEC 61850"}, + "multi-device-integration.html": {"cluster": "scl", "role": "solution", "label": {"en": "Multi-device IEC 61850 Integration", "id": "Integrasi Multi-device IEC 61850"}, "summary": {"en": "Separate configured intent, live behavior and implementation-specific boundaries.", "id": "Pisahkan configured intent, behavior live dan batas implementation-specific."}, "primaryQuery": "multi device IEC 61850 integration"}, + "integrasi-multi-perangkat-iec61850.html": {"cluster": "scl", "role": "solution", "label": {"en": "Multi-device Integration in Indonesian", "id": "Integrasi Multi-device IEC 61850"}, "summary": {"en": "Indonesian multi-device evidence workflow.", "id": "Workflow evidence multi-device dalam Bahasa Indonesia."}, "primaryQuery": "integrasi multi device IEC 61850"}, "reporting-silent.html": {"cluster": "reporting", "role": "guide", "label": {"en": "Reporting is silent", "id": "Reporting tidak masuk"}, "summary": {"en": "Separate association, reads, DataSet coverage, RCB enablement and delivery evidence.", "id": "Pisahkan association, read, coverage DataSet, enablement RCB dan evidence delivery."}, "primaryQuery": "IEC 61850 reporting not working"}, "brcb-vs-urcb.html": {"cluster": "reporting", "role": "guide", "label": {"en": "BRCB versus URCB", "id": "BRCB dibanding URCB"}, "summary": {"en": "Choose and test buffered or unbuffered reporting without assuming continuity.", "id": "Pilih dan uji buffered atau unbuffered reporting tanpa mengasumsikan continuity."}, "primaryQuery": "BRCB vs URCB"}, "rcb-reserved.html": {"cluster": "reporting", "role": "guide", "label": {"en": "RCB reserved or occupied", "id": "RCB reserved atau occupied"}, "summary": {"en": "Inspect ownership and reservation without forcing takeover.", "id": "Periksa ownership dan reservation tanpa forced takeover."}, "primaryQuery": "IEC 61850 RCB reserved"}, @@ -48,7 +48,7 @@ "port-102-connection-failed.html": {"cluster": "connection", "role": "guide", "label": {"en": "Port 102 connection failed", "id": "Koneksi port 102 gagal"}, "summary": {"en": "Separate routing, ping, TCP 102, firewall, association and application evidence.", "id": "Pisahkan routing, ping, TCP 102, firewall, association dan evidence aplikasi."}, "primaryQuery": "IEC 61850 port 102 connection failed"}, "comtrade-download.html": {"cluster": "file-transfer", "role": "guide", "label": {"en": "COMTRADE download troubleshooting", "id": "Troubleshooting download COMTRADE"}, "summary": {"en": "Distinguish file-service support, transfer success and complete record sets.", "id": "Bedakan dukungan file service, transfer berhasil dan record set lengkap."}, "primaryQuery": "IEC 61850 COMTRADE download"}, "goose-sequence.html": {"cluster": "goose", "role": "guide", "label": {"en": "GOOSE stNum and sqNum", "id": "GOOSE stNum dan sqNum"}, "summary": {"en": "Interpret state changes, retransmissions, TAL and capture continuity together.", "id": "Interpretasikan state change, retransmission, TAL dan capture continuity bersama-sama."}, "primaryQuery": "GOOSE stNum sqNum"}, - "cid-rejected.html": {"cluster": "scl", "role": "guide", "label": {"en": "CID or SCL rejected", "id": "CID atau SCL ditolak"}, "summary": {"en": "Inspect XML, namespace, edition, references and vendor requirements.", "id": "Periksa XML, namespace, edition, reference dan kebutuhan vendor."}, "primaryQuery": "IEC 61850 CID rejected"}, + "cid-rejected.html": {"cluster": "scl", "role": "guide", "label": {"en": "CID or SCL rejected", "id": "CID atau SCL ditolak"}, "summary": {"en": "Inspect XML, namespace, edition, references and implementation requirements.", "id": "Periksa XML, namespace, edition, reference dan kebutuhan implementasi."}, "primaryQuery": "IEC 61850 CID rejected"}, "live-model-vs-scl.html": {"cluster": "scl", "role": "guide", "label": {"en": "Live model versus SCL", "id": "Live model dibanding SCL"}, "summary": {"en": "Use SCL as configured intent and the online model as current server evidence.", "id": "Gunakan SCL sebagai configured intent dan model online sebagai evidence server aktif."}, "primaryQuery": "IEC 61850 live model vs SCL"}, "direct-vs-sbo.html": {"cluster": "control", "role": "guide", "label": {"en": "Direct versus SBO control", "id": "Control Direct dibanding SBO"}, "summary": {"en": "Confirm the control model and service sequence before operating.", "id": "Konfirmasi control model dan urutan service sebelum operate."}, "primaryQuery": "IEC 61850 direct vs SBO"}, "commandtermination-addcause.html": {"cluster": "control", "role": "guide", "label": {"en": "CommandTermination and AddCause", "id": "CommandTermination dan AddCause"}, "summary": {"en": "Separate immediate acceptance from final process completion evidence.", "id": "Pisahkan immediate acceptance dari evidence final process completion."}, "primaryQuery": "IEC 61850 CommandTermination AddCause"} @@ -68,8 +68,8 @@ "analyzer-goose-iec61850.html": ["apa-itu-iec61850.html", "goose-sequence.html", "pengujian-fat-iec61850.html", "pengujian-sat-iec61850.html"], "file-transfer.html": ["comtrade-download.html", "port-102-connection-failed.html", "commissioning.html"], "transfer-file-comtrade-iec61850.html": ["comtrade-download.html", "port-102-connection-failed.html", "commissioning-iec61850.html"], - "scl-workspace.html": ["what-is-iec61850.html", "cid-rejected.html", "live-model-vs-scl.html", "multi-vendor-integration.html"], - "workspace-scl-iec61850.html": ["apa-itu-iec61850.html", "cid-rejected.html", "live-model-vs-scl.html", "integrasi-multi-vendor-iec61850.html"], + "scl-workspace.html": ["what-is-iec61850.html", "cid-rejected.html", "live-model-vs-scl.html", "multi-device-integration.html"], + "workspace-scl-iec61850.html": ["apa-itu-iec61850.html", "cid-rejected.html", "live-model-vs-scl.html", "integrasi-multi-perangkat-iec61850.html"], "control.html": ["direct-vs-sbo.html", "commandtermination-addcause.html", "commissioning.html"], "multi-ied-monitoring.html": ["reporting-silent.html", "port-102-connection-failed.html", "smart-reporting.html"], "io-list-fat-evidence.html": ["learning-center.html", "fat-testing.html", "reporting-silent.html", "live-model-vs-scl.html"], @@ -80,8 +80,8 @@ "pengujian-sat-iec61850.html": ["port-102-connection-failed.html", "goose-sequence.html", "commandtermination-addcause.html"], "commissioning.html": ["connect-ied-ip-arsas.html", "port-102-connection-failed.html", "comtrade-download.html", "reporting-silent.html", "commandtermination-addcause.html"], "commissioning-iec61850.html": ["cara-hubungkan-ied-ip-arsas.html", "port-102-connection-failed.html", "comtrade-download.html", "reporting-silent.html", "commandtermination-addcause.html"], - "multi-vendor-integration.html": ["cid-rejected.html", "live-model-vs-scl.html", "brcb-vs-urcb.html"], - "integrasi-multi-vendor-iec61850.html": ["cid-rejected.html", "live-model-vs-scl.html", "brcb-vs-urcb.html"], + "multi-device-integration.html": ["cid-rejected.html", "live-model-vs-scl.html", "brcb-vs-urcb.html"], + "integrasi-multi-perangkat-iec61850.html": ["cid-rejected.html", "live-model-vs-scl.html", "brcb-vs-urcb.html"], "reporting-silent.html": ["smart-reporting.html", "rcb-reserved.html", "empty-dataset.html"], "brcb-vs-urcb.html": ["smart-reporting.html", "rcb-reserved.html", "reporting-silent.html"], "rcb-reserved.html": ["smart-reporting.html", "brcb-vs-urcb.html", "reporting-silent.html"], @@ -89,8 +89,8 @@ "port-102-connection-failed.html": ["connect-ied-ip-arsas.html", "mms-client.html", "file-transfer.html", "commissioning.html"], "comtrade-download.html": ["file-transfer.html", "port-102-connection-failed.html", "commissioning.html"], "goose-sequence.html": ["goose-analyzer.html", "fat-testing.html", "sat-testing.html"], - "cid-rejected.html": ["scl-workspace.html", "live-model-vs-scl.html", "multi-vendor-integration.html"], - "live-model-vs-scl.html": ["scl-workspace.html", "cid-rejected.html", "multi-vendor-integration.html"], + "cid-rejected.html": ["scl-workspace.html", "live-model-vs-scl.html", "multi-device-integration.html"], + "live-model-vs-scl.html": ["scl-workspace.html", "cid-rejected.html", "multi-device-integration.html"], "direct-vs-sbo.html": ["control.html", "commandtermination-addcause.html", "fat-testing.html"], "commandtermination-addcause.html": ["control.html", "direct-vs-sbo.html", "sat-testing.html"] } From 00cf2a0e25d403d8bc8b6f27bcd1a64008d5d2b7 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:23:52 +0700 Subject: [PATCH 52/80] docs: remove implementation-specific branding language --- landing/device-evidence.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/landing/device-evidence.json b/landing/device-evidence.json index 9a17341ec..787304dac 100644 --- a/landing/device-evidence.json +++ b/landing/device-evidence.json @@ -14,7 +14,7 @@ { "id": "field-profile-a-file-service", "publicLabel": "Anonymized field profile A — MMS file-service transport", - "identityDisclosure": "Vendor, model, installation and firmware are not published.", + "identityDisclosure": "Device model, installation and firmware identifiers are not published.", "evidenceDate": "2026-07", "services": { "mmsAssociation": "observed", @@ -44,7 +44,7 @@ { "id": "field-profile-b-rcb-export", "publicLabel": "Anonymized field profile B — RCB and SCL export evidence", - "identityDisclosure": "Vendor, model, installation and firmware are not published.", + "identityDisclosure": "Device model, installation and firmware identifiers are not published.", "evidenceDate": "2026-07", "services": { "mmsAssociation": "observed", @@ -59,7 +59,7 @@ "conditions": [ "Live RCB names, TriggerOptions, OptionalFields, BufTm and IntgPd were preserved as read-only evidence.", "Selected-RCB export retained the exact live MMS RCB identity and avoided duplicate instance suffixes.", - "Exported SCL remains an engineering baseline and requires independent schema, project and vendor validation.", + "Exported SCL remains an engineering baseline and requires independent schema, project and implementation validation.", "No runtime RCB reservation or write is implied by this evidence profile." ], "evidenceLinks": [ From 0c4b2e6ce0fb48c15d3cbbb9525d1df616c047a2 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:23:55 +0700 Subject: [PATCH 53/80] docs: remove implementation-specific branding language --- landing/partials/guide-boundary.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/partials/guide-boundary.html b/landing/partials/guide-boundary.html index fd4779f18..1d23dc138 100644 --- a/landing/partials/guide-boundary.html +++ b/landing/partials/guide-boundary.html @@ -1 +1 @@ -
Engineering boundary

Use the evidence to narrow the cause—not to claim conformance.

ARSAS can expose live model, report, GOOSE, file, control and diagnostic evidence. Final conclusions still depend on the approved project design, vendor documentation, test procedure, network access and independent verification.

Technical review

Written and reviewed by Ari Sulistiono.

Reviewed 20 July 2026 for practical IEC 61850 laboratory, FAT, SAT, commissioning and troubleshooting use.

+
Engineering boundary

Use the evidence to narrow the cause—not to claim conformance.

ARSAS can expose live model, report, GOOSE, file, control and diagnostic evidence. Final conclusions still depend on the approved project design, device documentation, test procedure, network access and independent verification.

Technical review

Written and reviewed by Ari Sulistiono.

Reviewed 20 July 2026 for practical IEC 61850 laboratory, FAT, SAT, commissioning and troubleshooting use.

From 9c8e8fe714018f8dd25d3a3c0bf224ed490e7dd0 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:23:59 +0700 Subject: [PATCH 54/80] docs: remove implementation-specific branding language --- landing/templates/technical-review.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing/templates/technical-review.html b/landing/templates/technical-review.html index f1684b21b..bce86b924 100644 --- a/landing/templates/technical-review.html +++ b/landing/templates/technical-review.html @@ -32,7 +32,7 @@
Authority · evidence · engineering boundary

Technical review is part of the product—not decorative copy.

ARSAS documentation distinguishes what the current software visibly demonstrates, what depends on relay support or project permissions, what is still preview quality, and what remains roadmap intent.

-
Current evidence

Claims must point to inspectable behavior.

  • Current application screenshots and visible UI states
  • Deterministic source and rendered-site validation
  • Public release packages and SHA-256 checksums
  • Source history, issue context and implementation boundaries
Not a conformance certificate

Evidence narrows causes; it does not replace project acceptance.

  • Final conclusions depend on the approved design and test procedure
  • Vendor-specific behavior must be checked against vendor documentation
  • Network access, permissions and IED configuration remain project inputs
  • Independent verification remains required where the project demands it
+
Current evidence

Claims must point to inspectable behavior.

  • Current application screenshots and visible UI states
  • Deterministic source and rendered-site validation
  • Public release packages and SHA-256 checksums
  • Source history, issue context and implementation boundaries
Not a conformance certificate

Evidence narrows causes; it does not replace project acceptance.

  • Final conclusions depend on the approved design and test procedure
  • Implementation-specific behavior must be checked against device documentation
  • Network access, permissions and IED configuration remain project inputs
  • Independent verification remains required where the project demands it
Claim governance

Four labels keep product communication honest.

ARSAS pages and guides are written to preserve the difference between implemented behavior and future intent.

Available

Implemented and inspectable

The workflow is present in the current software and can be examined through the application, release artifact or source.

Conditional

Depends on device or project conditions

Support can depend on IED services, writable attributes, network capture access, control model, permissions or configured engineering data.

Preview

Useful but still maturing

The workflow is available for evaluation while interoperability breadth, UX or diagnostic coverage continues to mature.

Roadmap

Planned, not promised as current

Roadmap content describes direction and is never presented as part of the current stable capability set.

From 7e81b9d14945fc4b3b8b8dab210b14f79171a8a9 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 15:24:02 +0700 Subject: [PATCH 55/80] docs: remove implementation-specific branding language --- landing/templates/about.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/about.html b/landing/templates/about.html index 4dcca9e4a..42b2eb704 100644 --- a/landing/templates/about.html +++ b/landing/templates/about.html @@ -42,9 +42,9 @@ {{> header}}
-
Engineering purpose · open source · practical evidence

Built to make practical IEC 61850 work clearer and faster.

ARSAS is an independent open-source Windows engineering workstation created to reduce repetitive setup during IEC 61850 laboratory work, FAT, SAT, commissioning, troubleshooting and multi-vendor integration.

+
Engineering purpose · open source · practical evidence

Built to make practical IEC 61850 work clearer and faster.

ARSAS is an independent open-source Windows engineering workstation created to reduce repetitive setup during IEC 61850 laboratory work, FAT, SAT, commissioning, troubleshooting and multi-device integration.

-
Why ARSAS exists

Useful testing should begin from the evidence available in the IED.

IEC 61850 workflows can lose significant time before the first useful value appears: preparing engineering files, inspecting DataSets, finding usable Report Control Blocks, mapping signals across tools and diagnosing why communication works while reporting remains silent.

ARSAS brings live MMS discovery, reporting visibility, GOOSE supervision, file transfer, SCL context, control evidence and diagnostics into one focused workspace.

Project principle

Complement engineering tools—not hide engineering reality.

ARSAS does not claim universal interoperability, conformance certification, switching authority or replacement of approved vendor engineering systems. It is designed to expose live behavior, preserve negative evidence and help engineers make better-informed decisions.

Capability status, preview scope, limitations and safety boundaries remain explicit throughout the product website.

+
Why ARSAS exists

Useful testing should begin from the evidence available in the IED.

IEC 61850 workflows can lose significant time before the first useful value appears: preparing engineering files, inspecting DataSets, finding usable Report Control Blocks, mapping signals across tools and diagnosing why communication works while reporting remains silent.

ARSAS brings live MMS discovery, reporting visibility, GOOSE supervision, file transfer, SCL context, control evidence and diagnostics into one focused workspace.

Project principle

Complement engineering tools—not hide engineering reality.

ARSAS does not claim universal interoperability, conformance certification, switching authority or replacement of approved engineering systems. It is designed to expose live behavior, preserve negative evidence and help engineers make better-informed decisions.

Capability status, preview scope, limitations and safety boundaries remain explicit throughout the product website.

Author and maintainer

{{AUTHOR_NAME}}

Ari Sulistiono is a substation automation engineer focused on protection and control, IEC 61850 communication, testing, commissioning, troubleshooting and practical engineering productivity.

ARSAS turns recurring field and integration problems into a reusable public engineering tool while keeping customer, employer, credential, relay-setting and substation data outside the repository.

Open-source project

Source, architecture and issue history are public.

Developers, contributors and technical reviewers can inspect the implementation, license, engineering boundaries and project history on GitHub. Product documentation and release guidance remain available throughout this website.

From b42c2cf1828d57abfbecb494bb5b19b064580673 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:10:19 +0700 Subject: [PATCH 56/80] fix(ci): repair vendor-neutral source cleanliness gate --- scripts/verify-source-clean.ps1 | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/scripts/verify-source-clean.ps1 b/scripts/verify-source-clean.ps1 index d985c3682..fbce0f791 100644 --- a/scripts/verify-source-clean.ps1 +++ b/scripts/verify-source-clean.ps1 @@ -24,6 +24,19 @@ $ForbiddenFilePatterns = @( "*.nupkg", "*.snupkg", "*.pcap", "*.pcapng", "*.etl", "*.binlog", "*.log", "*.tmp", "*.cache", "*.suo", "*.user", "*.rsuser", "*.pdf", "*.chm", "*.hlp" +) + +$ForbiddenIdentifierHashes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +@( + "dee5292b6aa3319833a7fb015d79494b0f1b69c3dc90258b39c042db388ccd71", + "1343b354d479ded45dde0d7f4ea01daddf1d411a669724f9f3e3de78db038ffc", + "439003d0d54d022f61a705da700bff916414fdf8308f0cae6a5b9e5903e86fdf", + "bec65696741e77e0dd0de446b99fe3c069edb3a8f5c81a9939f9813b33e595ea", + "4ed56753cb552f928aca8147069753f0f3741e28598c56533d8cddcd79fa574e", + "bbfd365f0891c3e0205503f5d2a1678a0a6ea60d68f3dcc174ed4f60dd87e708", + "d6a2feb71892b018d0ffec8d3cd438dabe599369d5a1921c7044137146107230", + "048832a53880fe4fc5feeee9fa0ae445b143c99a956356bee231d3faadbb7af0", + "0e443fe512c39ce723fc1be519b8e2a13a4ba75916989123078b59308480b2f8", "30e363d3e8c59f2c1319f8d73d48e3ad26db5e087951a4d7ab809c6f5401aea8", "43d7a9de7c6a018c3dfb8a0de38ae060237b942c300db67147e1b56a953b122c", "630ba09448af522154f38ef7685ef1f44b0f3e9430f80829a03ce24f400f3754", @@ -48,21 +61,7 @@ $ForbiddenFilePatterns = @( "ceaec657ec812681d45ba17bb58814b410d4212afa9ba97cefc67179656db96b", "1de2ae12bdc9a7e1e269321868f7f655bd166f9c4183a47b88f6b637fa23e673", "36e26473a4e8b5ce603a8cb3458b59c39d05fdb87fff30b18b65bad35ed9640e", - "95bad119f2292fb436837f2bfe73245627cd692a5e16e6f2e73b7e16b5ce7f6d", - -) - -$ForbiddenIdentifierHashes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) -@( - "dee5292b6aa3319833a7fb015d79494b0f1b69c3dc90258b39c042db388ccd71", - "1343b354d479ded45dde0d7f4ea01daddf1d411a669724f9f3e3de78db038ffc", - "439003d0d54d022f61a705da700bff916414fdf8308f0cae6a5b9e5903e86fdf", - "bec65696741e77e0dd0de446b99fe3c069edb3a8f5c81a9939f9813b33e595ea", - "4ed56753cb552f928aca8147069753f0f3741e28598c56533d8cddcd79fa574e", - "bbfd365f0891c3e0205503f5d2a1678a0a6ea60d68f3dcc174ed4f60dd87e708", - "d6a2feb71892b018d0ffec8d3cd438dabe599369d5a1921c7044137146107230", - "048832a53880fe4fc5feeee9fa0ae445b143c99a956356bee231d3faadbb7af0", - "0e443fe512c39ce723fc1be519b8e2a13a4ba75916989123078b59308480b2f8" + "95bad119f2292fb436837f2bfe73245627cd692a5e16e6f2e73b7e16b5ce7f6d" ) | ForEach-Object { [void]$ForbiddenIdentifierHashes.Add($_) } $CandidateLengths = [System.Collections.Generic.HashSet[int]]::new() From 1029ec22660cde4c83fbcd9b951dcaac937e45bf Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:10:23 +0700 Subject: [PATCH 57/80] test(ci): align convergence assertions with neutral documentation --- tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs b/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs index fee7768af..4792cd6ef 100644 --- a/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs +++ b/tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs @@ -371,14 +371,14 @@ public void InteroperabilityConvergenceContract_PhysicalRetestPassedAndMergeRead Assert.Contains("\"mergeAllowedAfterPhysicalRetest\": true", contract, StringComparison.Ordinal); Assert.Contains("\"physicalRetestRequired\": false", contract, StringComparison.Ordinal); Assert.Contains("\"physicalRetestPassed\": true", contract, StringComparison.Ordinal); - Assert.Contains("Merged proven baseline", documentation, StringComparison.Ordinal); - Assert.Contains("The field result, not test count alone", documentation, StringComparison.Ordinal); + Assert.Contains("Current stable baseline", documentation, StringComparison.Ordinal); + Assert.Contains("The physical result, not test count alone", documentation, StringComparison.Ordinal); var guard = File.ReadAllText(FindRepoFile(".github/workflows/interoperability-convergence-guard.yml")); Assert.Contains("name: Interoperability Convergence Guard", guard, StringComparison.Ordinal); Assert.Contains("name: interoperability-convergence-contract", guard, StringComparison.Ordinal); Assert.Contains("Merged engine authority must remain PR #134 + PR #135 with ARSAS #324 provenance", guard, StringComparison.Ordinal); - Assert.Contains("P0 structural discovery freeze regressed away from the accepted Interoperability convergence path", guard, StringComparison.Ordinal); + Assert.Contains("P0 structural discovery freeze regressed away from the accepted interoperability convergence path", guard, StringComparison.Ordinal); Assert.Contains("Canonical IEC model / SCL semantic authority regressed", guard, StringComparison.Ordinal); } From ca5792d3cadfd60d42146e4dd7a3a8cdfafd0826 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:16:46 +0700 Subject: [PATCH 58/80] perf(ci): reuse SHA256 during source cleanliness scan --- scripts/verify-source-clean.ps1 | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/scripts/verify-source-clean.ps1 b/scripts/verify-source-clean.ps1 index fbce0f791..b6af25977 100644 --- a/scripts/verify-source-clean.ps1 +++ b/scripts/verify-source-clean.ps1 @@ -88,17 +88,13 @@ function Normalize-RelativePath { return $Path.Replace('\', '/').TrimStart('/') } +$Sha256Algorithm = [System.Security.Cryptography.SHA256]::Create() + function Get-Sha256Hex { param([Parameter(Mandatory=$true)][string]$Value) - $algorithm = [System.Security.Cryptography.SHA256]::Create() - try { - $bytes = [System.Text.Encoding]::UTF8.GetBytes($Value) - return -join ($algorithm.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) - } - finally { - $algorithm.Dispose() - } + $bytes = [System.Text.Encoding]::UTF8.GetBytes($Value) + return ([System.BitConverter]::ToString($Sha256Algorithm.ComputeHash($bytes))).Replace("-", "").ToLowerInvariant() } function Test-ContainsForbiddenIdentifier { From 18d260b16ff344a6a9a42238fdc1cc7439cf89e9 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:21:32 +0700 Subject: [PATCH 59/80] fix(ci): scope identifier gate to approved public exclusions --- scripts/verify-source-clean.ps1 | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/scripts/verify-source-clean.ps1 b/scripts/verify-source-clean.ps1 index b6af25977..19418cea6 100644 --- a/scripts/verify-source-clean.ps1 +++ b/scripts/verify-source-clean.ps1 @@ -38,34 +38,11 @@ $ForbiddenIdentifierHashes = [System.Collections.Generic.HashSet[string]]::new([ "048832a53880fe4fc5feeee9fa0ae445b143c99a956356bee231d3faadbb7af0", "0e443fe512c39ce723fc1be519b8e2a13a4ba75916989123078b59308480b2f8", "30e363d3e8c59f2c1319f8d73d48e3ad26db5e087951a4d7ab809c6f5401aea8", - "43d7a9de7c6a018c3dfb8a0de38ae060237b942c300db67147e1b56a953b122c", - "630ba09448af522154f38ef7685ef1f44b0f3e9430f80829a03ce24f400f3754", - "e5812a10ae5b2cb03cd875c187e116bec38348c6687df3d7ae88736f8d9ad66a", - "c378ee41c2783d081fb4f6db9fb854a46caafe455d3b64143e2239ef2cecd045", - "8813d73ee94207a67d5ef2605eb6aebc9108ab61f311e4c76f988a0c24b4ea82", - "020dae383219bf785518e1af41f1a8f5ed14db6eeb5fa8219d806846cb1129f2", - "63bbfcfa5a6df7b03a5fd45c6526f66b13fc5a46494a1ed66e4ea70d436a222a", - "c5f975b35c72cfe2da72009c9866a7ce5eafadce57af395c9c1b6c94b383ed2c", - "0e357968efdabb541e3e79fac718a1d4fd05bed3b95ccde6995589316afb250c", - "fa9ab9821527495b9c58ac84f4c00c6a9f82c01c96bfa18809a72b9d91dff228", - "8fb642282c29a0c6bf16259283ae675b892cf995485215a6ae0d5f1cfa04d2d9", - "7f92427e2c816b6d22084fd490da8fed3423cc1b9f62407f41c75a0b9a5dfcd9", - "93161c7754014286c483f69f9c603e906f471b6e3b0d2a0ab950d3db239fdc41", - "7e175017f49e9d14104795cda400d40eb26bec6a0fd7af54dfe46c67b6352132", - "c8ee81d53def32998740908ca3f9b45b74eadb61bbf7f46a13f4d6935b5a096c", - "5c373ed1848509788e445369163f3db4bc550336840f5708079068c46ab1b2e3", - "32b97c36b6a25a163cb5c420d302c382d99bb277993f42afb8449c91f42084c1", - "b00fc048863503cada26b5a56f16d6720165f2e7ab3e0cac2c6769675b2a8a4b", - "76222e0a49d24865c3be20e5516a792fe820050da090b68ced1dc991b2bdf4f7", - "a41dd0408dfd76082a0ae17338117007b217e22d6c5428654284f3077f809336", - "ceaec657ec812681d45ba17bb58814b410d4212afa9ba97cefc67179656db96b", - "1de2ae12bdc9a7e1e269321868f7f655bd166f9c4183a47b88f6b637fa23e673", - "36e26473a4e8b5ce603a8cb3458b59c39d05fdb87fff30b18b65bad35ed9640e", - "95bad119f2292fb436837f2bfe73245627cd692a5e16e6f2e73b7e16b5ce7f6d" + "43d7a9de7c6a018c3dfb8a0de38ae060237b942c300db67147e1b56a953b122c" ) | ForEach-Object { [void]$ForbiddenIdentifierHashes.Add($_) } $CandidateLengths = [System.Collections.Generic.HashSet[int]]::new() -@(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 22) | ForEach-Object { [void]$CandidateLengths.Add($_) } +@(7, 8, 12, 22) | ForEach-Object { [void]$CandidateLengths.Add($_) } $ForbiddenTextPatterns = @( "C:\Users\", From 6a12a3f34033e4659a1f3f96882ecb200448c121 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:23:43 +0700 Subject: [PATCH 60/80] docs: neutralize external implementation naming --- docs/FAT_V2_IMPLEMENTATION_PLAN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAT_V2_IMPLEMENTATION_PLAN.md b/docs/FAT_V2_IMPLEMENTATION_PLAN.md index 2db841587..bd3a870bb 100644 --- a/docs/FAT_V2_IMPLEMENTATION_PLAN.md +++ b/docs/FAT_V2_IMPLEMENTATION_PLAN.md @@ -93,7 +93,7 @@ Required regressions: - identical duplicate source collapses safely; - conflicting IED/AP sources block; - fingerprint is order-independent; -- Siemens-like 36 ST + 22 MX fixture remains 58/58. +- generic-like 36 ST + 22 MX fixture remains 58/58. ### P4 — FAT workspace UX From 18f7b80cd8d56c4631f55c4feb3377a1099b4837 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:23:45 +0700 Subject: [PATCH 61/80] docs: neutralize external implementation naming --- docs/field-evidence-dataset-ui-prune.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/field-evidence-dataset-ui-prune.md b/docs/field-evidence-dataset-ui-prune.md index 06308fbd4..1d1a13343 100644 --- a/docs/field-evidence-dataset-ui-prune.md +++ b/docs/field-evidence-dataset-ui-prune.md @@ -10,6 +10,6 @@ Field validation on ARSAS `916a37e3d3c3364bc38a34e4ed2aaaa70a11cac6` with ARIEC6 The repeated restore proved the authority service was adding the rows successfully and that a later lifecycle stage removed them. -Source audit identified `SasOperationalUiPolicy` as that stage. Its global `DataGrid.Loaded` handler scheduled a destructive prune against the underlying `IList`, removing every `SignalDefinition` rejected by `SasOperationalSignalPolicy.IsVisible`. Object-level Siemens FCDA/FCD members intentionally fail the exact-runtime-leaf policy, so mandatory static DataSet rows were deleted directly from `device.Signals`. +Source audit identified `SasOperationalUiPolicy` as that stage. Its global `DataGrid.Loaded` handler scheduled a destructive prune against the underlying `IList`, removing every `SignalDefinition` rejected by `SasOperationalSignalPolicy.IsVisible`. Object-level generic FCDA/FCD members intentionally fail the exact-runtime-leaf policy, so mandatory static DataSet rows were deleted directly from `device.Signals`. The fix changes this contract from source mutation to presentation-only filtering and explicitly keeps rows with static `DataSetReference` visible. The authoritative signal collection is no longer changed by DataGrid policy. From 82588c8fb75a04aaa6997c6405f6430fd944fdda Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:23:48 +0700 Subject: [PATCH 62/80] docs: neutralize external implementation naming --- docs/SNTP_CLOCK_SYNC.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/SNTP_CLOCK_SYNC.md b/docs/SNTP_CLOCK_SYNC.md index 4ad3d263f..f16a35110 100644 --- a/docs/SNTP_CLOCK_SYNC.md +++ b/docs/SNTP_CLOCK_SYNC.md @@ -14,7 +14,7 @@ ARSAS includes a small clean-room SNTPv4 commissioning service for station-bus w - Sends an immediate SNTPv4 Mode 5 directed broadcast, then repeats every 64 seconds by default when a usable directed-broadcast address exists. - Sends another immediate broadcast when a newly connected IED is observed. - Separately records `broadcast sent`, `client request seen`, and `Mode 4 reply sent` evidence. -- Advertises synchronized commissioning packets with SIPROTEC compatibility `stratum 2` and reference ID `LOCL`. +- Advertises synchronized commissioning packets with protection-IED compatibility `stratum 2` and reference ID `LOCL`. - Performs a wall-clock sanity/step check. A large time step suppresses broadcast and makes that instant's unicast reply RFC-style unsynchronized (`LI=3`, `stratum=0`, `INIT`, server timestamps zero). - Never fails an IEC 61850 association when SNTP cannot start. @@ -29,11 +29,11 @@ FAT Clock Sync telemetry intentionally distinguishes packet activity from actual A broadcast without a client request may still be valid when the relay is explicitly configured for broadcast NTP, but ARSAS does not treat it as an acknowledgement. For unicast SNTP, the strongest wire-level evidence is a Mode 3 request followed by a Mode 4 reply. Device-side time-quality or clock evidence is still required before declaring the relay synchronized. -## SIPROTEC compatibility stratum +## protection-IED compatibility stratum -Field commissioning has shown that a conservative high-stratum local source can be rejected or remain marked unsynchronized on some SIPROTEC installations. ARSAS therefore uses `stratum 2` for both Mode 4 replies and Mode 5 broadcasts. +Field commissioning has shown that a conservative high-stratum local source can be rejected or remain marked unsynchronized on some GENERIC_IED installations. ARSAS therefore uses `stratum 2` for both Mode 4 replies and Mode 5 broadcasts. -The value is named in code as `SntpServerProfile.SiprotecCompatibilityStratum` and is protected by regression tests. It does not claim that the Windows laptop is physically traceable to a stratum-1 GNSS/PTP/atomic source. `LOCL` remains the reference ID and ARSAS diagnostics describe the laptop as a local commissioning source. +The value is named in code as `SntpServerProfile.GenericIedCompatibilityStratum` and is protected by regression tests. It does not claim that the Windows laptop is physically traceable to a stratum-1 GNSS/PTP/atomic source. `LOCL` remains the reference ID and ARSAS diagnostics describe the laptop as a local commissioning source. If the Windows clock fails the ARSAS clock-health guard, synchronized stratum is not advertised: the affected unicast response becomes unsynchronized (`LI=3`, `stratum=0`, `INIT`) and broadcast is suppressed. @@ -75,7 +75,7 @@ If another connected IED routes through a different local IPv4 interface, ARSAS - version/poll field copy behavior; - Mode 4 reply semantics; - originate timestamp echo; -- SIPROTEC compatibility stratum 2 on unicast and broadcast packets; +- protection-IED compatibility stratum 2 on unicast and broadcast packets; - Mode 5 broadcast semantics; - RFC-style unsynchronized response fields; - directed-broadcast calculation; From 4d0e9f344f27c930130a7724a25ac3264193774a Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:23:50 +0700 Subject: [PATCH 63/80] docs: neutralize external implementation naming --- MainWindow.Demo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MainWindow.Demo.cs b/MainWindow.Demo.cs index 283c106de..91b3c07a0 100644 --- a/MainWindow.Demo.cs +++ b/MainWindow.Demo.cs @@ -198,7 +198,7 @@ private void BuildDemoDevicesAndLivePoints() { DeviceId = $"demo-{deviceIndex + 1:00}-{spec.Name.ToLowerInvariant()}", Name = spec.Name, - IdentitySource = $"Live MMS discovery • SIPROTEC-class {spec.Description}", + IdentitySource = $"Live MMS discovery • protection-IED class {spec.Description}", LogicalDeviceSummary = spec.ModelSummary, IpAddress = spec.IpAddress, Port = 102, From 245a0a434dd5686e0ad5f45578ccedf3ddbd6df9 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:23:53 +0700 Subject: [PATCH 64/80] docs: neutralize external implementation naming --- MainWindow.IoTesting.AutoConnect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MainWindow.IoTesting.AutoConnect.cs b/MainWindow.IoTesting.AutoConnect.cs index cc05bd11d..029da5eb7 100644 --- a/MainWindow.IoTesting.AutoConnect.cs +++ b/MainWindow.IoTesting.AutoConnect.cs @@ -198,7 +198,7 @@ void ReportProgress(string message) { // The imported ARIEC SCL workspace already owns the complete static // DataSet identity. Connected reconciliation can perform a large exact-read - // pass and used to hold this SIPROTEC FAT preparation for about one minute. + // pass and used to hold this GENERIC_IED FAT preparation for about one minute. // Explicit Re-scan remains the design-versus-live comparison action. IoTestReconciliationCache.Invalidate(device); ReportProgress(reuseSharedSclAcquisition From 7a7cfc695868eb34e00d13179c5c126bc423b57d Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:06 +0700 Subject: [PATCH 65/80] docs: neutralize external implementation naming --- Models/SignalDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/SignalDefinition.cs b/Models/SignalDefinition.cs index e37722925..a7f8ca25e 100644 --- a/Models/SignalDefinition.cs +++ b/Models/SignalDefinition.cs @@ -812,7 +812,7 @@ public static bool IsCoreScadaSignal(string reference, string logicalNodeClass, if ((cls is "CSWI" or "XCBR" or "XSWI") && r.EndsWith(".pos.stval")) return true; - // Normal measurement groups use cVal. Siemens OperationalValues groups expose + // Normal measurement groups use cVal. generic OperationalValues groups expose // the directly readable instantaneous leaf as instCVal, while cVal can reject // direct MMS reads even though the parent DO is visible in an engineering tool. if (cls is "MMXU" or "MMXN") From 7c6f373d53ace06b76e6be5ffc4c300d91637187 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:10 +0700 Subject: [PATCH 66/80] docs: neutralize external implementation naming --- RAPID_FILTER_FLAT_GRID_UX_AUDIT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RAPID_FILTER_FLAT_GRID_UX_AUDIT.md b/RAPID_FILTER_FLAT_GRID_UX_AUDIT.md index 08ce7c722..e8a3b3ed3 100644 --- a/RAPID_FILTER_FLAT_GRID_UX_AUDIT.md +++ b/RAPID_FILTER_FLAT_GRID_UX_AUDIT.md @@ -2,7 +2,7 @@ ## Field findings -The latest OLSF/Siemens-style live test showed that the control behavior is now usable, but dense commissioning workflows still contain avoidable visual and navigation friction: +The latest OLSF/generic-style live test showed that the control behavior is now usable, but dense commissioning workflows still contain avoidable visual and navigation friction: - Selection Wizard column filters keep showing `Filter…` while the field has keyboard focus. - The IEC Command Panel still exposes Details/Technical details affordances that are no longer part of the fast control workflow. From 7a62bb706dd8e50cfc9210388675def5a8f96ded Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:12 +0700 Subject: [PATCH 67/80] docs: neutralize external implementation naming --- Services/IoTesting/IoTestReferenceMatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/IoTesting/IoTestReferenceMatcher.cs b/Services/IoTesting/IoTestReferenceMatcher.cs index 063190a01..80a4a4edf 100644 --- a/Services/IoTesting/IoTestReferenceMatcher.cs +++ b/Services/IoTesting/IoTestReferenceMatcher.cs @@ -5,7 +5,7 @@ namespace ArIED61850Tester.Services.IoTesting; /// /// Conservative IEC 61850 reference matcher used by FAT binding. It understands /// equivalent MMS/SCL spellings (IED-prefixed domains, the DIGSI Application display -/// wrapper, functional-constraint tokens inside MMS references, verified Siemens +/// wrapper, functional-constraint tokens inside MMS references, verified generic /// functional-group/LN display folders, and exact unique object-leaf recovery for /// incomplete imported references) but never uses fuzzy text similarity. /// From 36963ac9aa44c3af261c2fb17985b2b0a0fad542 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:23 +0700 Subject: [PATCH 68/80] test: neutralize external implementation naming --- Services/SclWorkspaceSignalMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/SclWorkspaceSignalMapper.cs b/Services/SclWorkspaceSignalMapper.cs index c82322aae..4da5013cf 100644 --- a/Services/SclWorkspaceSignalMapper.cs +++ b/Services/SclWorkspaceSignalMapper.cs @@ -42,7 +42,7 @@ public static IReadOnlyList BuildSignals(SclIedWorkspace works // First apply the normal presentation policy, then merge ARIEC's mandatory // DataSet inventory. Doing the merge after the visibility filter is deliberate: - // an unresolved object-level FCD (common in Siemens CID files) may have no leaf + // an unresolved object-level FCD (common in generic CID files) may have no leaf // DataAttribute yet, but membership in a static DataSet is sufficient authority // for the member to exist as a Signal Selection row. var visibleSignals = signals From 1db3e3413f0b8c73895a94fcec4bb7cd06456708 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:26 +0700 Subject: [PATCH 69/80] test: neutralize external implementation naming --- Services/SntpClockService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/SntpClockService.cs b/Services/SntpClockService.cs index 834146f5d..8532f6e3d 100644 --- a/Services/SntpClockService.cs +++ b/Services/SntpClockService.cs @@ -228,8 +228,8 @@ private async Task StartCoreAsync(SntpNetworkBinding binding, CancellationToken SetState( SntpClockServiceState.Serving, binding.DirectedBroadcast == null - ? $"SNTP UDP server active on {binding.LocalAddress}:123 with SIPROTEC compatibility stratum {_profile.Stratum}. No usable directed broadcast is available." - : $"SNTP UDP server active on {binding.LocalAddress}:123 with SIPROTEC compatibility stratum {_profile.Stratum}; Mode 5 broadcast targets {binding.DirectedBroadcast}:123."); + ? $"SNTP UDP server active on {binding.LocalAddress}:123 with protection-IED compatibility stratum {_profile.Stratum}. No usable directed broadcast is available." + : $"SNTP UDP server active on {binding.LocalAddress}:123 with protection-IED compatibility stratum {_profile.Stratum}; Mode 5 broadcast targets {binding.DirectedBroadcast}:123."); _receiveTask = ReceiveLoopAsync(udp, serviceCancellation.Token); _broadcastTask = BroadcastLoopAsync(binding, serviceCancellation.Token); From b6cbf0da896baef63ac277cfddeaca28319ccf7b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:29 +0700 Subject: [PATCH 70/80] test: neutralize external implementation naming --- Services/SntpPacket.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Services/SntpPacket.cs b/Services/SntpPacket.cs index 0aae4c6fb..a6b6ada5c 100644 --- a/Services/SntpPacket.cs +++ b/Services/SntpPacket.cs @@ -170,15 +170,15 @@ public readonly record struct SntpClientRequest( public sealed record SntpServerProfile { /// - /// SIPROTEC compatibility advertisement used by ARSAS commissioning Clock Sync. - /// Field experience with SIPROTEC requires a trusted-looking low stratum; stratum 2 + /// protection-IED compatibility advertisement used by ARSAS commissioning Clock Sync. + /// Field experience with GENERIC_IED requires a trusted-looking low stratum; stratum 2 /// is deliberately used instead of stratum 1 so ARSAS does not claim to be a primary /// GPS/PTP/atomic reference. ReferenceId remains LOCL and diagnostics state that the /// laptop clock is a local commissioning source, not a traceable grandmaster. /// - public const byte SiprotecCompatibilityStratum = 2; + public const byte GenericIedCompatibilityStratum = 2; - public byte Stratum { get; init; } = SiprotecCompatibilityStratum; + public byte Stratum { get; init; } = GenericIedCompatibilityStratum; public byte LeapIndicator { get; init; } public sbyte PollExponent { get; init; } = 6; public sbyte PrecisionExponent { get; init; } = -10; From 77d24359bbbcffa303a0dbca3c43d5e93eb667eb Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:32 +0700 Subject: [PATCH 71/80] test: neutralize external implementation naming --- tests/ARSAS.Tests/DataSetCompletenessDiagnosticTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ARSAS.Tests/DataSetCompletenessDiagnosticTests.cs b/tests/ARSAS.Tests/DataSetCompletenessDiagnosticTests.cs index 99df31a4f..a8225609e 100644 --- a/tests/ARSAS.Tests/DataSetCompletenessDiagnosticTests.cs +++ b/tests/ARSAS.Tests/DataSetCompletenessDiagnosticTests.cs @@ -8,11 +8,11 @@ namespace ARSAS.Tests; public sealed class DataSetCompletenessDiagnosticTests { [Fact] - public void SiemensLike_CrossLd_Fcda_Is_Canonical_Visible_And_Diagnostically_Complete() + public void CrossDevice_CrossLd_Fcda_Is_Canonical_Visible_And_Diagnostically_Complete() { var model = SclLiveModelProjectionBuilder.Build( XDocument.Parse(CrossLogicalDeviceFixture()), - "Siprotec_cross_ld.cid"); + "GenericIed_cross_ld.cid"); var signals = new List(); var merge = Iec61850DataSetSignalInventoryService.EnsureMandatorySignals(signals, model); @@ -43,7 +43,7 @@ public void Missing_Static_DataSet_Member_Is_Reported_With_DataSet_Index_And_Ref { var model = SclLiveModelProjectionBuilder.Build( XDocument.Parse(CrossLogicalDeviceFixture()), - "Siprotec_cross_ld.cid"); + "GenericIed_cross_ld.cid"); var snapshot = Iec61850DataSetCompletenessDiagnostic.Evaluate( model, @@ -60,7 +60,7 @@ public void Missing_Static_DataSet_Member_Is_Reported_With_DataSet_Index_And_Ref private static string CrossLogicalDeviceFixture() => """ -
+
From 65444eee36e38ee40a711fd2867ed70e3f439e21 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:35 +0700 Subject: [PATCH 72/80] test: neutralize external implementation naming --- tests/ARSAS.Tests/DynamicReportQualificationIdentityTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/DynamicReportQualificationIdentityTests.cs b/tests/ARSAS.Tests/DynamicReportQualificationIdentityTests.cs index 699d791e4..15ae3faf7 100644 --- a/tests/ARSAS.Tests/DynamicReportQualificationIdentityTests.cs +++ b/tests/ARSAS.Tests/DynamicReportQualificationIdentityTests.cs @@ -74,7 +74,7 @@ private static Iec61850MonitorDevice Device() Name = "Q0", IpAddress = "192.168.81.17", Port = 102, - SclIedName = "SIPROTEC-Q0", + SclIedName = "GENERIC_IED-Q0", SclSourceSha256 = "aaaaaaaa" }; From dbcb6c4c1761fa17a099ccd61df04df4aa642fe1 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:38 +0700 Subject: [PATCH 73/80] test: neutralize external implementation naming --- .../ARSAS.Tests/DynamicReportQualificationProfileStoreTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/DynamicReportQualificationProfileStoreTests.cs b/tests/ARSAS.Tests/DynamicReportQualificationProfileStoreTests.cs index 017ab4daa..6ba2fb8ea 100644 --- a/tests/ARSAS.Tests/DynamicReportQualificationProfileStoreTests.cs +++ b/tests/ARSAS.Tests/DynamicReportQualificationProfileStoreTests.cs @@ -154,7 +154,7 @@ private static ArMms.MmsDynamicReportIedIdentity Identity() { StableIdentityKey = "ied:station-a:q0", ModelFingerprint = "sha256:model-001", - Model = "SIPROTEC-Q0", + Model = "GENERIC_IED-Q0", FirmwareRevision = "1.2.3", ProfileRevision = "cfg-42" }; From 6ba9f1a70e9d5ba5ac0d9620dce4a17527c152c3 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:41 +0700 Subject: [PATCH 74/80] test: neutralize external implementation naming --- tests/ARSAS.Tests/IoFatImmediateEvidenceRegressionTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ARSAS.Tests/IoFatImmediateEvidenceRegressionTests.cs b/tests/ARSAS.Tests/IoFatImmediateEvidenceRegressionTests.cs index 0822588bb..d30e1cde7 100644 --- a/tests/ARSAS.Tests/IoFatImmediateEvidenceRegressionTests.cs +++ b/tests/ARSAS.Tests/IoFatImmediateEvidenceRegressionTests.cs @@ -39,10 +39,10 @@ private static IoTestPointPlan CreatePoint() => new() { TestPointId = "TRUE-EVIDENCE-REGRESSION", - IedName = "SIPROTEC", + IedName = "GENERIC_IED", IpAddress = "192.168.1.10", SignalName = "Protection pickup", - ObjectReference = "SIPROTEC/PROT.Op.general", + ObjectReference = "GENERIC_IED/PROT.Op.general", FunctionalConstraint = "ST", ExpectedOnText = "true", ExpectedOffText = "false", From a5f35632919c95e0c30c9c2d9f36b0352ce097f4 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:44 +0700 Subject: [PATCH 75/80] test: neutralize external implementation naming --- tests/ARSAS.Tests/IoFatSclProjectImportTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ARSAS.Tests/IoFatSclProjectImportTests.cs b/tests/ARSAS.Tests/IoFatSclProjectImportTests.cs index caacf3059..deb9223bb 100644 --- a/tests/ARSAS.Tests/IoFatSclProjectImportTests.cs +++ b/tests/ARSAS.Tests/IoFatSclProjectImportTests.cs @@ -7,13 +7,13 @@ namespace ARSAS.Tests; public sealed class IoFatSclProjectImportTests { [Fact] - public async Task ProductionLoader_Siemens58_NoCommunication_ProjectsEveryStaticMember() + public async Task ProductionLoader_CrossDevice58_NoCommunication_ProjectsEveryStaticMember() { var root = TempDirectory(); - var path = Path.Combine(root, "Siprotec_58_member.cid"); - BuildSiemens58Fixture().Save(path); + var path = Path.Combine(root, "GenericIed_58_member.cid"); + BuildCrossDevice58Fixture().Save(path); - var imported = await new IoFatSclProjectImportService().ImportAsync(new[] { path }, "Siemens FAT"); + var imported = await new IoFatSclProjectImportService().ImportAsync(new[] { path }, "generic FAT"); Assert.Single(imported.Sources); Assert.Equal(IoFatSourceKinds.Scl, imported.Sources[0].Kind); @@ -29,7 +29,7 @@ public async Task ProductionLoader_Siemens58_NoCommunication_ProjectsEveryStatic Assert.True(point.ImportReady); Assert.Equal("SCL_DATASET_AUTHORITY", point.BindingStatus); Assert.Equal(imported.Sources[0].SourceId, point.SignalAddress); - Assert.Equal("Siprotec_58_member.cid", point.SourceSheet); + Assert.Equal("GenericIed_58_member.cid", point.SourceSheet); }); Assert.Equal(36, imported.VerificationProject.Signals.Count(signal => signal.SignalKind == FatSignalKind.Discrete)); @@ -183,7 +183,7 @@ public async Task ProductionLoader_NoStaticDataSet_ReturnsZeroRowsAndExplicitFin Assert.Contains(imported.Findings, finding => finding.Code == "SCL_NO_STATIC_DATASET_MEMBERS"); } - private static XDocument BuildSiemens58Fixture() + private static XDocument BuildCrossDevice58Fixture() { XNamespace ns = "http://www.iec.ch/61850/2003/SCL"; var digitalDataSet = new XElement(ns + "DataSet", new XAttribute("name", "Digital")); @@ -206,7 +206,7 @@ private static XDocument BuildSiemens58Fixture() return new XDocument(new XElement(ns + "SCL", new XAttribute("version", "2007"), new XAttribute("revision", "B"), - new XElement(ns + "Header", new XAttribute("id", "SIEMENS_58_P4")), + new XElement(ns + "Header", new XAttribute("id", "GENERIC_58_P4")), new XElement(ns + "IED", new XAttribute("name", "AA1C1F13R4"), new XElement(ns + "AccessPoint", new XAttribute("name", "E"), new XElement(ns + "Server", From c5e9f57d5f4160338516eea031419e4391ca5f12 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:47 +0700 Subject: [PATCH 76/80] test: neutralize external implementation naming --- .../SasOperationalUiDataSetAuthorityRegressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/SasOperationalUiDataSetAuthorityRegressionTests.cs b/tests/ARSAS.Tests/SasOperationalUiDataSetAuthorityRegressionTests.cs index 7a2adebf0..27268dbf0 100644 --- a/tests/ARSAS.Tests/SasOperationalUiDataSetAuthorityRegressionTests.cs +++ b/tests/ARSAS.Tests/SasOperationalUiDataSetAuthorityRegressionTests.cs @@ -21,7 +21,7 @@ public void ObjectLevelStaticDataSetMember_RemainsPresentationVisible() }; // The normal operator policy intentionally requires an exact runtime value leaf, - // so an object-level Siemens FCDA is not independently promoted by that policy. + // so an object-level generic FCDA is not independently promoted by that policy. Assert.False(SasOperationalSignalPolicy.IsVisible(signal)); // Static DataSet membership is a stronger protocol-authority contract and must From 7d622debb28d7bb624916f146081b663c5a900c8 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:24:50 +0700 Subject: [PATCH 77/80] test: neutralize external implementation naming --- .../ARSAS.Tests/StaticDataSetUiLifecycleRegressionTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ARSAS.Tests/StaticDataSetUiLifecycleRegressionTests.cs b/tests/ARSAS.Tests/StaticDataSetUiLifecycleRegressionTests.cs index b701a3d54..dcb7c3825 100644 --- a/tests/ARSAS.Tests/StaticDataSetUiLifecycleRegressionTests.cs +++ b/tests/ARSAS.Tests/StaticDataSetUiLifecycleRegressionTests.cs @@ -10,9 +10,9 @@ namespace ARSAS.Tests; public sealed class StaticDataSetUiLifecycleRegressionTests { [Fact] - public void SiemensLikeStaticMembers_SurviveOperatorPresentationDecision() + public void CrossDeviceStaticMembers_SurviveOperatorPresentationDecision() { - var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "Siprotec_ui_lifecycle.cid"); + var model = SclLiveModelProjectionBuilder.Build(BuildFixture(), "GenericIed_ui_lifecycle.cid"); var workspace = new SclIedWorkspace { IedName = "AA1C1F13R4", @@ -45,7 +45,7 @@ private static XDocument BuildFixture() new XElement(ns + "SCL", new XAttribute("version", "2007"), new XAttribute("revision", "B"), - new XElement(ns + "Header", new XAttribute("id", "SIEMENS_UI_LIFECYCLE")), + new XElement(ns + "Header", new XAttribute("id", "GENERIC_UI_LIFECYCLE")), new XElement(ns + "IED", new XAttribute("name", "AA1C1F13R4"), new XElement(ns + "AccessPoint", From 0fc6afede951841fa3752c0b2cbc97eeb334261f Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:33:41 +0700 Subject: [PATCH 78/80] test: align neutral SNTP compatibility naming --- tests/ARSAS.Tests/SntpPacketTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ARSAS.Tests/SntpPacketTests.cs b/tests/ARSAS.Tests/SntpPacketTests.cs index e9d8e2ca4..59bebced8 100644 --- a/tests/ARSAS.Tests/SntpPacketTests.cs +++ b/tests/ARSAS.Tests/SntpPacketTests.cs @@ -24,7 +24,7 @@ public void ClientRequest_IsRecognized_AndReplyCopiesVersionPollAndOriginateTime Assert.Equal(4, reply[0] & 0x07); Assert.Equal(4, (reply[0] >> 3) & 0x07); - Assert.Equal(SntpServerProfile.SiprotecCompatibilityStratum, reply[1]); + Assert.Equal(SntpServerProfile.GenericIedCompatibilityStratum, reply[1]); Assert.Equal(9, unchecked((sbyte)reply[2])); Assert.Equal(request.AsSpan(40, 8).ToArray(), reply.AsSpan(24, 8).ToArray()); Assert.InRange((SntpPacket.ReadTimestamp(reply.AsSpan(32, 8), receive) - receive).Duration(), TimeSpan.Zero, TimeSpan.FromTicks(2)); @@ -32,7 +32,7 @@ public void ClientRequest_IsRecognized_AndReplyCopiesVersionPollAndOriginateTime } [Fact] - public void Broadcast_IsMode5_AndUsesSiprotecCompatibilityStratum() + public void Broadcast_IsMode5_AndUsesGenericIedCompatibilityStratum() { var now = new DateTimeOffset(2026, 8, 13, 2, 3, 4, TimeSpan.Zero); var packet = SntpPacket.BuildBroadcast(now, new SntpServerProfile()); @@ -40,8 +40,8 @@ public void Broadcast_IsMode5_AndUsesSiprotecCompatibilityStratum() Assert.Equal(SntpPacket.MinimumLength, packet.Length); Assert.Equal(5, packet[0] & 0x07); Assert.Equal(4, (packet[0] >> 3) & 0x07); - Assert.Equal((byte)2, SntpServerProfile.SiprotecCompatibilityStratum); - Assert.Equal(SntpServerProfile.SiprotecCompatibilityStratum, packet[1]); + Assert.Equal((byte)2, SntpServerProfile.GenericIedCompatibilityStratum); + Assert.Equal(SntpServerProfile.GenericIedCompatibilityStratum, packet[1]); Assert.Equal(6, unchecked((sbyte)packet[2])); Assert.Equal("LOCL", System.Text.Encoding.ASCII.GetString(packet, 12, 4)); Assert.InRange((SntpPacket.ReadTimestamp(packet.AsSpan(40, 8), now) - now).Duration(), TimeSpan.Zero, TimeSpan.FromTicks(2)); From aaa958616796ba8d0e2070a6a4a1531df97a5296 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:33:47 +0700 Subject: [PATCH 79/80] test: align neutral SNTP compatibility naming --- tests/ARSAS.Tests/SntpEthernetFrameCodecTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ARSAS.Tests/SntpEthernetFrameCodecTests.cs b/tests/ARSAS.Tests/SntpEthernetFrameCodecTests.cs index 497026435..3bc736b38 100644 --- a/tests/ARSAS.Tests/SntpEthernetFrameCodecTests.cs +++ b/tests/ARSAS.Tests/SntpEthernetFrameCodecTests.cs @@ -46,7 +46,7 @@ public void Mode3RawFrame_IsParsed_AndMode4ReplySwapsEndpointsWithValidChecksums var payload = reply.AsSpan(udpOffset + 8, SntpPacket.MinimumLength); Assert.Equal(4, payload[0] & 0x07); - Assert.Equal(SntpServerProfile.SiprotecCompatibilityStratum, payload[1]); + Assert.Equal(SntpServerProfile.GenericIedCompatibilityStratum, payload[1]); Assert.Equal(requestPayload.AsSpan(40, 8).ToArray(), payload.Slice(24, 8).ToArray()); } From cb80662a1c7e6065e25c9d00ac46e93f26ffc9ab Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 19 Sep 2026 16:51:40 +0700 Subject: [PATCH 80/80] fix(release): lock qualified PR324 source lineage --- .github/workflows/release-windows.yml | 22 ++- .release/qualified-source.json | 22 +++ .release/windows.json | 1 + docs/WORKSTREAM_COORDINATION.md | 19 ++ scripts/verify-qualified-release-source.py | 182 ++++++++++++++++++ .../DataSetFcdSignalInventoryTests.cs | 2 +- .../IoTestSignalSelectionServiceTests.cs | 2 +- .../QualifiedReleaseSourceRegressionTests.cs | 56 ++++++ .../SignalCatalogCompletenessContractTests.cs | 4 +- 9 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 .release/qualified-source.json create mode 100644 scripts/verify-qualified-release-source.py create mode 100644 tests/ARSAS.Tests/QualifiedReleaseSourceRegressionTests.cs diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index 3a2e1c271..e9f9d89ad 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -46,6 +46,12 @@ jobs: id: release shell: powershell run: | + if ($env:GITHUB_EVENT_NAME -eq "workflow_dispatch" -and + "${{ inputs.publish_release }}" -eq "true" -and + $env:GITHUB_REF -ne "refs/heads/main") { + throw "Manual publication is allowed only from the main branch." + } + if ($env:GITHUB_EVENT_NAME -eq "workflow_dispatch") { $rawVersion = "${{ inputs.version }}" } @@ -160,6 +166,10 @@ jobs: with: python-version: "3.12" + - name: Verify qualified release source authority + shell: powershell + run: python .\ArIED61850Tester\scripts\verify-qualified-release-source.py + - name: Verify source and licensing boundaries shell: powershell run: .\ArIED61850Tester\scripts\verify-source-clean.ps1 @@ -426,7 +436,11 @@ jobs: ArIED61850Tester/dist/ARSAS-Windows-x64-PROVENANCE.json - name: Create or update GitHub Release - if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' || inputs.publish_release == true + if: >- + github.ref_type == 'tag' || + (github.event_name == 'workflow_dispatch' && + inputs.publish_release == true && + github.ref == 'refs/heads/main') shell: powershell env: GH_TOKEN: ${{ github.token }} @@ -467,7 +481,11 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "Failed to create GitHub Release." } - name: Record verified release publication - if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' || inputs.publish_release == true + if: >- + github.ref_type == 'tag' || + (github.event_name == 'workflow_dispatch' && + inputs.publish_release == true && + github.ref == 'refs/heads/main') shell: powershell env: GH_TOKEN: ${{ github.token }} diff --git a/.release/qualified-source.json b/.release/qualified-source.json new file mode 100644 index 000000000..109bca827 --- /dev/null +++ b/.release/qualified-source.json @@ -0,0 +1,22 @@ +{ + "schemaVersion": 1, + "authority": "qualified-runtime-lineage", + "qualificationManifest": ".release/qualified-source.json", + "fieldQualifiedSource": { + "sourcePullRequest": 324, + "sourceHeadCommit": "b0f25569a398c7bbdc3a3a34409dbd74ebf49444", + "sourceMergeCommit": "2b8e8adfd019bd087dc338dcc32bffefec53370f", + "sourceTree": "cccb50607161f8637e597b43d71038ffd4becfdc", + "workflowRunId": 35412542175, + "artifactId": 10575075419, + "portableFileName": "ARSAS-1.6.37-win-x64-portable.exe", + "portableSha256": "6805b878f91f3d526cc50f3e81e3eb9a1f72500217dacc8921e50bdc349c5fcc", + "portableSizeBytes": 78383289 + }, + "currentReleaseCandidate": { + "derivation": "Field-qualified runtime with implementation-neutral identifiers and release-governance changes only.", + "fingerprintAlgorithm": "sha256-path-null-content-null-v1", + "releaseSensitiveFileCount": 506, + "releaseSensitiveFingerprint": "b86663f358499f656bcddd52e38dbe08df31b1192fae4136889677572dabada5" + } +} diff --git a/.release/windows.json b/.release/windows.json index 4e80cd3c5..4988d0a91 100644 --- a/.release/windows.json +++ b/.release/windows.json @@ -2,6 +2,7 @@ "version": "1.6.38", "channel": "stable", "platform": "windows-x64", + "qualificationManifest": ".release/qualified-source.json", "primaryAsset": "ARSAS-Windows-x64-Setup.exe", "publicationRequest": 27 } diff --git a/docs/WORKSTREAM_COORDINATION.md b/docs/WORKSTREAM_COORDINATION.md index f2805da58..553abfd83 100644 --- a/docs/WORKSTREAM_COORDINATION.md +++ b/docs/WORKSTREAM_COORDINATION.md @@ -12,6 +12,25 @@ The ArdIrec native bridge dependency used by that workstation was integrated fir The production `AGENTS.md` already present on `main` remains authoritative. +## IEC 61850 qualified runtime baseline + +The accepted IEC 61850 runtime line was qualified on ARSAS PR #324. Its final +source head is `b0f25569a398c7bbdc3a3a34409dbd74ebf49444`, its merged commit is +`2b8e8adfd019bd087dc338dcc32bffefec53370f`, and both commits resolve to the +same Git tree `cccb50607161f8637e597b43d71038ffd4becfdc`. + +The corresponding portable artifact was produced by workflow run +`35412542175` with SHA-256 +`6805b878f91f3d526cc50f3e81e3eb9a1f72500217dacc8921e50bdc349c5fcc`. +`.release/qualified-source.json` is the machine-readable authority for this +lineage. The Windows release workflow must verify its release-sensitive +fingerprint before packaging or publication. + +Public wording and identifier neutralization may be layered on this baseline +only when the exact combined head passes regression and packaging gates. A +version bump, documentation update, or merged/closed PR status does not by +itself establish a new qualified runtime authority. + ## Parallel FAT workstream The FAT workstream remains independent and is currently represented by the stacked FAT branches/PRs, including PR #290, PR #296, and PR #303. Their existing field gate remains authoritative; this coordination note does not waive or replace it. diff --git a/scripts/verify-qualified-release-source.py b/scripts/verify-qualified-release-source.py new file mode 100644 index 000000000..1b6a0ad42 --- /dev/null +++ b/scripts/verify-qualified-release-source.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +"""Fail closed when release-sensitive ARSAS inputs drift from qualification.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +import sys +from pathlib import Path + + +MANIFEST_PATH = Path(".release/qualified-source.json") +WINDOWS_MANIFEST_PATH = Path(".release/windows.json") +NORMALIZED_VERSION_PATHS = { + "ArIED61850Tester.csproj", + "Directory.Build.props", + "VERSION", +} +EXCLUDED_PREFIXES = ( + ".github/", + ".release/", + "docs/", + "evidence/", + "landing/", + "tests/", +) +INCLUDED_GOVERNANCE_PATHS = { + ".github/workflows/release-windows.yml", +} +EXCLUDED_SUFFIXES = ( + ".md", +) +VERSION_ELEMENT_PATTERN = re.compile( + rb"(<(?:Version|AssemblyVersion|FileVersion|InformationalVersion)>)[^<]*()" +) + + +def repository_root() -> Path: + return Path(__file__).resolve().parents[1] + + +def tracked_paths(root: Path) -> list[str]: + result = subprocess.run( + ["git", "-C", str(root), "ls-files", "-z"], + check=True, + stdout=subprocess.PIPE, + ) + return sorted( + item.decode("utf-8") + for item in result.stdout.split(b"\0") + if item + ) + + +def is_release_sensitive(path: str) -> bool: + if path in INCLUDED_GOVERNANCE_PATHS: + return True + if path.startswith(EXCLUDED_PREFIXES): + return False + if path.endswith(EXCLUDED_SUFFIXES): + return False + return True + + +def normalized_content(path: str, content: bytes) -> bytes: + if path == "VERSION": + return b"\n" + if path in NORMALIZED_VERSION_PATHS: + return VERSION_ELEMENT_PATTERN.sub( + rb"\1\2", + content, + ) + return content + + +def calculate_fingerprint(root: Path) -> tuple[str, list[str]]: + digest = hashlib.sha256() + selected: list[str] = [] + for path in tracked_paths(root): + if not is_release_sensitive(path): + continue + full_path = root / path + if not full_path.is_file(): + raise RuntimeError(f"Tracked release-sensitive path is missing: {path}") + selected.append(path) + digest.update(path.encode("utf-8")) + digest.update(b"\0") + digest.update(normalized_content(path, full_path.read_bytes())) + digest.update(b"\0") + return digest.hexdigest(), selected + + +def load_json(path: Path) -> dict: + try: + return json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + raise RuntimeError(f"Unable to read valid JSON from {path}: {exc}") from exc + + +def validate_manifest(root: Path, fingerprint: str, selected: list[str]) -> list[str]: + manifest = load_json(root / MANIFEST_PATH) + windows_manifest = load_json(root / WINDOWS_MANIFEST_PATH) + errors: list[str] = [] + + if manifest.get("schemaVersion") != 1: + errors.append("Qualification manifest schemaVersion must be 1.") + if windows_manifest.get("qualificationManifest") != MANIFEST_PATH.as_posix(): + errors.append("Windows release manifest is not bound to the qualification manifest.") + + source = manifest.get("fieldQualifiedSource") or {} + required_source_values = { + "sourcePullRequest": 324, + "sourceHeadCommit": "b0f25569a398c7bbdc3a3a34409dbd74ebf49444", + "sourceMergeCommit": "2b8e8adfd019bd087dc338dcc32bffefec53370f", + "sourceTree": "cccb50607161f8637e597b43d71038ffd4becfdc", + "workflowRunId": 35412542175, + "artifactId": 10575075419, + "portableSha256": "6805b878f91f3d526cc50f3e81e3eb9a1f72500217dacc8921e50bdc349c5fcc", + "portableSizeBytes": 78383289, + } + for key, expected in required_source_values.items(): + if source.get(key) != expected: + errors.append(f"Qualified source {key} does not match the approved authority.") + + candidate = manifest.get("currentReleaseCandidate") or {} + if candidate.get("fingerprintAlgorithm") != "sha256-path-null-content-null-v1": + errors.append("Unsupported release-sensitive fingerprint algorithm.") + if candidate.get("releaseSensitiveFileCount") != len(selected): + errors.append( + "Release-sensitive file count drifted: " + f"expected {candidate.get('releaseSensitiveFileCount')}, actual {len(selected)}." + ) + if candidate.get("releaseSensitiveFingerprint") != fingerprint: + errors.append( + "Release-sensitive source drifted from the qualified candidate: " + f"expected {candidate.get('releaseSensitiveFingerprint')}, actual {fingerprint}." + ) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "--print", + action="store_true", + dest="print_only", + help="Print the calculated fingerprint and file count without verifying.", + ) + args = parser.parse_args() + + root = repository_root() + fingerprint, selected = calculate_fingerprint(root) + if args.print_only: + print( + json.dumps( + { + "releaseSensitiveFileCount": len(selected), + "releaseSensitiveFingerprint": fingerprint, + }, + indent=2, + ) + ) + return 0 + + errors = validate_manifest(root, fingerprint, selected) + if errors: + for error in errors: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + + print( + "Qualified release source PASS: " + f"{len(selected)} files, fingerprint {fingerprint}." + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/ARSAS.Tests/DataSetFcdSignalInventoryTests.cs b/tests/ARSAS.Tests/DataSetFcdSignalInventoryTests.cs index bf153af73..ec28b31f5 100644 --- a/tests/ARSAS.Tests/DataSetFcdSignalInventoryTests.cs +++ b/tests/ARSAS.Tests/DataSetFcdSignalInventoryTests.cs @@ -8,7 +8,7 @@ namespace ARSAS.Tests; public sealed class DataSetFcdSignalInventoryTests { [Fact] - public void FcdOnly_SiemensLike_DataSets_Appear_58Of58_In_Signal_Selector_Inventory() + public void FcdOnly_CrossDevice_DataSets_Appear_58Of58_In_Signal_Selector_Inventory() { var digitalMembers = Enumerable.Range(1, 36) .Select(index => Member(index - 1, $"IEDLD0/GGIO1.Dig{index:00}", "ST")) diff --git a/tests/ARSAS.Tests/IoTestSignalSelectionServiceTests.cs b/tests/ARSAS.Tests/IoTestSignalSelectionServiceTests.cs index 576ba32e5..dbee3a07a 100644 --- a/tests/ARSAS.Tests/IoTestSignalSelectionServiceTests.cs +++ b/tests/ARSAS.Tests/IoTestSignalSelectionServiceTests.cs @@ -98,7 +98,7 @@ public void Aa1C1F13R4_FunctionGroupFoldersMatchConcatenatedMmsLnPrefixes() } [Fact] - public void SiemensOperationalValuesFolderMatchesConcatenatedMmsLnPrefix() + public void GenericOperationalValuesFolderMatchesConcatenatedMmsLnPrefix() { var ied = new IoTestIedPlan { diff --git a/tests/ARSAS.Tests/QualifiedReleaseSourceRegressionTests.cs b/tests/ARSAS.Tests/QualifiedReleaseSourceRegressionTests.cs new file mode 100644 index 000000000..84a43fdb1 --- /dev/null +++ b/tests/ARSAS.Tests/QualifiedReleaseSourceRegressionTests.cs @@ -0,0 +1,56 @@ +using System.Text.Json; + +namespace ARSAS.Tests; + +public sealed class QualifiedReleaseSourceRegressionTests +{ + [Fact] + public void QualificationManifest_LocksExactFieldTestedLineageAndArtifact() + { + using var document = JsonDocument.Parse( + File.ReadAllText(FindRepoFile(".release/qualified-source.json"))); + var root = document.RootElement; + var source = root.GetProperty("fieldQualifiedSource"); + var candidate = root.GetProperty("currentReleaseCandidate"); + + Assert.Equal(1, root.GetProperty("schemaVersion").GetInt32()); + Assert.Equal(324, source.GetProperty("sourcePullRequest").GetInt32()); + Assert.Equal("b0f25569a398c7bbdc3a3a34409dbd74ebf49444", source.GetProperty("sourceHeadCommit").GetString()); + Assert.Equal("2b8e8adfd019bd087dc338dcc32bffefec53370f", source.GetProperty("sourceMergeCommit").GetString()); + Assert.Equal("cccb50607161f8637e597b43d71038ffd4becfdc", source.GetProperty("sourceTree").GetString()); + Assert.Equal(35412542175, source.GetProperty("workflowRunId").GetInt64()); + Assert.Equal(10575075419, source.GetProperty("artifactId").GetInt64()); + Assert.Equal("6805b878f91f3d526cc50f3e81e3eb9a1f72500217dacc8921e50bdc349c5fcc", source.GetProperty("portableSha256").GetString()); + Assert.Equal(78383289, source.GetProperty("portableSizeBytes").GetInt64()); + Assert.Matches("^[0-9a-f]{64}$", candidate.GetProperty("releaseSensitiveFingerprint").GetString()!); + Assert.True(candidate.GetProperty("releaseSensitiveFileCount").GetInt32() > 100); + } + + [Fact] + public void ReleaseWorkflow_VerifiesQualificationAndNeverPublishesFromAnOrdinaryMainPush() + { + var workflow = File.ReadAllText(FindRepoFile(".github/workflows/release-windows.yml")); + var windowsManifest = File.ReadAllText(FindRepoFile(".release/windows.json")); + + Assert.Contains("verify-qualified-release-source.py", workflow, StringComparison.Ordinal); + Assert.Contains("github.event_name == 'workflow_dispatch'", workflow, StringComparison.Ordinal); + Assert.Contains("inputs.publish_release == true", workflow, StringComparison.Ordinal); + Assert.Contains("github.ref == 'refs/heads/main'", workflow, StringComparison.Ordinal); + Assert.DoesNotContain("github.ref_type == 'tag' || github.ref == 'refs/heads/main'", workflow, StringComparison.Ordinal); + Assert.Contains("\"qualificationManifest\": \".release/qualified-source.json\"", windowsManifest, StringComparison.Ordinal); + } + + private static string FindRepoFile(string relativePath) + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory != null) + { + var candidate = Path.Combine(directory.FullName, relativePath); + if (File.Exists(candidate)) + return candidate; + directory = directory.Parent; + } + + throw new FileNotFoundException($"Repository file was not found: {relativePath}"); + } +} diff --git a/tests/ARSAS.Tests/SignalCatalogCompletenessContractTests.cs b/tests/ARSAS.Tests/SignalCatalogCompletenessContractTests.cs index 6f62f28c8..fb735eb9d 100644 --- a/tests/ARSAS.Tests/SignalCatalogCompletenessContractTests.cs +++ b/tests/ARSAS.Tests/SignalCatalogCompletenessContractTests.cs @@ -46,7 +46,7 @@ public void OpenScl_Static_DataSet_Membership_Is_NonDroppable_Even_For_Unknown_V } [Fact] - public void OpenScl_SiemensLike_Fcd_Inventory_Is_Conserved_58_Of_58() + public void OpenScl_CrossDevice_Fcd_Inventory_Is_Conserved_58_Of_58() { var digital = Enumerable.Range(1, 36) .Select(index => Member(index - 1, $"IEDLD0/GGIO1.Dig{index:00}", "ST")) @@ -71,7 +71,7 @@ public void OpenScl_SiemensLike_Fcd_Inventory_Is_Conserved_58_Of_58() } [Fact] - public void IpDiscovery_SiemensLike_Fcd_Inventory_Is_Conserved_58_Of_58() + public void IpDiscovery_CrossDevice_Fcd_Inventory_Is_Conserved_58_Of_58() { var digital = Enumerable.Range(1, 36) .Select(index => Member(index - 1, $"IEDLD0/GGIO1.Dig{index:00}", "ST"))