Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/device-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,29 @@ body:
placeholder: 1.6.18
validations:
required: true
- type: input
id: evidence-date
attributes:
label: Evidence or test date (YYYY-MM-DD)
description: Use the actual test date, not the date this issue was filed.
placeholder: 2026-09-22
validations:
required: true
- type: dropdown
id: evidence-kind
attributes:
label: Evidence type
description: A pull request or implementation history alone is not proof of a new physical-relay test.
options: [Sanitized field capture or test log, Sanitized ARSAS diagnostic and reproducible steps, Engineering or implementation history only, Planned test - no result yet]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the unsatisfiable planned-test intake option

When a reporter selects Planned test - no result yet, no actual test date or sanitized diagnostic exists, but both fields remain required and the date instructions explicitly demand an actual test date. The advertised path therefore forces users either to enter fabricated evidence or abandon the form; planned tests need a separate form/optional fields, or this option should be removed from the evidence intake.

AGENTS.md reference: AGENTS.md:L58-L65

Useful? React with 👍 / 👎.

validations:
required: true
- type: input
id: prior-profile
attributes:
label: Existing anonymized profile ID (if this is a retest)
placeholder: field-profile-a-file-service
validations:
required: false
- type: input
id: windows-version
attributes:
Expand Down
29 changes: 29 additions & 0 deletions landing/device-evidence.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,34 @@
"missingRetest": "No public retest on the current stable release is documented.",
"recordScope": "Linked pull requests form a public engineering and implementation trail, not raw relay capture or independent field retest.",
"registryUpdate": "A registry edit or website deployment does not change the evidence date or establish a retest."
},
"coveragePlan": {
"scope": "published-anonymized-profiles-only",
"serviceOrder": [
"mmsAssociation",
"liveModel",
"reporting",
"fileDirectory",
"fileTransfer",
"goose",
"control",
"selectedRcbExport"
],
"noPublishedTestAcrossProfiles": [
"goose",
"control"
],
"nextEvidence": {
"goose": "Sanitized capture showing publisher identity, GoID/AppID, stNum/sqNum, TAL, payload order and expected DataSet mapping; include negative or missing-frame findings.",
"control": "Approved non-energized test showing ctlModel, Direct/SBO sequence, service and CommandTermination results, rejection/AddCause and independently checked process feedback."
},
"stableRetest": {
"state": "not-publicly-documented",
"requirements": [
"Exact stable tag and ARSAS version",
"Test date and anonymized IED model/firmware or disclosure boundary",
"Positive and negative service result with sanitized diagnostics and acquisition conditions"
]
}
}
}
9 changes: 9 additions & 0 deletions landing/templates/bukti-kompatibilitas.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ <h2>Lihat service IEC 61850 mana yang memiliki evidence publik—dan mana yang b
</table>
</div>
<p class="matrix-boundary"><strong>Cara membacanya:</strong> satu service yang berhasil tidak berarti service lain otomatis bekerja, dan tidak ada row yang menjadi sertifikasi conformance IEC 61850. Buka profile di bawah untuk melihat kondisi exact dan engineering record publik di balik statusnya.</p>
<div class="evidence-gap-panel" data-evidence-coverage="published-anonymized-profiles-only">
<div class="section-head"><span class="kicker">Gap evidence · capture berikutnya</span><h3>Apa yang belum dibuktikan secara publik pada kedua profil ini?</h3><p>GOOSE dan Control berstatus Not tested pada kedua profil. Ini gap evidence field publik, bukan pernyataan bahwa ARSAS tidak memiliki kemampuan tersebut. Service yang tidak dicantumkan oleh satu profil tetap Not declared, bukan Not tested.</p></div>
<div class="feature-grid">
<article class="card" data-uncovered-service="goose"><span class="status-pill">Not tested pada kedua profil</span><h4>GOOSE</h4><p>Kumpulkan capture yang disanitasi: identitas publisher, GoID/AppID, stNum/sqNum, TAL, urutan payload dan kecocokan DataSet; sertakan temuan negatif atau frame yang hilang.</p></article>
<article class="card" data-uncovered-service="control"><span class="status-pill">Not tested pada kedua profil</span><h4>Control</h4><p>Uji di lingkungan non-energized yang berwenang: ctlModel, urutan Direct/SBO, hasil service dan CommandTermination, rejection/AddCause serta process feedback yang diverifikasi terpisah.</p></article>
<article class="card" data-stable-retest="not-publicly-documented"><span class="status-pill">Retest stable terbaru belum terdokumentasi</span><h4>Uji ulang pada v{{STABLE_VERSION}}</h4><p>Untuk tiap service, sertakan tag stable dan versi ARSAS yang tepat; tanggal uji dan model/firmware IED anonim atau batas disclosure; hasil positif dan negatif dengan diagnostic serta kondisi acquisition yang disanitasi. Riwayat engineering lama tidak menggantikan uji field baru.</p></article>
</div>
<p class="matrix-boundary"><strong>Kontribusi hasil yang bounded:</strong> tulis tanggal uji sebenarnya, versi release, jenis evidence dan kondisi service. Sertakan hasil negatif; hapus identifier sensitif. <a href="https://github.com/masarray/arsas/issues/new?template=device-compatibility.yml" target="_blank" rel="noopener">Kirim evidence per service →</a> Matrix di atas tetap menjadi sumber status Not tested dan Not declared per profil.</p>
</div>
</div>
</section>

