Skip to content

Latest commit

 

History

History
234 lines (201 loc) · 13.8 KB

File metadata and controls

234 lines (201 loc) · 13.8 KB
project iris-lab
task IRIS DTL/ObjectScript learning workbench with Notepad++ round-trip
effort E3
phase observe
progress 0/36
mode build
started 2026-08-04
updated 2026-08-04

Problem

Shawn is 19 years deep in HL7 interface engineering (Mirth, BridgeLink, Epic, Cerner) and is now learning InterSystems IRIS for interoperability roles. IRIS transformation code (DTL + embedded ObjectScript) cannot be learned by reading — it only clicks when you can edit a transform, run it against a real message, and see the delta immediately. Today he has no IRIS instance, no execution loop, and no way to run a transform from the editor he actually lives in (Notepad++). The InterSystems-native answer (Management Portal DTL editor, VS Code ObjectScript extension) requires leaving that editor and, more importantly, requires an IRIS instance he does not yet have.

Vision

Shawn edits Transform.cls in Notepad++, hits one hotkey, and the transformed message appears in the second Notepad++ view within a couple of seconds — with compile errors landing in the same pane instead of a stack trace he has to go hunting for. The loop is tight enough that he learns DTL the way he learned HL7: by breaking it and watching what changes. Because PipeHat already owns the second view and already has field-aware Compare Views, the "what did the transform actually change" question gets answered visually, per field, with zero extra code.

Out of Scope

Not building a new Notepad++ plugin — PipeHat already exists and already solves message loading, parsing, tree view, and diffing. Not building a DTL editor, DTL syntax highlighting, or a graphical mapper; the Management Portal already has a good one and competing with it is wasted effort. Not building a production IRIS deployment, a production namespace, security hardening, or a FHIR server. Not wrapping IRIS in a REST API in v1 — that is a later performance optimization, not a v1 requirement. Not touching PHI: the lab runs synthetic messages only.

Constraints

  • Windows 11 host, Docker Desktop available (currently stopped), bun 1.3.14 present.
  • IRIS for Health Community Edition is required, not plain IRIS Community: EnsLib.HL7.* and the HL7 schema library ship only in the irishealth-community image.
  • Community Edition is licensed for development/learning only, single-instance, capped cores/sessions. Acceptable for this purpose, not for anything client-facing.
  • Driver code is TypeScript on bun. ObjectScript is unavoidable on the IRIS side.
  • No new Notepad++ plugin DLL. Integration is via NppExec (a stock Plugin Admin plugin) in v1, and via a PipeHat feature branch in v2.
  • No PHI in the lab directory, ever.

Goal

A one-command local IRIS for Health sandbox plus a bun driver such that editing lab/Transform.cls and lab/input.hl7 in Notepad++ and pressing an NppExec hotkey produces the transformed HL7 in lab/output.hl7 within 5 seconds, with compile and runtime errors rendered into that same file rather than lost inside the container.

Criteria

Sandbox

  • ISC-1: docker compose up -d in iris-lab/ starts a container named iris-lab
  • ISC-2: The image is intersystems/irishealth-community (HL7 stack present), not iris-community
  • ISC-3: Container exposes the portal and 1972 on localhost (host 42773, not 52773 — reserved)
  • ISC-4: docker compose bind-mounts host ./lab to container /lab read-write
  • ISC-5: Management Portal returns HTTP 200 — not probed; the loop never touches HTTP
  • ISC-6: ##class(%EnsembleMgr).IsEnsembleNamespace("USER") returns 1 after setup
  • ISC-7: ##class(EnsLib.HL7.Message) resolves in the target namespace
  • ISC-8: HL7 schema 2.5 resolves — ADT_A01 DocType applied at runtime
  • ISC-9: Setup is idempotent — run once, not yet re-run

Harness

  • ISC-10: Lab.Runner class loads and compiles into the target namespace
  • ISC-11: Lab.Runner.Run() reads /lab/input.hl7 without a hardcoded host path
  • ISC-12: Lab.Runner.Run() compiles /lab/Transform.cls at every invocation
  • ISC-13: A DTL compile error is reported as COMPILE FAILED on stderr, exit 1, empty stdout
  • ISC-14: The compile-error text includes the offending line, not just a %Status code
  • ISC-15: MSH-9 and MSH-12 drive DocType resolution, not a hardcoded DocType
  • ISC-16: An unresolvable DocType falls back to raw parse — code path written, not provoked
  • ISC-17: A runtime <UNDEFINED> reports RUNTIME FAILED — not provoked
  • ISC-18: Output HL7 has one segment per line (CR converted to CRLF) for Notepad++
  • ISC-19: Lab.Runner prints one status line (class, DocType, elapsed ms) to session stdout, keeping output.hl7 pure HL7 so PipeHat can still parse it

