Files
BAT/exchangeResources/claude-skills/ignition-exchange-conformance/SKILL.md
2026-09-15 13:06:45 -05:00

6.8 KiB

name, description
name description
ignition-exchange-conformance Check whether an Ignition project follows Inductive Automation's Exchange Resources Style Guide before publishing it to the Ignition Exchange — resource namespacing, Perspective/Vision/Python naming, session-property and client-tag prohibitions, database and logger conventions. Use when asked whether a project is Exchange-ready, when reviewing a project intended for the Exchange, or before packaging one for upload. Bundles a stdlib-only checker (exchange_lint.py) with 38 rules traceable to guide sections. Not for converting a project into that shape (use ignition-exchange-convert) or for packaging and upload (use ignition-exchange-publish).

Ignition Exchange conformance

Tests an Ignition project against the Exchange Resources Style Guide v1.1.0.

Run exchange_lint.py (next to this file) rather than checking by eye — the rules are numerous, mechanical, and easy to half-apply.

python3 skills/ignition-exchange-conformance/exchange_lint.py /path/to/Project

Stdlib only, Python 3.8+. Accepts a project folder, a folder containing several projects (a gateway's projects/ directory, an unpacked backup, a repo), or a project export .zip. Infers the resource name from the namespace the project already uses — no configuration.

Exit codes: 0 clean · 1 findings · 2 bad invocation.

The one rule that is actually mandatory

The guide states exactly one must:

Views, Styles, Scripts, etc. must all be contained within an appropriately named folder. This allows easy imports into existing projects, where those project resources won't overlap, interfere, or overwrite existing parts of a user's project.

Everything else — every naming convention — the guide explicitly calls optional:

We encourage you to follow the naming conventions outlined here, however these conventions are not required.

So exchange_lint.py reports FAIL only for namespacing, the storage prohibitions, and the project-name character set. Naming is warn. Don't report warnings as blockers, and don't contort a project to silence them — a consistent house style is defensible and the guide says so.

Namespace roots — two spellings, both correct

Per the guide's Project Browser screenshots:

Under Exchange/<ResourceName>/ Under exchange/<resource-name>/
Views, Windows, Templates Style classes
Named queries, Reports Project library (scripts)
SFCs, Transaction groups WebDev sources
Alarm pipelines, Tags Images

Uppercase roots take Title Case or PascalCase names; lowercase roots take kebab-case — except the project library, which is code-facing and uses lowercase or camelCase (exchange.resourceName.scriptName), matching the logger convention exchange.resourceName.LoggerName.

The storage prohibitions

Both are FAILs because both break a clean import, and both are easy to miss:

  • Perspective session custom properties. Importing merges them into the host project's session props. Use view params/custom plus, for genuinely non-reactive cross-session state, system.util.getGlobals()['exchange']['resourceName'].
  • Vision client tags. Same reasoning.

What the checker cannot tell you

A clean report is not "ready to publish". Four gaps, all of which have bitten:

  1. Content. It reads names and structure, never label text. A visible string like "Acme Internal — do not distribute" passes silently. Always sweep user-visible strings separately:
    python3 - <<'PY'
    import json, glob, re
    bad = re.compile(r"internal|confidential|do not|contest|demo only|todo|fixme", re.I)
    for f in glob.glob("<project>/com.inductiveautomation.perspective/views/**/view.json", recursive=True):
        v = json.load(open(f))
        def walk(n):
            yield n
            for c in (n.get("children") or []): yield from walk(c)
        for n in walk(v.get("root") or {}):
            t = (n.get("props") or {}).get("text")
            if isinstance(t, str) and bad.search(t): print(f, repr(t))
    PY
    
  2. Referential integrity. It does not check that embedded view paths, style classes or script calls resolve. See ignition-exchange-convert.
  3. Runtime. Nothing is executed or rendered. Load it on a gateway.
  4. The shipped package. E-UP-DOCS checks the working directory, not the export zip. See ignition-exchange-publish.

Findings that need interpretation

E-STYLE-HIER false positives. It decides where a style class belongs by finding which views reference it, reading only literal props.style.classes strings. A class name built in a binding — 'exchange/my-resource/priority/' + level.lower() — is invisible, so a shared class gets reported as single-use. Verify before acting; do not duplicate style classes to silence it.

E-PAGE-ROOT is not in the guide. It is an added caution: a page mapped to / takes over the root URL of any project the resource is imported into. Real trade-off — a resource with no / page cannot be launched from the gateway's Perspective project list, only from its full page URL (/data/perspective/client/<project>/<page>). Flag the trade-off; let the user decide.

E-PY-VAR volume. A project written in idiomatic snake_case produces hundreds of warnings, since the guide wants camelCase for variables and functions (deliberately diverging from PEP-8; UPPER_SNAKE constants are exempt). Offer --ignore E-PY-VAR,E-PY-FUNC rather than a mass rename, unless the user wants full conformance.

Useful flags

Flag Effect
--strict Warnings become failures
--ignore E-PY-VAR,E-COMP-NAME Suppress rules
-v Every occurrence (default collapses at 8 per rule)
--json Machine-readable, for CI
--list-rules All 38 rules with descriptions

Reporting results

The summary reads N of 38 rules exercised. Quote it. A project with no database, tags or named queries legitimately leaves those rules unexercised, and a narrow pass should never be presented as a broad one.

Self-test

test_exchange_lint.py builds a conforming and a non-conforming project in a temp dir, asserts the checker is silent on the first and fires the expected rule id on the second, and asserts every declared rule is reachable in the source. Run it after changing any rule.

python3 skills/ignition-exchange-conformance/test_exchange_lint.py

Provenance

Rules derived from Exchange Resources Style Guide v1.1.0. Checker exercised against four real Perspective projects and validated on Ignition 8.1.20 and 8.3.7 gateways (2026-09-15). Where a rule is an inference rather than guide text it says so in its description — currently only E-PAGE-ROOT.