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

8.0 KiB

name: ignition-exchange-convert description: Convert an existing Ignition project into a publishable Exchange resource — re-namespacing views/styles/scripts, migrating Perspective session properties to view properties, renaming Python to the guide's conventions — and prove the conversion did not break anything. Use when asked to make a project Exchange-ready, de-brand a project for publication, or refactor one out of session-scoped state. Bundles verify_refs.py and parity.py, which catch the two failure modes a conformance checker cannot see: dangling references and silently dropped features. Not for merely checking conformance (use ignition-exchange-conformance) or uploading (use ignition-exchange-publish).

Converting a project into an Exchange resource

A conversion is a large mechanical rename plus one genuine refactor. It is very easy to produce a project that is perfectly named and completely broken — so the verification gates below are not optional garnish, they are the point.

Check the target shape first with ignition-exchange-conformance; this skill is what to do about the findings.

Work on a copy

Never convert in place. Copy the project to its new name, leave the original untouched, and diff against it at the end. The original is your parity baseline — without it you cannot prove you did not drop a feature.

Ignition project folder name is the project name, so the copy's directory must already be the new lowercase-dashed name.

Order matters

Do it in this order. Step 2 invalidates bindings that step 3 would otherwise have to rewrite twice.

  1. Delete what must not ship. Vision client-tags/, scratch views, dev-only resources.
  2. Session properties → view properties, while paths are still the familiar old ones. Verify the app still works before touching namespaces.
  3. Re-namespace views, style classes, scripts, page config — one mechanical sweep.
  4. Python pass — tabs, camelCase, docstrings, loggers.
  5. Content sweep — branding, internal names, placeholder text.
  6. Verify (gates below), then package.

Step 2 is the real refactor

Everything else is search-and-replace. This one changes how the app works.

The guide forbids session custom properties in an Exchange resource because importing merges them into the host project's session props. Replace with:

  • State lives on the root view as custom properties.
  • Flows down to child views as params (the guide is explicit: params = public/configuration, custom = internal).
  • Flows up as page-scoped messages handled on the root view, named exchange.resourceName.handlerName.

Bindings must stay reactive. Do not park binding-read state in system.util.getGlobals() — globals do not trigger Perspective binding updates. Globals are only for non-reactive cross-session data, and most resources need none.

Before rewriting a binding, check what it actually depended on. A bidirectional binding that writes a session prop does not necessarily re-trigger the data fetch; if the fetch keyed off a refreshToken, then an explicit Apply button was already the commit point and your message-based version preserves behaviour exactly. Read the dependency graph rather than assuming.

Jython 2.7 constraints

Gateway-scoped code is Jython 2.7. Violations are silent until they reach the gateway.

  • No f-strings. % formatting only.
  • No typing, statistics, zoneinfo.
  • Java exceptions bypass except Exception: — use bare except: with sys.exc_info() in gateway-facing defensive code.
  • Scripts embedded in view.json are JSON strings that must be tab-indented: the first character of every line in the block is a tab.

Python renames

The guide wants camelCase functions and variables (deliberately not PEP-8) and tab indentation. Two traps:

  • Rename through the tokenizer, not regex. A blind replace will corrupt string literals and attribute names that happen to match (lt.tm_wday).
  • Do not rename data-dictionary keys. Keys inside dicts returned to view bindings are a wire format between the scripts and every binding that reads them. They are not covered by any rule and renaming them is a large, risky diff for no conformance gain.

Tab conversion applies to indentation only — bracket continuation lines starting with spaces are fine and normal. Verify with the tokenizer's INDENT tokens, not by grepping for leading spaces.

Verification gates

Run all four. Each catches something the others cannot.

1 · Conformance

python3 skills/ignition-exchange-conformance/exchange_lint.py <new-project>

Target: 0 failures. Warnings are a judgement call.

2 · Referential integrity

python3 skills/ignition-exchange-convert/verify_refs.py <new-project> --old <OldNamespace>

Resolves every embedded view path, style-class reference, view.params.X / view.custom.X, and every call into the project library against the functions that actually exist. Also flags leftover old-namespace strings, surviving session.custom reads, untabbed script transforms, f-strings, and resource.json drift. Target: 0 breakages.

With ~40 function renames, a missed call site is the single most likely defect, and nothing else finds it.

3 · Feature parity

python3 skills/ignition-exchange-convert/parity.py <original-project> <new-project>

Fingerprints every view — component types and names, bindings by target property and type, script transforms, event and message handlers, embedded views, declared params — with names normalised for case, punctuation and snake/camel. A pure rename cancels out; a dropped feature does not.

Target: zero LOST or MISSING lines. ADDED lines are expected — the session-props refactor legitimately adds params and handlers. Read the diff, do not just count it.

4 · Behaviour

If the project has a test suite over its pure modules, run it against the converted library. Point the package name at the new location and alias camelCase functions and their keyword arguments back to the original names — a renamed kwarg (higher_is_worsehigherIsWorse) breaks callers that pass it by name, which no static check catches.

Compare against the original project's result, not against zero failures. A pre-existing failure staying pre-existing is a pass.

Then: load it on a gateway

Static analysis cannot prove a screen renders. Load the project and open it. A clean project load proves resources parse; only a browser proves components render.

De-branding

Conformance checkers read names, never label text. Sweep user-visible strings explicitly — props.text, title, placeholder, tooltip — for company names, project codenames, contest or demo references, internal hostnames, ticket numbers. Strip the text rather than deleting the component, so layout and parity are unchanged.

Testing a minimum-version claim

To claim a floor lower than the gateway you built on, load the project on that version — against a copy of the files. An older gateway rewrites every resource.json with lastModification metadata and leaves root-owned .resources/ artifacts behind that your user account cannot delete (clean up with a throwaway container rather than sudo).

A clean load proves resources parse. It does not prove screens render — a component property introduced in a later version fails at render time, not load time. Open the client.

Reporting

Report what the gates actually said, with numbers. A conversion reported accurately with two known gaps is worth far more than one claimed clean. If a gate is a false positive, say which and why — the tooling is not infallible and both bundled scripts have known blind spots documented in their docstrings.

Provenance

Procedure and both scripts derived from a full conversion of a 20-view / 32-style / 3-module Perspective project (2026-09-15), verified on Ignition 8.1.20 and 8.3.7. The gate thresholds are the ones that actually caught defects during it.