Expand Down
9 changes: 9 additions & 0 deletions landing/templates/compatibility.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ <h2>See exactly which IEC 61850 services have public evidence—and which do not
</table>
</div>
<p class="matrix-boundary"><strong>How to read this:</strong> one successful service does not imply another service works, and no row certifies IEC 61850 conformance. Open the profile below to see the exact conditions and public engineering records behind each status.</p>
<div class="evidence-gap-panel" data-evidence-coverage="published-anonymized-profiles-only">
<div class="section-head"><span class="kicker">Evidence gaps · next capture</span><h3>What is missing from these two published profiles?</h3><p>GOOSE and Control are marked Not tested in both profiles. This is a gap in public field evidence, not a claim that ARSAS lacks either capability. A service omitted from one profile remains Not declared, not Not tested.</p></div>
<div class="feature-grid">
<article class="card" data-uncovered-service="goose"><span class="status-pill">Not tested in both profiles</span><h4>GOOSE</h4><p>Sanitized capture showing publisher identity, GoID/AppID, stNum/sqNum, TAL, payload order and expected DataSet mapping; include negative or missing-frame findings.</p></article>
<article class="card" data-uncovered-service="control"><span class="status-pill">Not tested in both profiles</span><h4>Control</h4><p>Approved non-energized test showing ctlModel, Direct/SBO sequence, service and CommandTermination results, rejection/AddCause and independently checked process feedback.</p></article>
<article class="card" data-stable-retest="not-publicly-documented"><span class="status-pill">Current stable retest not documented</span><h4>Recheck on v{{STABLE_VERSION}}</h4><p>For any service, provide the exact stable tag and ARSAS version; test date and anonymized IED model/firmware or disclosure boundary; positive and negative service result with sanitized diagnostics and acquisition conditions. Historical engineering records do not substitute for a new field test.</p></article>
</div>
<p class="matrix-boundary"><strong>Contribute a bounded result:</strong> state the actual test date, release version, evidence type and service conditions. Preserve negative results; remove sensitive identifiers. <a href="https://github.com/masarray/arsas/issues/new?template=device-compatibility.yml" target="_blank" rel="noopener">Submit service evidence →</a> The matrix above remains the source for profile-specific Not tested and Not declared statuses.</p>
</div>
</div>
</section>

Expand Down
53 changes: 53 additions & 0 deletions scripts/validate-adoption-proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,45 @@ def main() -> int:
if not isinstance(urls, list) or not urls or any(url not in links for url in urls):
errors.append(f"{profile_id}/{service}: unsupported service-level engineering record")

