| 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 |
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.
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.
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.
- 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 theirishealth-communityimage. - 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.
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.
- ISC-1:
docker compose up -diniris-lab/starts a container namediris-lab - ISC-2: The image is
intersystems/irishealth-community(HL7 stack present), notiris-community - ISC-3: Container exposes the portal and 1972 on localhost (host 42773, not 52773 — reserved)
- ISC-4:
docker composebind-mounts host./labto container/labread-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.5resolves —ADT_A01DocType applied at runtime - ISC-9: Setup is idempotent — run once, not yet re-run
- ISC-10:
Lab.Runnerclass loads and compiles into the target namespace - ISC-11:
Lab.Runner.Run()reads/lab/input.hl7without a hardcoded host path - ISC-12:
Lab.Runner.Run()compiles/lab/Transform.clsat every invocation - ISC-13: A DTL compile error is reported as
COMPILE FAILEDon 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>reportsRUNTIME FAILED— not provoked - ISC-18: Output HL7 has one segment per line (CR converted to CRLF) for Notepad++
- ISC-19:
Lab.Runnerprints one status line (class, DocType, elapsed ms) to session stdout, keepingoutput.hl7pure HL7 so PipeHat can still parse it
- ISC-20:
bun xform.tsexits 0 on a successful transform - ISC-21:
bun xform.tsexits 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-labcontainer 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.hl7before 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.hl7when stdin is empty, so the file-based debug workflow still works by hand
- ISC-27:
lab/input.hl7is a valid synthetic ADT^A01 that parses under schema 2.5 - ISC-28:
lab/Transform.clsis 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
- 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
| 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 |
| 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 |
- 2026-08-04 — Image must be
intersystems/irishealth-community, notintersystems/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.RESTsubclass, 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.cppglue, docs. Left uncommitted in his repo deliberately — it is his git history and the review is his to make.
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 SENDAPP → IRISLAB |
| ISC-29 | <assign> + $zconvert |
PID-5.1 doe → DOE |
| ISC-29 | <if> / <true> |
PID-8 M → MALE |
| ISC-29 | <foreach> |
both PID-3 repetitions got .5 = LAB |
| ISC-30 | <code> |
MSH-10 → LAB2026080462127 |
| 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-52788on 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./laband./srcon the host.
Earlier, before Docker was available (6/36):
- ISC-22:
bun xform.tsagainst a stopped daemon printed exactlyDocker 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.mdcarries a 9-row Mirth/BridgeLink → IRIS mapping table. - ISC-32:
README.mdstates "Community Edition is development-and-learning only". - ISC-33:
README.mdcarries the verbatim 4-line NppExec script. - ISC-34/35/36:
rgoveriris-lab/found no.dllbuild target, noCMakeLists, no real identifiers (SSN999999999is the reserved-invalid placeholder), and no literal credential outside the documented public default in prose. bun build setup.ts --target=bunsucceeded — 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.