Skip to content

Latest commit

 

History

History
172 lines (120 loc) · 18.1 KB

File metadata and controls

172 lines (120 loc) · 18.1 KB
id server-side-tooling-split
title Investigation — Server-side vs container-side tooling (thick-server vs thin-server for the inner loop)
type investigation
status accepted
created 2026-05-25
updated 2026-05-25
tags
dev-bridge
server-side
tooling
inner-loop

Investigation — Server-side vs container-side tooling (thick-server vs thin-server for the inner loop)

Status: Investigation + recommendation → adopted by m-cli-go-toolchain-spec.md §9 · Updated: 2026-05-25 Companion to: vista-iris-dev-bridge-spec.md (core §6.5, §8) · …-public.md · …-va.md · vscode-plugin-design.md · iris-native-source-control-investigation.md Question driving this doc: How much of the development tooling could be pushed server-side into IRIS — running inside the dev instance (as M, ideally in the standard library) — so that unit testing and the watch/inner-loop run on the development server, with the VS Code plugin connecting from outside? The dev instance is a non-production test/dev VistA, installed once, with no impact on operational systems.

⚠ Framing. This is an analysis of a design fork, not a contract change. The core spec deliberately swung container-ward.m-first, file-side tests on YottaDB, the tree-sitter-m stack beside IRIS — and set aside the IRIS-native engine (iris-native-source-control-investigation.md). This document asks the opposite question and finds the inner loop bisects cleanly: the run-and-verify half belongs server-side; the read-understand-edit half does not. Adopted: m-cli-go-toolchain-spec.md §9 adopts the bisection and the server-resident pure-M run/verify half, resolving the six open questions below (§9.1).


0. TL;DR

  1. The inner loop bisects along one line: does the work need the live FileMan DD + data or the compiler (→ server-side), or does it need the parse tree (→ container-side)? Everything sorts onto one side or the other (§1).
  2. Most of the run-and-verify half can live in IRIS — compile, execute, the integration unit-test tier, a watch/compile-test-coverage daemon, and drift detection. The "one-time install on a non-prod dev box" premise removes the only safety objection to putting them there (§2).
  3. The static half resists the movem lint / m fmt / m lsp are built on tree-sitter-m (C/WASM). Pushing them into IRIS means re-implementing the M parser in M; that is the expensive part, and it would fork the single parse substrate (§2, vscode-plugin-design §6.1).
  4. The decisive nuance: there is a difference between pushing tooling into IRIS's proprietary %-library (server-side but locks to IRIS, breaks the YottaDB / .m-first goal) and shipping it as portable pure-M in m-stdlib that merely runs server-side on the dev IRIS (server-side and still portable). Prefer the latter for everything that moves (§4).
  5. Net effect on the design: if adopted, the VS Code plugin shrinks toward a thin trigger + renderer over a fat dev server — closer to the IRIS-native investigation's original recommendation than to v4. It is a fidelity/simplicity ↔ portability/parity trade, stated honestly in §5.

1. The dividing line

Every piece of inner-loop tooling sorts by a single test:

Does it need the live runtime (the compiled routines + FileMan DD + data), or does it need the source parse tree?

  • Runtime-coupled → wants to run where the runtime is = inside IRIS. The whole reason the core keeps an IRIS integration tier at all is that routine behavior depends on the target's DD + data (core §6.4); that dependency exists only server-side, so the work that exercises it is most faithful there.
  • Source-coupled → wants to run where the parse substrate is = the tree-sitter-m WASM stack in the remote-dev container (vscode-plugin-design §6.1). IRIS has no tree-sitter; moving this work means rebuilding the grammar in M.

So "how much can go server-side?" = essentially the entire run-and-verify half of the loop, plus source control (which the IRIS-native investigation already showed is server-side). Not the read-understand-edit half.

        the inner loop, bisected
   ┌───────────────────────────────────────────────────────────────────────┐
   │  read / understand / edit         │   run / verify                       │
   │  (source-coupled → CONTAINER)     │   (runtime-coupled → IRIS dev box)   │
   │                                   │                                      │
   │  • tree-sitter-m highlight        │  • compile  ($SYSTEM.OBJ.Load)       │
   │  • m lint / m fmt (72 rules)      │  • execute                           │
   │  • m lsp (diag/nav/complete)      │  • INTEGRATION unit tests (DD+data)  │
   │  • vista-meta hover / domain      │  • WATCH: recompile→retest→coverage  │
   │  • the editor UI                  │  • drift detection (DB↔FS)           │
   │                                   │  • source-control round-trip         │
   └───────────────────────────────────────────────────────────────────────┘
        VS Code plugin (thin)              the dev VistA-on-IRIS (fat)

2. Component-by-component