Driver (UNIX filter, per the PipeHat provider contract)

  • ISC-20: bun xform.ts exits 0 on a successful transform
  • ISC-21: bun xform.ts exits non-zero when the transform failed to compile
  • ISC-22: Driver detects a stopped Docker daemon and prints a single actionable line
  • ISC-23: Driver detects a stopped iris-lab container and prints the start command
  • ISC-24: Driver round-trip completes in under 5 seconds on a warm container
  • ISC-25: Driver deletes stale lab/output.hl7 before each run, so a failed run can never emit the previous run's message as this run's result
  • ISC-26: Diagnostics go to stderr, transformed message to stdout — never mixed
  • ISC-37: Driver reads the message from stdin and writes the result to stdout
  • ISC-38: Driver falls back to lab/input.hl7 when stdin is empty, so the file-based debug workflow still works by hand

Learning content

  • ISC-27: lab/input.hl7 is a valid synthetic ADT^A01 that parses under schema 2.5
  • ISC-28: lab/Transform.cls is a working DTL that a beginner can read top to bottom
  • ISC-29: The sample transform demonstrates <assign>, <if>, and <foreach>
  • ISC-30: The sample transform includes one embedded ObjectScript <code> block
  • ISC-31: README maps each Mirth concept Shawn knows to its IRIS equivalent
  • ISC-32: README states the Community Edition license limitation explicitly
  • ISC-33: README documents the exact NppExec command string to paste

Anti-criteria

  • ISC-34: Anti: No new Notepad++ plugin DLL is created — PipeHat is the plugin
  • ISC-35: Anti: No real PHI is written into lab/ at any point
  • ISC-36: Anti: No IRIS credential is hardcoded in a file that gets committed

Test Strategy

isc type check threshold tool
ISC-1..4 infra docker ps + docker inspect mounts container up Bash
ISC-5 http curl -s -o /dev/null -w '%{http_code}' 200 Bash
ISC-6..8 objectscript batch script through iris session prints 1 Bash
ISC-10..19 behavior run harness with good and deliberately broken inputs output file content matches Bash + Read
ISC-20..26 cli invoke bun xform.ts, check exit code + stdout exact Bash
ISC-27..33 content Read the file, confirm required element present present Read/Grep
ISC-34..36 anti Grep repo for .dll build targets, PHI patterns, literal passwords zero hits Grep

Features

name satisfies depends_on parallelizable
compose-sandbox ISC-1..5 none yes
namespace-setup ISC-6..9 compose-sandbox no
runner-class ISC-10..19 namespace-setup no
bun-driver ISC-20..26 runner-class yes
sample-content ISC-27..30 none yes
docs-and-nppexec ISC-31..33 bun-driver yes
pipehat-integration (v2) deferred bun-driver no

