Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions dist/icr-registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,48 @@
"status": "Supported"
}
],
"VSLFS": [
{
"call": "UPDATE^DIE",
"custodian": "DI",
"icr": "DBS",
"source": {
"anchor": "updatedie-updater",
"doc_key": "DI/fm22_2dg"
},
"status": "Supported"
},
{
"call": "$$GET1^DIQ",
"custodian": "DI",
"icr": "DBS",
"source": {
"anchor": "get1diq-data-retriever-single-field",
"doc_key": "DI/fm22_2dg"
},
"status": "Supported"
},
{
"call": "$$GET1^DIQ",
"custodian": "DI",
"icr": "DBS",
"source": {
"anchor": "get1diq-data-retriever-single-field",
"doc_key": "DI/fm22_2dg"
},
"status": "Supported"
},
{
"call": "FILE^DIE",
"custodian": "DI",
"icr": "DBS",
"source": {
"anchor": "filedie-filer",
"doc_key": "DI/fm22_2dg"
},
"status": "Supported"
}
],
"VSLIO": [
{
"call": "CALL^%ZISTCP",
Expand Down
107 changes: 106 additions & 1 deletion dist/msl-seam-pin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"msl_ref": "v0.8.0",
"msl_ref": "v0.9.0",
"seams": {
"STDENV": {
"contract_version": 1,
Expand Down Expand Up @@ -31,6 +31,111 @@
}
]
},
"STDKV": {
"contract_version": 1,
"entry_points": [
{
"args": [
{
"doc": "collection id",
"name": "coll",
"type": "string"
},
{
"doc": "record key",
"name": "key",
"type": "string"
}
],
"label": "$$exists^STDKV(coll, key)",
"raises": [],
"returns": {
"doc": "1 iff the record exists; 0 otherwise",
"type": "bool"
}
},
{
"args": [
{
"doc": "collection id",
"name": "coll",
"type": "string"
},
{
"doc": "record key",
"name": "key",
"type": "string"
},
{
"doc": "field id within the record",
"name": "field",
"type": "string"
},
{
"doc": "value returned when the field is unset",
"name": "default",
"type": "string"
}
],
"label": "$$get^STDKV(coll, key, field, default)",
"raises": [],
"returns": {
"doc": "the stored value, or `default` when unset",
"type": "string"
}
},
{
"args": [
{
"doc": "collection id",
"name": "coll",
"type": "string"
},
{
"doc": "record key",
"name": "key",
"type": "string"
}
],
"label": "$$kill^STDKV(coll, key)",
"raises": [],
"returns": {
"doc": "1 (idempotent — absent record is a no-op)",
"type": "bool"
}
},
{
"args": [
{
"doc": "collection id (VSLFS: a FileMan file number)",
"name": "coll",
"type": "string"
},
{
"doc": "record key (VSLFS: an IENS)",
"name": "key",
"type": "string"
},
{
"doc": "field id within the record (VSLFS: a field number)",
"name": "field",
"type": "string"
},
{
"doc": "the value to store (raw bytes; byte-faithful)",
"name": "value",
"type": "string"
}
],
"label": "$$set^STDKV(coll, key, field, value)",
"raises": [],
"returns": {
"doc": "1 on success",
"type": "bool"
}
}
]
},
"STDNET": {
"contract_version": 1,
"entry_points": [
Expand Down
1 change: 1 addition & 0 deletions dist/namespace-registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"globals": [],
"routines": [
"VSLCFG",
"VSLFS",
"VSLIO"
]
}
Expand Down
1 change: 1 addition & 0 deletions docs/memory/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

One line per memory file. Content lives in the files, not here.

- [m3-vslfs](m3-vslfs.md) — VSL/MSL **M3 Lane B DONE** (2026-06-16): **VSLFS** binds the `STDKV` storage seam (MSL **v0.9.0**) to VistA's FileMan DBS — `$$set`=`UPDATE^DIE` (returns resolved IENS; `"+1,"` adds), `$$get`/`$$exists`=`$$GET1^DIQ`, `$$kill`=`FILE^DIE` with FDA **`.01="@"`** (**no `DELETE^DIE` exists**; `^DIK`/direct KILL forbidden). Re-pinned `msl_ref` v0.8.0→v0.9.0. **Dual-engine GREEN 7/7** (vehu + foia-t12) over **#8989.51** (free-text `.01`, no other required fields → safe ZZ throwaway record; no DD install). DIERR→**`,U-VSL-FS-DIERR,`** `$ECODE` + `$$lastError`; MSG_ROOT="ERR" keeps errors private. **No `$ZVERSION` arm** (FileMan DBS portable). ICR **notional** (`@icr DBS` marker — gen-icr.py taught NOTIONAL_MARKERS; see [[notional-dbia-not-a-blocker]]). 3 boundaries green; suite **22/22** no regression. Branch `m3-vslfs` **stacked on `m2-vslio`** (unmerged). Next: M4 (VSLSEC+VSLLOG).
- [t1.2-vslcfg](t1.2-vslcfg.md) — VSL T1.2 (2026-06-16): **VSLCFG**, the first VSL* module — binds the STDENV config seam to XPAR at the **SYS entity** (`$$get`=`$$GET^XPAR("SYS",…)`, `$$set`=`EN^XPAR("SYS",…)`), validated live on vehu. **All 3 determinism boundaries GREEN** (① re-pin `msl_ref`→v0.7.0 carrying real STDENV; ② check-icr ICR #2263; ③ check-citations vs gold corpus). Citation reconciled: **XU/krn_8_0_dg_toolkit_ug / ICR #2263**, not the plan's XT guess. **Keystone unblock:** driver→live XPAR via `M_YDB_GBLDIR`/`M_YDB_ROUTINES`. **Remaining blocker:** `m test --docker` honors gbldir but NOT M_YDB_ROUTINES → VSLCFGTST aborts 0/0 (XPAR routines unresolved); fix = layer the resident routine base in the m test/m-ydb path (m-cli/m-ydb session) or test-in-place `--resident`.
- [meta-root + owed VSLSEED filer](meta-root-and-owed.md) — layer declared in **root `repo.meta.json`** (migrated off `dist/` 2026-06-15, Phase B item 1); the owed `fileViaDie^VSLSEED` FileMan filer (re-homed from m-stdlib STDSEED per the G2 waterline decision) lands here when a v-layer seeding consumer needs it.
- [t0b4-msl-seam-pin](t0b4-msl-seam-pin.md) — VSL T0b.4 (v-stdlib leg, 2026-06-15): the **cross-repo MSL seam-contract pin** — `dist/msl-seam-pin.json` (pins MSL `v0.6.0` + frozen `seams` copy) + `tools/msl_seam_pin.py` drift gate (`make check-msl-pin`). Reads the sibling MSL @ tag via `git show`; **SKIP-green when unreachable** (so it SKIPs in CI today, asserts at dev-time); fetch-at-tag path deferred to T1.1. Don't conflate with v-stdlib's own (VSL*) `dist/seam-snapshot.json`.
Expand Down
87 changes: 87 additions & 0 deletions docs/memory/m3-vslfs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: m3-vslfs
description: VSL/MSL M3 Lane B DONE — VSLFS binds the STDKV storage seam (MSL v0.9.0) to VistA's FileMan DBS (UPDATE^DIE / $$GET1^DIQ / FILE^DIE). Re-pinned msl_ref v0.8.0→v0.9.0. Dual-engine GREEN 7/7 (vehu YDB + foia-t12 IRIS) over #8989.51: create/get byte-identical, exists, kill (FDA .01="@"), DIERR→,U-VSL-FS-DIERR, $ECODE. 3 boundaries green; ICR notional (DBS marker).
metadata:
type: project
---

# VSL T-M3 Lane B — VSLFS (FileMan DBS storage adapter), 2026-06-16

The VistA side of the M3 storage seam (S1): `VSLFS` binds the portable MSL
`STDKV` seam (MSL **v0.9.0**) to VistA's FileMan Database Server (DBS) API.
Branch `m3-vslfs` **stacked on `m2-vslio`** (NOT off `main` — m2-vslio is
unmerged and carries the Makefile `--routines $(SRC)` test fix + the
icr-registry/pin tooling VSLFS needs; branching off main would regress VSLIO).
Merge order: m2-vslio → m3-vslfs. Third `VSL*` module (after VSLCFG, VSLIO).

## Re-pin (boundary ①)
`make pin` after hand-setting `dist/msl-seam-pin.json` `msl_ref` v0.8.0→**v0.9.0**:
syncs the `seams` block from `git show v0.9.0:dist/seam-snapshot.json` → now
carries **STDENV + STDNET + STDKV** (STDKV = 4 verbs). `check-msl-pin` green.

## The adapter — FileMan DBS binding ONLY (4 verbs, same signature as STDKV)
`$$set^VSLFS(file,iens,field,value)`→resolved IENS · `$$get(file,iens,field,
default)` · `$$exists(file,iens)`→1/0 · `$$kill(file,iens)`→1 · `$$lastError()`.
- **set** = `UPDATE^DIE("","FDA","IEN","ERR")` (handles both `"+1,"` add and
in-place file); returns the resolved IENS (for an add, the new IEN from
`IEN(n)` via `resolveIens`). Pragmatic adapter return (like VSLIO's device
handle), not STDKV's bool — FileMan create must surface the new IEN.
- **get/exists** = `$$GET1^DIQ(file,iens,field,"","","ERR")`. A DIERR on a read
is NOT an error — `$$get` returns the default, `$$exists` returns 0 (the STDKV
"absent → default" semantics). exists probes `.01`.
- **kill** = delete via an FDA **`.01="@"`** through `FILE^DIE("","FDA","ERR")`.
**KEY corpus finding: there is NO `DELETE^DIE`** — the Supported DBS delete is
filing `.01="@"`; `^DIK`/direct global KILL are forbidden (Classic/non-DBS).

## Error contract — loud (kickoff decision 4)
A DIERR on a **write** maps to a clean **`,U-VSL-FS-DIERR,`** `$ECODE` (via
`raiseDierr`), with the composed DIERR TEXT in `^TMP($job,"vslfs","err")` for
`$$lastError`. Every DBS call passes an explicit **MSG_ROOT `"ERR"`** so errors
land in the adapter's own array, never the shared `^TMP("DIERR",$J)`. `kill` is
idempotent (records a DIERR, still returns 1).

## Test file — an EXISTING low-risk file (no DD install; that's the deferred v-pkg track)
**#8989.51 PARAMETER DEFINITION**: `.01` (NAME) is free-text with **NO other
required fields** (verified live via a DD probe — `^DD(8989.51,*)`), so a
throwaway **ZZ-namespaced** record (`"ZZVSLFS "_$job_<tag>`) is created and
deleted cleanly through the DBS API. The NAME is uppercase, so the round-trip
value is **transform-invariant** → byte-identical set→get over real FileMan.
Present on both engines.

## Acceptance — dual-engine GREEN 7/7 (the exit criterion)
3 tests / 7 assertions on **BOTH** `vehu`(YDB) and `foia-t12`(IRIS): create→get
byte-identical, exists→kill→exists-false→get-default, and DIERR-is-loud
(`$$set` into bogus file 99999999 raises `U-VSL-FS-...`, `$$lastError` carries
the FileMan text). **No `$ZVERSION` arm** — FileMan DBS is VistA-portable
(kickoff decision 5 confirmed). Full v-stdlib suite on vehu **22/22** (VSLCFG 3 +
VSLFS 7 + VSLIO 10 + smoke 2) — no regression. Recipe: `m test --engine ydb
--docker vehu --chset m --routines src --routines <m-stdlib>/src tests/VSLFSTST.m`
(IRIS: `--engine iris --docker foia-t12 --namespace VISTA`). Driver stack only.

## ICR is NOTIONAL — never a blocker (gate change shipped here)
The FileMan DBS API has **no ICR number in the gold corpus** (custodian DI, doc
`DI/fm22_2dg`) — and per the user directive the DBIA registry is a notional,
human-curated FORUM list, not enforced programmatically. So `tools/gen-icr.py`
now accepts a **notional marker** `@icr DBS` (NOTIONAL_MARKERS) in place of a
number; the gate's real invariants stay (`@status Supported` + no-direct-global).
Each VSLFS call: `; doc: @icr DBS @call <c> @status Supported @custodian DI
@source DI/fm22_2dg#<anchor>`. See shared memory [[notional-dbia-not-a-blocker]]
+ plan §5.4. Do NOT re-raise the missing-number as a gap.

## Gates (all green)
`make check-fast`: fmt/lint (**0 findings** — restructured `stashDierr`'s `$order`
loops to the `for quit do` house idiom to clear M-MOD-009 4-commands/line) +
`m arch check .` (layer v) + check-seams (0 — VSLFS is a consumer) + **check-icr
(8: VSLCFG #2263×2 + VSLIO #2118×2 + VSLFS DBS×4)** + **check-citations (8 vs
gold corpus — DI/fm22_2dg anchors verified)** + check-namespaces (3 VSL routines)
+ **check-msl-pin (v0.9.0)** + check-engine-access. No KIDS/VSLBLD (that's M5).

## Owed / next
- **M2 tail** (parallel, unblocked): STDNET IRIS leg + tier-3 TLS.
- **DD-install enabler** (deferred v-pkg track): teach `v pkg` the FileMan
FILE-DD component, then re-test VSLFS with a throwaway *dedicated* file
instead of #8989.51. The "DD install" the M3 milestone bundles.
- **Next: M4** (VSLSEC + VSLLOG — security + audit seams, §12.2). VSLLOG reuses
this FileMan-DBS binding (S3) for the audit-file sink.
Companion to [[m2-vslio]] (the Lane-B adapter rhythm) + the m-stdlib leaf
`m3-stdkv-storage-seam`.
124 changes: 124 additions & 0 deletions src/VSLFS.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
VSLFS ; v-stdlib — VistA FileMan storage adapter (FileMan DBS record store).
; m-lint: disable-file=M-MOD-024
; M-MOD-024 false positives: the analyser reads the FileMan DBS I/O arrays
; (FDA / IEN / ERR, written by the called DBS routine by-reference) as
; locals-before-def; they are the documented GETS/UPDATE/FILE convention.
; Same suppression as VSLIO/STDNET.
;
; Binds the MSL storage seam (STDKV, S1) to VistA's FileMan Database Server
; (DBS) API: a record store addressed by (file, iens, field). It exposes the
; same four-verb signature as STDKV — $$set/$$get/$$exists/$$kill — backed by
; FileMan DBS calls, never direct global access (architecture §3.2). The
; adapter contains ONLY the VistA binding; any non-FileMan logic stays in the
; MSL seam, called up (m/v waterline §9 no-duplication).
;
; Public API (the handle is a FileMan IENS; values are field values):
; $$set^VSLFS(file,iens,field,value) — file a field (UPDATE^DIE); add a
; record with iens "+1," -> resolved IENS
; $$get^VSLFS(file,iens,field,default)— read a field ($$GET1^DIQ), else default
; $$exists^VSLFS(file,iens) — 1 iff the record exists
; $$kill^VSLFS(file,iens) — delete the record (FILE^DIE, .01="@")
; $$lastError^VSLFS() — last FileMan DIERR detail, else ""
;
; *** ERROR CONTRACT — loud, never a silent wrong value ***
; A FileMan DIERR on a write maps to a clean ,U-VSL-FS-DIERR, $ECODE, with the
; DIERR text composed into ^TMP($job,"vslfs","err") for $$lastError. Reads of
; an absent record/field are NOT errors — $$get returns the default and
; $$exists returns 0 (the STDKV "absent -> default" semantics). Every DBS call
; passes an explicit MSG_ROOT ("ERR") so errors land in this adapter's own
; array, never the shared ^TMP("DIERR",$J).
;
; ICR note: the FileMan DBS API is the public DBS programmer API (FileMan
; Developer's Guide, custodian DI). The DBIA/ICR *number* is notional — a
; manually-curated FORUM list, not enforced programmatically — so each call is
; tagged `@icr DBS` (the notional marker), with a real @status/@custodian/
; @source. See docs/memory notional-dbia-not-a-blocker + plan §5.4.
;
quit
;
; ---------- the storage seam, bound to FileMan DBS (4 verbs) ----------
;
set(file,iens,field,value) ; File `value` into (file,iens,field); return the resolved IENS, else raise.
; doc: @param file numeric FileMan file number
; doc: @param iens string IENS; "+1," (etc.) adds a new record
; doc: @param field string field number within the file
; doc: @param value string external value to file
; doc: @returns string the resolved IENS on success (the new IENS for an add)
; doc: @raises U-VSL-FS-DIERR a FileMan DIERR (detail in $$lastError)
; doc: @icr DBS @call UPDATE^DIE @status Supported @custodian DI @source DI/fm22_2dg#updatedie-updater
new FDA,IEN,ERR
set FDA(file,iens,field)=value
do UPDATE^DIE("","FDA","IEN","ERR")
if $data(ERR("DIERR")) do raiseDierr("set",.ERR) quit ""
quit $$resolveIens(iens,.IEN)
;
get(file,iens,field,default) ; Read (file,iens,field) via $$GET1^DIQ; return value, else `default`.
; doc: @param file numeric FileMan file number
; doc: @param iens string IENS of the record
; doc: @param field string field number
; doc: @param default string value returned when the field/record is unset
; doc: @returns string the external field value, or `default`
; doc: @icr DBS @call $$GET1^DIQ @status Supported @custodian DI @source DI/fm22_2dg#get1diq-data-retriever-single-field
new val,ERR
set val=$$GET1^DIQ(file,iens,field,"","","ERR")
if $data(ERR("DIERR")) quit default
quit $select(val="":default,1:val)
;
exists(file,iens) ; Return 1 iff record (file,iens) exists (its .01 reads without a DIERR).
; doc: @param file numeric FileMan file number
; doc: @param iens string IENS of the record
; doc: @returns bool 1 iff the record exists; 0 otherwise
; doc: @icr DBS @call $$GET1^DIQ @status Supported @custodian DI @source DI/fm22_2dg#get1diq-data-retriever-single-field
new val,ERR
set val=$$GET1^DIQ(file,iens,".01","","","ERR")
if $data(ERR("DIERR")) quit 0
quit $select(val="":0,1:1)
;
kill(file,iens) ; Delete record (file,iens) via an FDA .01="@" through FILE^DIE; return 1.
; doc: @param file numeric FileMan file number
; doc: @param iens string IENS of the record to delete
; doc: @returns bool 1 (idempotent — a DIERR is recorded, not raised)
; doc: @icr DBS @call FILE^DIE @status Supported @custodian DI @source DI/fm22_2dg#filedie-filer
new FDA,ERR
set FDA(file,iens,".01")="@"
do FILE^DIE("","FDA","ERR")
if $data(ERR("DIERR")) do stashDierr("kill",.ERR)
quit 1
;
lastError() ; The last VSLFS error message (the composed FileMan DIERR detail).
; doc: @returns string ^TMP($job,"vslfs","err"), or "" if none
quit $get(^TMP($job,"vslfs","err"))
;
; ---------- internals ----------
;
raiseDierr(who,ERR) ; Stash the DIERR detail, then raise the clean ,U-VSL-FS-DIERR,.
do stashDierr(who,.ERR)
set $ecode=",U-VSL-FS-DIERR,"
quit
;
stashDierr(who,ERR) ; Compose the FileMan DIERR text into ^TMP($job,"vslfs","err").
new m,nl,seq
set nl=$char(10)
set m=who_": FileMan DIERR ("_$get(ERR("DIERR"))_")"
set seq=$order(ERR("DIERR",""))
for quit:seq="" do
. do:seq=+seq addText(seq,.ERR,.m,nl)
. set seq=$order(ERR("DIERR",seq))
set ^TMP($job,"vslfs","err")=m
quit
;
addText(seq,ERR,m,nl) ; Append every TEXT line of DIERR `seq` to `m` (by ref).
new ln
set ln=$order(ERR("DIERR",seq,"TEXT",""))
for quit:ln="" do
. set m=m_nl_$get(ERR("DIERR",seq,"TEXT",ln))
. set ln=$order(ERR("DIERR",seq,"TEXT",ln))
quit
;
resolveIens(iens,IEN) ; Resolve a "+n," add-node IENS to its real IENS; else echo iens.
; UPDATE^DIE returns the new internal entry number for a "+n," placeholder in
; IEN(n); a non-placeholder IENS files in place and is returned unchanged.
new n
if $extract(iens,1)'="+" quit iens
set n=+$piece($extract(iens,2,$length(iens)),",")
quit $get(IEN(n))_","
Loading
Loading