Tooling component Server-side fit Why
Compile ($SYSTEM.OBJ.Load/Compile) ✅ already native Runs on IRIS by definition (core §6.5)
Execute / run a routine ✅ always M executes on IRIS
Unit testing — integration tier strong Exists because of the live DD+data (core §6.4, §8.2); that only exists server-side (§3)
Watch / inner-loop daemon good (splits) IRIS primitives exist (background JOB, work-queue manager, task scheduler, OnAfterCompile/OnAfterSave hooks — investigation §B.1); covers compile+test+coverage, not lint/fmt (§3.2)
Drift detection (DB↔FS) Reads the live namespace; per-routine hashes already in the baseline (core §6.3, §14)
Source-control round-trip ✅ already native git-source-control runs server-side and drives the OS git binary (investigation §B.2)
Discovery / seeding ◐ partly Discovery probes are server-side queries; orchestration + baseline are the bridge's (core §5–§6)
Coverage Server-side if the tests run server-side; LCOV rendering stays client-side
Unit testing — fast file-side tier ✅ keep client Runs on YottaDB + parser with no transform; gates the PR without IRIS (core §8.2) — don't move it
Lint / format (m lint/m fmt, 72 rules, taint) ❌ hard Built on tree-sitter-m (C/WASM); no tree-sitter in IRIS (vscode-plugin-design §2.1, §6)
LSP intelligence (m lsp diag/nav/complete) ❌ hard Same parse substrate
VistA domain knowledge (vista-meta TSVs) Could be globals server-side; today precomputed data the editor reads locally (vscode-plugin-design §2.2)
Editor UI ❌ client It is VS Code

Caveat on "❌ hard": it is not impossible — VistA's own XINDEX is pure-M static analysis that already runs resident in the namespace, so a weaker version of in-IRIS static analysis exists today. But tree-sitter-m (99.06% clean on the 39,330-routine corpus, vscode-plugin-design §2.1) is far richer; reimplementing it in M is the cost being weighed, and doing so forks the "one parse substrate" invariant.


3. The two pieces the question names

3.1 Unit testing — the right home is server-side

The core already defines two test tiers (§8.2):

  1. File-side, fastm test/coverage on the .m tree (parser + YottaDB), no transform, gates the PR. Keep this client-side — it is the cheap, frequent gate and needs no IRIS.
  2. IRIS integration — M-Unit / behavioral tests against the actual DD + data. This is the tier the question is really about.

Today the bridge stages routines and orchestrates the integration tests from outside on each run. The server-side move is to install a resident test harness once into the dev namespace(s), so the tests live where the data is:

  • The plugin triggers a run (Atelier REST POST /action/query against a runner method, or a small custom endpoint) and renders results in VS Code's Test Explorer — trigger + render, nothing more on the client.
  • The harness runs against the live FileMan DD + data — exactly what cannot be reproduced file-side (core §6.4), which is why this tier belongs server-side.
  • Keep both tiers. Per-iteration server round-trips are slower than the YottaDB gate; the fast tier stays the PR gate, the resident tier validates against reality.

Implementation choice matters — see §4. A pure-M / M-Unit-style runner shipped in m-stdlib stays portable (runs on YottaDB and the dev IRIS); IRIS's proprietary %UnitTest does not.

3.2 Watch / inner-loop — doable, but it splits

A server-side watch daemon can collapse the round-trip from "save .m → stage → load → run → report" to "the server is already watching." On a routine change it would: recompile → run affected tests → compute coverage → stream results back to the plugin over the same WebSocket transport the Lite Terminal and debugger already use (investigation §A.2, §A.5). IRIS supplies the primitives — background JOB, a parallel work-queue manager, the task scheduler, and the OnAfterCompile/OnAfterSave source-control hooks (investigation §B.1) — to trigger and run it.

But watch bisects exactly like everything else: the static half of "watch on save" — lint and format-on-save — still needs tree-sitter-m. So a server-side watcher covers compile + test + coverage; lint/fmt stay in the container. The developer experiences one loop; it is served by two halves.


4. The decisive nuance: proprietary % vs portable pure-M

"Push it into IRIS" hides two very different moves:

Move Server-side? Portable? Verdict
Into IRIS's proprietary %-library (%UnitTest, %Studio.SourceControl) ❌ — locks to IRIS; breaks the YottaDB / .m-first / public-community goals (core §6.1) avoid for the canonical path
As portable pure-M shipped in m-stdlib, running server-side on the dev IRIS ✅ — same M runs on YottaDB and IRIS prefer

m-stdlib is already described as "a pure-M runtime stdlib" (vscode-plugin-design §2.1). A pure-M test runner and a pure-M watch coordinator can ship as part of it and be installed once into the dev instance. This gets server-side execution without paying the portability tax — it is the move that lets the question's instinct and the core spec's .m-first goal coexist instead of conflict.


5. The trade vs. v4