Decisions

  • 2026-08-04 — Image must be intersystems/irishealth-community, not intersystems/iris-community. EnsLib.HL7.* and the HL7 v2 schema library are only in the for-Health edition. Choosing the plain image is the single most likely way to lose an afternoon here.
  • 2026-08-04 — v1 transport is docker exec + a bind mount, not a REST endpoint. A REST endpoint needs a %CSP.REST subclass, a web application, and an auth decision; a bind mount needs none of that and the latency difference does not matter for a learn loop. REST is the v2 upgrade once the loop is proven.
  • 2026-08-04 — No new Notepad++ plugin. PipeHat already provides message loading, the second view, and field-aware Compare Views. Building a second plugin duplicates all of it.
  • 2026-08-04 — refined: original request framed this as "a plugin". Aperture oscillation against the strategic context (PipeHat exists, job search is the real goal) moved the deliverable from "new plugin" to "execution loop + PipeHat feature", which is both less work and more portfolio-relevant.
  • 2026-08-04 — Delegation floor (E3 soft, >=2) not met: 0 delegation capabilities invoked. Show your math — Forge/Anvil would have written the same ObjectScript harness from the same docs I already fetched, and the session's operating instruction forbids spawning agents unasked. The work is single-author and doc-bound, not exploration-bound.
  • 2026-08-04 — ISA written directly rather than via Skill("ISA", "scaffold"). The canonical twelve-section format was already loaded in context from the Algorithm file; a second load adds tokens and no structure. Deviation logged here per doctrine.
  • 2026-08-04 — Docker Desktop daemon is stopped on this host, so ISC-1..26 cannot be live-probed this run. They stay [ ] rather than being claimed. No [x] without evidence.
  • 2026-08-04 — refined: PipeHat must stay vendor-neutral (principal's call, and correct — it is a portfolio artifact and an open HL7 tool). Resolution is not "keep them apart" but "define a neutral seam": PipeHat grows a generic External Transform Provider that knows only stdin -> stdout, exit code, and iris-lab becomes one provider behind it. PipeHat gains a feature that stands on its own merits for Mirth/Rhapsody/Saxon users and never learns a vendor name; iris-lab stays a separate repo and a separate portfolio signal.
  • 2026-08-04 — refined: driver reworked from a file-shuffler into a UNIX filter to match that contract. The bind mount stays as the container transport, but it is now an implementation detail behind stdin/stdout rather than the interface.
  • 2026-08-04 — PipeHat feature shipped this run: src/TransformProvider.h, tests/TransformProviderTest.cpp, main.cpp glue, docs. Left uncommitted in his repo deliberately — it is his git history and the review is his to make.

Verification

Live, end to end. cat lab/input.hl7 | bun xform.ts → exit 0, OK Lab.Transform 159 ms | round trip 351 ms, transformed HL7 on stdout. Every DTL construct in the sample fired and is visible in the output:

ISC Construct Evidence in the returned message
ISC-29 <assign> MSH-3 SENDAPPIRISLAB
ISC-29 <assign> + $zconvert PID-5.1 doeDOE
ISC-29 <if> / <true> PID-8 MMALE
ISC-29 <foreach> both PID-3 repetitions got .5 = LAB
ISC-30 <code> MSH-10LAB2026080462127
ISC-15 DocType resolution 2.5:ADT_A01 resolved from MSH-9 + MSH-12, not hardcoded
ISC-24 Latency 351 ms round trip, budget was 5000 ms

The failure path was verified by actually failing. The first run returned exit 1, empty stdout, and on stderr:

COMPILE FAILED -- ERROR #5475: Error compiling routine: Lab.Transform.1.  Errors:
ERROR: Lab.Transform.cls(Transform+50) #1011: Invalid name : 'target.{MSH:10}'

That is ISC-13, ISC-14, ISC-21, ISC-25 and ISC-26 confirmed in one shot, and it caught a real defect in the shipped sample: the {SEG:field} shorthand works in <assign> but not inside <code>, where an HL7 message is a virtual document and needs SetValueAt. A second defect followed — $zdatetime($horolog,8) appends the time, putting a space inside MSH-10. Both are now fixed with the reason written next to the fix.

Two host-environment gotchas cost the first two boots and are now documented in docker-compose.yml:

  • Host port 52773 sits inside a Hyper-V reserved range (52689-52788 on this machine), so the bind fails with "access permissions" before IRIS starts. Host port is now 42773.
  • A durable-%SYS named volume is created root-owned while IRIS runs as uid 51773, so boot dies on Cannot create target: /durable/iris/. The volume is gone; the sandbox is disposable by design and everything durable lives in ./lab and ./src on the host.

Earlier, before Docker was available (6/36):

  • ISC-22: bun xform.ts against a stopped daemon printed exactly Docker daemon is not running. Start Docker Desktop, wait for the whale, retry. and exited 2. One line, actionable, no stack trace.
  • ISC-26: same run — output is plain text, no ANSI, NppExec-console safe.
  • ISC-31: README.md carries a 9-row Mirth/BridgeLink → IRIS mapping table.
  • ISC-32: README.md states "Community Edition is development-and-learning only".
  • ISC-33: README.md carries the verbatim 4-line NppExec script.
  • ISC-34/35/36: rg over iris-lab/ found no .dll build target, no CMakeLists, no real identifiers (SSN999999999 is the reserved-invalid placeholder), and no literal credential outside the documented public default in prose.
  • bun build setup.ts --target=bun succeeded — both drivers parse.

Blocked (30/36): Docker Desktop daemon was stopped on the host, so no container, no namespace, no compile, no transform. src/Lab.Runner.cls and lab/Transform.cls are written against documented APIs but have never been compiled. Highest-risk spots on first run: OutputToLibraryStream argument shape and ResolveSchemaTypeToDocType argument order. These stay [ ], not [DEFERRED-VERIFY], because the probe is possible the moment Docker starts — it just was not possible now.