vms-8d4: SHOW CLUSTER reflects real SCS membership (INV-DCL facade-kill) - #422
Merged
Merged
Conversation
cmd_show_cluster() hardcoded "%SYSTEM-I-NOTMEMBER" unconditionally, so a
genuinely-clustered node lied about being standalone even though src/vmsscs/
implements the full SCS stack (datalink, NISCA VC, connection manager, MSCP
serve, the vms-694 rejoin epic). The membership state lived only inside the
SCSD process's in-memory peer table with no way for the separate DCL process
to read it.
Implement the local IPC docs/design-cluster-node.md §3.1 already specifies
("SCSD exposes a local IPC ... to userspace consumers (SHOW CLUSTER, mount)"),
in its simplest honest form: SCSD publishes its live member set to a well-known
file, consumers read it. The file IS the daemon's real state (Rule 9 / INV-6),
not a per-process fake — when the daemon is down or this node has not joined,
the file is absent and NOTMEMBER is the honest answer, exactly as a real
non-member VMS node reports.
- src/vmsscs/include/scs_membership.h: header-only (all static inline, so no
cross-image symbol vector wiring) publish/read/clear IPC. File byte format is
an OVMX invention and labeled as such (Rule 8); resolves via
$OVMX_CLUSTER_STATE_PATH, else <$OVMX_SYSGEN_PATH>.members, else a default.
- src/vmsscs/scsd.c: scsd_publish_membership() writes the live view (~1 Hz off
the main loop) — local node plus every peer whose VMS$VAXcluster connection
reached OPEN (vaxcluster_open_reached) or that received the coordinator's
post-commit membership burst (membership_bursts). Cleared on departure/exit.
- src/vmsdcl/dcl_cmd_show.c: cmd_show_cluster() reads the published view and
renders the OpenVMS SHOW CLUSTER default report (NODE/SOFTWARE/STATUS banner,
Rule 8 — modeled on public docs); NOTMEMBER only when genuinely not a member.
Software version shown only for the local node (peer software is unknown).
- src/libvms/syssvc/sys_misc.c: SIBLING facade-kill — F$GETSYI CLUSTER_MEMBER
and CLUSTER_NODES now read the same live membership instead of the static
SYSGEN VAXCLUSTER flag / a hardcoded 1, keeping them consistent with SHOW
CLUSTER.
Tests (run foreground, pass):
- tests/vmsscs/test_scs_membership.c: publish/read/clear round-trip at the real
IPC boundary (not mocking the function under test).
- tests/integration/test_show_cluster_membership.sh: RUNS DCL against a
membership file in SCSD's own publish format — member set shown when present,
NOTMEMBER when absent/empty, unknown-name peer keyed on SCSSYSTEMID.
Full fidelity against a live 2-node cluster requires lab-2 (SCSD actually
publishing on a real join); this proves the wiring and the honest split.
SHOW CLUSTER/CONTINUOUS (SCACP live-updating TUI) remains a snapshot — filed
as a follow-up, not a membership lie.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e vector entry The native-link graph (link_native_graph, alpine-musl) has its own include-path lists and its own DECC$SHR symbol vector, independent of CMake's target_include_directories. My SHOW CLUSTER / F$GETSYI change slipped past the glibc Debug build (which reported OVMX_LINK_NATIVE off) and reddened 5 native- link jobs. Reproduced in the alpine:3.20 x86_64-musl container via `cmake --build --target link_native_graph`: 1. Compile error: mk_libvms_shr.sh (sys_misc.c) and mk_dcl.sh (dcl_cmd_show.c) could not find scs_membership.h -> added -I src/vmsscs/include to both. 2. Link error: %LINK-F-ERROR, unresolved external symbol 'strftime' -- SHOW CLUSTER's date banner is the first native-linked consumer of strftime, which was not in DECC$SHR's .vms$sv symbol vector -> appended strftime=PROCEDURE to mk_decc_shr.sh (append-only). All other RTL calls the change makes (fopen/fgets/fclose/sscanf/rename/remove/getpid/time/localtime_r/toupper/ memset/strncpy/snprintf/...) were already exported. No new OVMX cross-image symbol and no new DCL TU were introduced (the IPC is header-only static inline), so the mk_dcl.sh TUS / run_dcl_native.sh NOBJ enumerations are unaffected. Verified: the full x86_64 native-link graph links clean -- 5 libs + DECC$SHR + DCL.EXE + LOGINOUT.EXE = 9 artifacts, all EM_X86_64, zero DT_NEEDED. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
run_dcl_native.sh (the "DCL.EXE VMS-native Link + Activate" job) compiles the DCL TUs itself with its OWN include list before calling mk_dcl.sh, so it needs -I src/vmsscs/include for dcl_cmd_show.c's scs_membership.h include too. The NOBJ==23 guard is unchanged (no new TU added). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
#445) Bump OVMX_PRODUCT_VERSION V0.4 → V0.4-1. 24 PRs / 32 commits since V0.4, packed across dimensions (point release toward the 0.5 milestone): self-host (R7) #409 lib$tparse · #411 CLI$ compiled-CLD · #413 sys$setddir #414 lib$get_foreign · #415 sys$filescan (RTL foundation) #418 parse_tables.mar→C (spine #2) · #429 LIBRARIAN.EXE+.OLB (spine #3) #435 shareable-vector freeze (GSMATCH stability) authenticity #421 veracity rubric (Q1 oracle-source/Q2 real-inject) · #424 30 oracle-pinned constants #433 rmsdef.h 74 fabricated RMS codes → oracle UX/DCL/RMS #422 SHOW CLUSTER real membership · #441 DCL per-@-level local scope #442 RMS XAB dates → VMS 1858-epoch quadword networking #419 virtio NIC (user-mode default + opt-in tap/bridge) docs #423 clustering release train + swept: other threads' merged work on main since V0.4 Clustering config-authoring UX (vms-098) + its public-manual grounding gate remain 0.5 (minor) deliverables — not triggered by this point cut. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
cmd_show_cluster()(src/vmsdcl/dcl_cmd_show.c) hardcoded%SYSTEM-I-NOTMEMBERunconditionally — so a genuinely-clustered node lied about being standalone, even thoughsrc/vmsscs/implements the full SCS stack (datalink, NISCA VC, connection manager, MSCP serve, the vms-694 rejoin epic). The live membership lived only inside the SCSD process's in-memory peer table, unreachable by the separate DCL process. This is the top INV-DCL facade.Fix — the real query path
Implements the local IPC that
docs/design-cluster-node.md§3.1 already specifies ("SCSD exposes a local IPC ... to userspace consumers (SHOW CLUSTER, mount)"), in its simplest honest form: SCSD publishes its live member set to a well-known file; consumers read it. The file IS the daemon's real state (Rule 9 / INV-6) — not a per-process fake. When the daemon is down or this node has not joined, the file is absent andNOTMEMBERis the honest answer, exactly as a real non-member VMS node reports.src/vmsscs/include/scs_membership.h— header-only (allstatic inline, so no shareable-image symbol-vector wiring; sidesteps the native-link cascade). Byte format is an OVMX invention, labeled as such (Rule 8). Path resolves via$OVMX_CLUSTER_STATE_PATH, else<$OVMX_SYSGEN_PATH>.members, else a default.src/vmsscs/scsd.c—scsd_publish_membership()writes the live view (~1 Hz off the main loop): local node plus every peer whose VMS$VAXcluster connection reached OPEN (vaxcluster_open_reached) or that received the coordinator's post-commit membership burst (membership_bursts). Cleared on departure/exit.src/vmsdcl/dcl_cmd_show.c— reads the published view, renders the OpenVMS SHOW CLUSTER default report (NODE/SOFTWARE/STATUS banner, Rule 8 — modeled on public docs). Software version shown only for the local node (peer software is genuinely unknown, so not claimed).Sibling audit
F\$GETSYI("CLUSTER_MEMBER")/CLUSTER_NODES(src/libvms/syssvc/sys_misc.c) — shared the facade (read the static SYSGENVAXCLUSTERflag / hardcoded1). Fixed in-scope: both now read the same live membership, consistent with SHOW CLUSTER.SHOW CLUSTER/CONTINUOUS(SCACP live-updating TUI) — now shows the real snapshot rather than lying, but the continuous live-refresh screen is a larger piece of work. Filed as a follow-up, not a membership lie.Tests (run foreground, all pass)
tests/vmsscs/test_scs_membership.c— publish/read/clear round-trip at the real IPC boundary (does not mock the function under test): absent → NOTMEMBER; 2-node set round-trips; unknown-name peer keyed on SCSSYSTEMID; clear → NOTMEMBER.tests/integration/test_show_cluster_membership.sh— RUNS DCL against a membership file written in SCSD's own publish format (the real daemon↔DCL boundary): member set shown when present, NOTMEMBER when absent/empty, unknown-name peer shown by SCSSYSTEMID.vmsscs_scsd_wire_unitand all 27 fastscs-labelled tests green;runtime_target_gate+ negctl green.Requires lab-2 to fully verify: an end-to-end proof that SCSD actually publishes the file on a real 2-node join (this PR proves the wiring and the honest standalone/member split without a live cluster).
🤖 Generated with Claude Code