The IRIS-native investigation already established that IRIS ships the entire round-trip engine server-side (Atelier + isfs + compile + debug + git-source-control + per-item locking) — and v4 set it aside to keep .m first-class and portable. This question is, in effect: reconsider that for the dev box. The trade is real and symmetric:

  • Toward IRIS (this question): everything runs where the data is; no .m.int transform on the run/test path; one box; fewer moving parts; the integration tier and watch loop become native. Cost: portability and the public/community story erode if the tooling is IRIS-proprietary (mitigated by §4).
  • Toward the container (v4 today): portable, YottaDB-native, community-shareable, parity by construction. Cost: the transform boundary (core §6.5) and tooling that lives beside IRIS rather than in it.

What the "one-time, non-prod" premise does and doesn't buy:

  • Removes the safety objection. Modifying a dev/test VistA is fine; git-source-control's own MapEverywhere() + BaselineExport bootstrap already assume exactly such a one-time server install (investigation §B.2).
  • Does not by itself satisfy parity. A server-side component must be installed identically in both profiles to stay [Parity] — which holds, since both the public and VA profiles have an IRIS dev instance (core §9).
  • Does not by itself preserve portability. That is what §4 (pure-M, not %-library) is for.

6. Parity / Profile / Instance mapping (if adopted)

  • [Parity] (identical both environments): the resident pure-M test harness + watch coordinator version set and their command/endpoint contract; the two-tier test model (fast file-side gate + resident integration tier); the bisection itself.
  • [Profile] (differs by environment): whether the dev IRIS host has the OS prerequisites for any server-side git/watch (git ≥ 2.31, callout privileges — investigation §E); the trigger/stream transport endpoint per environment; AI/MCP access to the runner (core §9).
  • [Instance] (discovered): the namespace(s) the harness installs into; the routine/test inventory; the DD+data the integration tier runs against (core §5–§6).

7. How this would amend the existing specs (proposed, not decided)

Current spec position This investigation Possible resolution
Integration tests orchestrated by the bridge from outside on each run (core §8.2) Install a resident test harness in the dev namespace; the plugin triggers + renders Promote the integration tier to a server-resident, pure-M harness; keep the file-side gate unchanged
watch is a file-side affordance driven from the container (vscode-plugin-design §8) A server-side daemon handles compile+test+coverage; lint/fmt stay client-side Split watch into a server half (run/verify) and a client half (static) — one loop, two halves
Round-trip is a bridge-owned .m.int transform at the IRIS boundary (core §6.5) Run-and-verify already lives server-side; the transform feeds it No change to the canonical .m file unit; this is where work runs, not what is versioned
Tooling lives beside IRIS in the remote-dev container (vscode-plugin-design §7.3) Run/verify tooling can live in IRIS as portable pure-M in m-stdlib Reconcile via §4: server-side ≠ IRIS-proprietary

Open questions

Resolved. All six are answered in m-cli-go-toolchain-spec.md §9.1; they remain here as the design rationale.

  1. Pure-M vs %UnitTest. Is a pure-M / M-Unit-style runner in m-stdlib rich enough for the VistA integration tier, or is IRIS %UnitTest needed (and the portability cost accepted on the dev box only)?
  2. Watch trigger. Source-control hook (OnAfterCompile/OnAfterSave), a polling background JOB, or the work-queue manager — which is the robust, low-overhead trigger at the 33,941-routine scale?
  3. Result transport. Reuse the Atelier WebSocket transports (terminal/debug) for streaming watch/test results, or a purpose-built endpoint? How does it map to the VS Code Test Explorer API?
  4. Coverage server-side. Can a pure-M coverage probe produce LCOV the existing gutter renderer consumes, or does coverage stay a file-side concern?
  5. Where does vista-meta's domain model live — precomputed TSVs read by the editor (today) vs. globals queried server-side — and does the watch loop regenerate it on change (vscode-plugin-design §12, TSV-freshness)?
  6. Two-tier drift. With both a fast file-side tier and a resident server tier, how are their results reconciled in the editor so the developer sees one verdict, not two?

References

  • Bridge: vista-iris-dev-bridge-spec.md (core §5–§6, §6.4, §6.5, §8.2, §14), …-public.md, …-va.md.
  • Editor surface: vscode-plugin-design.md (§2.1 m-dev-tools/m-stdlib, §6.1 tree-sitter substrate, §7.3 thin-client topology, §8 bridge affordances).
  • IRIS-native capabilities: iris-native-source-control-investigation.md (§A.2 Atelier/transport, §A.5 terminal/debug WebSockets, §B.1 %Studio.SourceControl hooks, §B.2 git-source-control, §E prerequisites/scale).
  • IRIS frameworks named (not re-verified here): %UnitTest.Manager/%UnitTest.TestCase, background JOB, the parallel work-queue manager, the task scheduler; VistA XINDEX as the pure-M static-analysis precedent; M-Unit as the community pure-M xUnit.