# R6.3 coverage is computed from declared profile statuses; an omitted service is
# Not declared, never silently converted into Not tested.
plan = evidence.get("coveragePlan")
if not isinstance(plan, dict) or plan.get("scope") != "published-anonymized-profiles-only":
errors.append("device-evidence.json: missing bounded coverage plan")
plan = {}
service_order = plan.get("serviceOrder")
declared_services = set().union(*(set(p.get("services", {})) for p in profiles if isinstance(p, dict) and isinstance(p.get("services"), dict)))
if not isinstance(service_order, list) or len(service_order) != len(set(service_order)) or set(service_order) != declared_services:
errors.append("device-evidence.json: coverage service inventory differs from profile declarations")
service_order = []
uncovered = {
service for service in service_order
if profiles and all(
isinstance(profile, dict)
and isinstance(profile.get("services"), dict)
and profile["services"].get(service) == "not-tested"
for profile in profiles
)
}
gaps = plan.get("noPublishedTestAcrossProfiles")
if not isinstance(gaps, list) or len(gaps) != len(set(gaps)) or set(gaps) != uncovered:
errors.append("device-evidence.json: cross-profile gaps must be derived from explicit Not tested statuses")
gaps = []
next_evidence = plan.get("nextEvidence")
if not isinstance(next_evidence, dict) or set(next_evidence) != uncovered or any(
not isinstance(value, str) or len(value.strip()) < 60 for value in next_evidence.values()
):
errors.append("device-evidence.json: each uncovered service needs a concrete capture request")
next_evidence = {}
stable_retest = plan.get("stableRetest")
if not isinstance(stable_retest, dict) or stable_retest.get("state") != "not-publicly-documented" or not isinstance(stable_retest.get("requirements"), list) or len(stable_retest["requirements"]) != 3 or any(not isinstance(item, str) or len(item.strip()) < 20 for item in stable_retest["requirements"]):
errors.append("device-evidence.json: incomplete current-stable retest intake requirements")
if any(isinstance(p, dict) and p.get("lastRetest") is not None for p in profiles) and isinstance(stable_retest, dict) and stable_retest.get("state") == "not-publicly-documented":
errors.append("device-evidence.json: retest declaration changed; refresh coverage plan and page claims")
Comment on lines +286 to +290

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Separate historical retests from current-stable verification

When a profile records any valid lastRetest, this check rejects the still-correct not-publicly-documented state even if that retest used an older release; meanwhile the preceding validation rejects every alternative state. For example, a retest on 1.6.20 while stable is 1.6.40 cannot be represented without failing CI. Compare the retest version with the current stable version and support the documented state transition rather than treating every retest as current-stable evidence.

AGENTS.md reference: AGENTS.md:L178-L178

Useful? React with 👍 / 👎.


issue_form = read(ROOT / ".github" / "ISSUE_TEMPLATE" / "device-compatibility.yml", errors)
require_values(issue_form, "device-compatibility.yml", ("id: evidence-date", "id: evidence-kind", "id: prior-profile", "actual test date", "engineering or implementation history only"), errors, "R6.3 evidence intake")

for name in ("compatibility.html", "bukti-kompatibilitas.html"):
text = read(TEMPLATES / name, errors)
if 'data-evidence-matrix="true"' not in text:
Expand Down Expand Up @@ -290,6 +329,20 @@ def main() -> int:
errors.append(f"{name}: evidence matrix drift for {profile_id}/{service}/{status}")
if 'data-evidence-freshness="true"' not in text or "{{STABLE_VERSION}}" not in text:
errors.append(f"{name}: current stable source or historical freshness note missing")
if 'data-evidence-coverage="published-anonymized-profiles-only"' not in text or 'data-stable-retest="not-publicly-documented"' not in text:
errors.append(f"{name}: missing bounded cross-profile coverage and stable-retest gap")
for service in gaps:
if f'data-uncovered-service="{service}"' not in text:
errors.append(f"{name}: missing explicit public evidence gap for {service}")
for service in declared_services - set(gaps):
if f'data-uncovered-service="{service}"' in text:
errors.append(f"{name}: declared service incorrectly shown as uncovered: {service}")
if name == "compatibility.html":
for prompt in next_evidence.values():
if prompt not in text:
errors.append(f"{name}: coverage capture guidance drift from registry")
else:
require_values(text, name, ("GOOSE", "Control", "Not tested", "Not declared", "tanggal uji", "retest"), errors, "Indonesian coverage guidance")
if name == "compatibility.html":
require_values(text, name, ("Historical evidence", "not publicly recorded", "not documented", "not raw field captures"), errors, "R6.2 historical/retest boundary")
else:
Expand Down
Loading