Skip to content

authenticity: INV-1 system-identity SSOT + logical-driven login banners (vms-e652) - #1

Merged
baron-3dl merged 6 commits into
mainfrom
worktree-authenticity-inv1
Jul 29, 2026
Merged

baron-3dl merged 6 commits into
mainfrom
worktree-authenticity-inv1

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

Keystone of the authenticity parity program (vms-898). Unblocks A1 (SHOW SYSTEM real process table) and A4 (login fidelity).

What was wrong

The tree carried four different hardcoded versions at once:

Version Where
V7.3 DCL banner, SHOW SYSTEM, F$GETSYI, LOGINOUT, sshd, MONITOR, AUTHORIZE
V1.0 STARTUP.COM, SHOW LICENSE, PRODUCT SHOW
V0.1 sys$getsyi SYI$_VERSION, TCP/IP banner

sys$getsyi answered V0.1 while F$GETSYI answered V7.3 — two different answers to the same question.

Identity SSOT

src/libvms/include/ovmx_identity.h implements the D1 dual identity:

  • Human surfaces → OVMX product version, badged OVMX V0.1 - OpenVMS-compatible (INV-0 trademark ceiling).
  • Machine surfaces (F$GETSYI VERSION / SYI$_VERSION) → true-to-arch VMS-compat token.
  • Iron rule — never lie to the metal. ARM has no VMS lineage, so OVMX-on-ARM reports its own version rather than a fabricated VSI one.

Header-only by necessity: consumers span the static PID-1 init, three different tool link sets, and DCL.

Login banners come from logicals

Operator correction mid-implementation: on VMS the banner is not compiled into LOGINOUT. SYS$ANNOUNCE (pre-prompt) and SYS$WELCOME (post-auth) are logicals a manager defines at boot; @file displays a file's contents for multi-line banners; both ship undefined so LOGINOUT falls back to its built-in.

This is the bigger tell. A greybeard types DEFINE/SYSTEM SYS$WELCOME "..." in the first ten minutes — a hardcoded printf() swallows it silently, which is worse than a wrong version because the verb appears to work.

Standing gate

tests/integration/test_identity_ssot.sh fails the build on any version literal outside the SSOT, and on any regression of the banner back to a hardcoded printf. Both failure modes verified by negative control — a gate that cannot fail is worthless.

The gate earned its keep immediately: it found five hardcoded versions my manual grep missed, including the sys$getsyi one.

Tests

37/37 ctest pass (baseline 35). Added a unit test (32 assertions) and the gate. Updated the three existing tests that asserted the old hardcoded values, plus the QEMU boot-banner assertions — none skipped or weakened.

Known gap (filed separately)

Logical-name tables are per-process today: lnm_get_manager() builds an in-process table and nothing but the daemon reads SYLOGICALS.CONF. So a boot-time or DEFINE/SYSTEM definition does not yet propagate to other processes. The banner is correctly wired to the logical, so it starts working the moment system-wide logicals do.

Also open: purity sign-off on the V9.2-3 patch level and the SYI$_VERSION field padding width (family V9.2-x is operator-ruled under D1).

🤖 Generated with Claude Code

baron-3dl and others added 6 commits July 28, 2026 20:58
…rs (vms-e652)

One module now owns system identity (src/libvms/include/ovmx_identity.h) and
every surface reads it. Implements the D1 dual identity: an OVMX product
version shown to humans, badged "OpenVMS-compatible" per INV-0, and a
true-to-arch VMS-compat token for machine interop -- with the iron rule that
OVMX never fabricates a VMS arch (ARM answers with its own version).

The tree carried FOUR different hardcoded versions at once: V7.3 (DCL banner,
SHOW SYSTEM, F$GETSYI, LOGINOUT, sshd, MONITOR, AUTHORIZE), V1.0 (STARTUP.COM,
SHOW LICENSE, PRODUCT SHOW), V0.1 (sys$getsyi SYI$_VERSION, TCP/IP banner),
plus a separate compat constant. sys$getsyi answered "V0.1" while F$GETSYI
answered "V7.3" -- two different answers to the same question.

Login banners: on VMS these come from the SYS$ANNOUNCE / SYS$WELCOME logicals
a manager defines at boot, not a compiled-in printf. Adds ovmx_banner.h
implementing VMS semantics (string, or "@file" to display a file's contents),
shipping both undefined as VMS does so LOGINOUT falls back to the built-in.
LOGINOUT and the SSH daemon now resolve the logical; SYLOGICALS.CONF documents
both; STARTUP.COM no longer bakes in a banner version.

INV-1 is a standing gate, not a one-time cleanup: tests/integration/
test_identity_ssot.sh fails the build on any version literal outside the SSOT
and on any regression of the banner back to a hardcoded printf. Both failure
modes verified by negative control.

Tests: 37/37 ctest pass (baseline 35; +1 unit test with 32 assertions, +1
gate). Updated the three existing tests that asserted the old hardcoded
values, plus the QEMU boot-banner assertions.

Known gap filed separately: logical-name tables are per-process today, so a
boot-time or DEFINE/SYSTEM definition does not yet reach other processes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
git add -A in the INV-1 commit swept the local CMake build tree into the
branch. Remove it from the index and widen the ignore to build-*/ so
build-local/, build-ci8/ and build-246gate/ cannot be committed again.

No source changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t infrastructure

The per-process logical-name gap was filed under vms-898 but described as a
'known gap (filed separately)', which read as external infrastructure. It is
not: logicals are how a VMS system is configured, so a facility that looks
implemented and silently isn't is the epic's own thesis (uneven depth, not
absence) and squarely INV-6 territory.

Records the empirical two-process demonstration that DEFINE/SYSTEM does not
propagate, notes that the in-process facility is otherwise deep (four tables,
hierarchical search, /TABLE=, DEASSIGN/SYSTEM) so it should not be rebuilt,
and states that A4 cannot honestly close on the banner work alone.

rd: vms-d37 re-scoped and now blocks vms-46b (A4).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ause

Chasing the per-process logical-name finding exposed a single root cause under
a large fraction of the authenticity board, which the per-surface tell table
could not see because it lists symptoms.

OVMX has no executive. It is N independent Linux processes each privately
simulating a whole VMS system. The dividing line is mechanical: file-backed
state (queue, SYSGEN, SYSUAF, accounting, known images) is genuinely shared
and accidentally correct; memory-resident state (logical names, process table,
event flags, mailboxes, device table) is per-process fiction that reports
success.

Evidence is empirical, not inferred: two-process logical test; no shm_open or
MAP_SHARED anywhere in vmsprocess; SHOW SYSTEM/SHOW USERS permanently report
one process; event flags live in the PCB so common EFCs are inexpressible;
$CREMBX is a socketpair named in LNM$PROCESS_TABLE so the canonical VMS IPC
pattern cannot work at all.

Consequence: A1, A4, SHOW USERS, B1, B6/B7 and broadcast are written as
display work but are blocked on shared state. Fixing them surface-by-surface
produces better-looking LARP.

Also re-tiers SHOW DEVICE: it prints the host Linux mount table (volume labels
are mount-point basenames, including the kernel version string) — an INV-4
leak on a first-two-minutes command, currently filed as an M2 depth item. The
roadmap's own rule ranks by tell-probability, so it is M1.

rd: vms-6b8 (executive gap epic, blocks vms-853/d37/c17/905), vms-b9f
(SHOW DEVICE leak), vms-c17 (SPAWN stub).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correcting an overstatement in the previous commit. vms.ko already implements
a real VMS executive: access modes/privileges (0x01-0x04), ASTs (0x10-0x12),
event flags INCLUDING VMS common clusters (0x20-0x27, vms_common_ef_lock), and
the lock manager (0x30+). My claim that common event flag clusters were 'not
expressible' was false — they are implemented; userspace event_flags.c simply
never calls them and uses the PCB instead.

So vms-6b8 is three distinct problems, not one:
  1. UNWIRED  - event flags, ASTs, privileges: kernel code exists, ignored.
                Only sys_lock.c reaches /dev/vms via vms_kif. Cheapest win.
  2. ABSENT   - process table, logical names, device table, mailboxes.
  3. NO DOCKER - containers have no /dev/vms, and Docker is where CI runs.

(3) is the actual ruling needed, not 'kernel or not'. sys_lock.c already set
the precedent and wrote the policy down (Docker gets SS$_NOSUCHDEV, 'by
design'); generalizing it gates every executive-dependent authenticity item
behind the QEMU harness. Alternative is one API with two backends, where INV-6
requires the degraded backend to declare itself rather than silently succeed.

Also notes that LNM sits on the hot path of every file open, so
ioctl-per-translation deserves separate consideration from the other
structures.

rd: vms-6b8 scope corrected, design gate raised for the operator ruling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Operator ruling 2026-07-28. The Docker RUNTIME layer is dead; this records it
where it cannot be forgotten and mechanizes it.

I had carried 'Docker mode = glibc dev/CI convenience' as a remembered fact and
re-asserted it — including raising a design gate premised on it — instead of
re-deriving from current state. The ruling existed only in prose and did not
hold. Hence a gate.

- CLAUDE.md Project-Specific Rule 9 (hard invariant): OVMX has exactly one
  runtime, the real-kernel/QEMU path, with vms.ko as the executive via
  /dev/vms. Draws the load-bearing distinction the earlier item missed:
  Docker as a RUNTIME target is forbidden; Docker as BUILD/TEST tooling
  (Dockerfile.bootable, src/kernel, tests/qemu) is fine and expected — those
  produce and test the real runtime.
- Never add a silent userspace fallback for an executive facility. If
  /dev/vms is absent, fail honestly (SS$_NOSUCHDEV, as sys_lock.c does).
- tests/integration/test_runtime_target.sh: standing gate, registered in
  ctest, both failure modes verified by negative control. Written so that
  DELETING Dockerfile/docker-compose.yml is the success state, not a gate edit.
- Root Dockerfile + docker-compose.yml marked DEAD LEGACY in-file. They are
  NOT deleted yet: ci.yml's docker-build job feeds several 'needs:' jobs
  including uat-session, so deleting today takes CI red. Tracked by vms-71a.
- Quick Reference no longer advertises 'docker compose up' as a way to run OVMX.

Roadmap §2.1.1 corrected: the two-backend seam I proposed was premised on the
stale Docker assumption. Executive is kernel-mode, full stop; the per-process
userspace tables are legacy to be replaced, not fallbacks.

Surfaces the real blocker: NO CI job loads vms.ko (persistent-boot runs its
QEMU script inside a Docker container; there is no kernel-module job). That is
the mechanical CAUSE of the executive gap — untestable facilities defaulted to
per-process fakes that report success. The architecture drifted to fit the test
harness. vms-e4d (QEMU CI job loading vms.ko) is therefore the first step of
the retrofit, before any executive code.

rd: vms-6b8 gate resolved; vms-e4d + vms-71a filed; vms-90b retention question
answered. 38/38 ctest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Jul 29, 2026
…rt (vms-6b8)

Ran the five-pass swarm decomposition. Pass 0 rigor: HEAVY — blast heavy
(kernel + libvms + vmsprocess + vmsdcl + CI, >20 files, >3 packages),
adversarial heavy floor (privileges/access modes are a security surface),
+1 coverage modifier because no CI job loads vms.ko so every touched path is
uncovered. 24 items.

COLD-START HAZARD FOUND AND GATED. Every item referenced artifacts that do not
exist on main — verified with git cat-file: docs/design-executive-retrofit.md
(PR #2), tests/integration/test_runtime_target.sh (PR #1), CLAUDE.md Rule 9
(PR #1), and the roadmap sec 2.1 analysis (PR #1). An agent dispatched cold
would be told to obey Rule 9, find no Rule 9, and re-derive the exact
conclusion this epic exists to correct. Added vms-pre, a human prerequisite
(merge PR #1 + PR #2) that blocks all three DAG entry points. Belt and braces:
every item now also carries its constraints inline, so items remain executable
even if the docs are missing.

Also fixed placeholder item IDs in the dispatch notes (vms-LNM1 etc. never
existed) which would have sent a cold agent chasing dead references.

Added the heavy-rigor layer: concurrent veracity adversaries per wave (vms-vx1,
vms-vx2) written against this project's actual failure history — they attack
the specific cheats available here (stubbing /dev/vms, simulating two processes
with two threads, inheritance-based mailbox passing). Plus reviewers, a
dedicated security review of the privilege surface, five sweeps, and an e2e
that proves the facilities COMPOSE, which is where the original design failed.

Downstream consumers annotated SUBSTRATE-BLOCKED (vms-853, vms-46b, vms-c17,
vms-905) so a cold agent does not mistake them for display work and build a
better-looking fake.

DAG verified sealed: only vms-pre is ready.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@baron-3dl
baron-3dl marked this pull request as ready for review July 29, 2026 02:42
@baron-3dl
baron-3dl merged commit 2061424 into main Jul 29, 2026
33 checks passed
baron-3dl added a commit that referenced this pull request Jul 29, 2026
* docs: executive retrofit dispatch plan (vms-6b8)

Decomposes the executive gap into a dispatch-ready, outcome-scoped tree.

Sequencing is driven by testability, not by the design ruling. No CI job
currently loads vms.ko (persistent-boot runs its QEMU script inside a Docker
container; there is no kernel-module job), so the kernel executive is
unprovable in CI. That is the mechanical cause of the gap: untestable
facilities defaulted to per-process userspace fakes that report success. The
architecture drifted to fit the test harness. Phase 0 is therefore a hard
barrier.

  Phase 0  vms-e4d  QEMU CI job loads vms.ko                        [QA]
  Phase 1  vms-ef1 / vms-as1 / vms-pv1  wire the ioctls that
           ALREADY exist in vms.ko (event flags incl. common
           clusters, ASTs, privileges) — wiring, not design  [Systems]
  Phase 2  vms-71a  migrate Docker CI jobs, delete Dockerfile       [QA]
  Phase 3  vms-pt1 process table, vms-ln0 LNM design ruling ->
           vms-d37 LNM, vms-dv1 devices, vms-mb1 mailboxes    [Systems]
  Phase 4  vms-fk1  delete the fakes; gate forbids their return

Dependencies wired so exactly three items are dispatchable now: vms-e4d
(the barrier), vms-ln0 (the one real design fork left — LNM sits on the hot
path of every file open, so ioctl-per-translation is not automatically
right), and vms-b9f (SHOW DEVICE host-mount-table leak, independent of the
executive entirely).

Standing constraints restated per item: one runtime target (Rule 9), never a
silent userspace fallback (fail honestly with SS$_NOSUCHDEV), not done until
proven against a real /dev/vms, clean-room, purity sign-off on VMS values.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* plan: swarm-plan the executive retrofit + harden the DAG for cold start (vms-6b8)

Ran the five-pass swarm decomposition. Pass 0 rigor: HEAVY — blast heavy
(kernel + libvms + vmsprocess + vmsdcl + CI, >20 files, >3 packages),
adversarial heavy floor (privileges/access modes are a security surface),
+1 coverage modifier because no CI job loads vms.ko so every touched path is
uncovered. 24 items.

COLD-START HAZARD FOUND AND GATED. Every item referenced artifacts that do not
exist on main — verified with git cat-file: docs/design-executive-retrofit.md
(PR #2), tests/integration/test_runtime_target.sh (PR #1), CLAUDE.md Rule 9
(PR #1), and the roadmap sec 2.1 analysis (PR #1). An agent dispatched cold
would be told to obey Rule 9, find no Rule 9, and re-derive the exact
conclusion this epic exists to correct. Added vms-pre, a human prerequisite
(merge PR #1 + PR #2) that blocks all three DAG entry points. Belt and braces:
every item now also carries its constraints inline, so items remain executable
even if the docs are missing.

Also fixed placeholder item IDs in the dispatch notes (vms-LNM1 etc. never
existed) which would have sent a cold agent chasing dead references.

Added the heavy-rigor layer: concurrent veracity adversaries per wave (vms-vx1,
vms-vx2) written against this project's actual failure history — they attack
the specific cheats available here (stubbing /dev/vms, simulating two processes
with two threads, inheritance-based mailbox passing). Plus reviewers, a
dedicated security review of the privilege surface, five sweeps, and an e2e
that proves the facilities COMPOSE, which is where the original design failed.

Downstream consumers annotated SUBSTRATE-BLOCKED (vms-853, vms-46b, vms-c17,
vms-905) so a cold agent does not mistake them for display work and build a
better-looking fake.

DAG verified sealed: only vms-pre is ready.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
baron-3dl pushed a commit that referenced this pull request Aug 3, 2026
…eliberately EXCLUDED

Operator authorised the documentation hunt. Published sources only.

PROVENANCE EXCLUSION recorded first and prominently: the highest-detail
source found (VAXcluster Disk I/O Internals Manual, DEC Mar 1988, on
bitsavers) is EXCLUDED and must stay excluded -- every page is footed
'Confidential and Proprietary' and its symbol index cross-references SCS
SOURCE LISTINGS. That is outside Rule 8. Nothing from it is used. Excluding
it costs nothing since its flow-control content restates the clean DTJ
passage. A confidential source read once cannot be un-read, and the legal
protection on this whole effort depends on that discipline.

Findings:
1. CREDIT CANNOT cause a receiver-side discard of a SEQUENCED message --
   DTJ No.5 p.26: message-credit controls are instituted at the SENDING
   node; silent credit discard is documented only for datagrams.
   Independently confirms 4M.22's kill, from documentation this time.
2. A NAMED discard counter exists and we have never read it: SHOW PORT/VC
   publishes 'Illegal Seq Msg' and seven sibling buckets (VSI Cluster
   Systems App.F Ex.F.2 p.365). New tool tools/portwatch.sh; run dispatched.
3. The published model says a removed node is EXPECTED TO REBOOT (DTJ No.5
   p.17; RECNXINTERVAL/CLUEXIT App.C.7.1 p.314). Flagged clearly as
   inference that no source keys admission on INCARNATION -- and sec 4 and
   sec 3.11 already spent the obvious version.
4. Sequence window: PARTIAL. Duplicates discarded, cumulative ack. NOT FOUND
   whether acceptance requires exactly last+1.
5. Connection states: NOT FOUND, now exhaustively. con_sent/disc_sent/
   con_pend/disc_pend are undefined in every public source.
6. Useful null: IDSM V5.2 has NO SCS chapter. Do not repeat that search.
7. Still worth getting: Roy G. Davis, VAXcluster Principles (Digital Press
   1993) -- a published book, borrowable on archive.org with a login. The
   #1 remaining documentation move; needs an operator account.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 7, 2026
… real DCL.EXE/LOGINOUT.EXE via LINK.EXE (#155)

* vms-206: LINK.EXE emits real x86_64 crt0 + cross-image CALL PLT stubs

emit_shareable() in src/vmslink/link.c had two pieces hardcoded to
AArch64 machine code regardless of g_out_machine: the synthesized crt0
entry stub for a main()-based --executable, and the cross-image CALL
PLT stub + import-CALL detection (gated on R_AARCH64_CALL26/JUMP26
only, so R_X86_64_PLT32 references to a producer universal never
routed through the import table). An x86_64 main()-based program, or
any x86_64 image with a cross-image CALL import, linked to garbage --
exactly DCL.EXE's shape, blocking vms-cb5f.

Adds, additively per g_out_machine (mirrors vms-8f5's e_machine gate):
  - is_call also covers R_X86_64_PLT32, so a PLT32 reference to an
    undefined-locally, --use-producer-exported symbol becomes an
    import exactly like aarch64's CALL26/JUMP26.
  - the cross-image-call reloc-apply branch gets an x86_64 case: a
    PC32-style S+A-P write targeting the PLT stub instead of the
    (absent) callee.
  - the PLT stub emitter gets an x86_64 case: `jmp *disp32(%rip)`
    (FF 25 imm32) through the import-GOT cell -- the one-instruction
    analogue of aarch64's adrp/ldr/br page+lo12 GOT load+branch.
  - the crt0 emitter gets a real x86_64 stub: mov rdi,[rsp] / lea
    rsi,[rsp+8] / lea rdx,[rsi+rdi*8+8] recovers argc/argv/envp per
    the SysV process-entry stack layout, `call main` then `mov
    edi,eax ; call exit` tails the return value into exit(). Encoding
    verified against `as`'s AT&T disassembly byte-for-byte.

New test (src/imgact/test/run_multiobj_exec_x86_64.sh, wired into CI
as multiobj-exec-x86_64): links a two-object main() program against a
hand-written producer shareable via LINK.EXE --executable --use,
activates it by executing the image directly (real kernel PT_INTERP
-> IMGACT.EXE, native x86_64, no emulation), and checks the process
really ran main(), read a real argc/argv off the stack (two runs, two
different exit codes), made a real cross-image CALL through the new
PLT stub into the producer, and exited via a cross-image exit() call.
Verified this test fails with 'unresolved external symbol' before the
fix (confirmed via stash) and passes after.

Regression: aarch64 MVP (run_test.sh), aarch64 crt0/PLT
(run_multiobj_exec.sh) and x86_64 simple-reloc (run_test_x86_64.sh)
harnesses all stay green. Full ctest suite: 106/107 pass; the one
failure (facility_attribution_negctl) is pre-existing and unrelated
(reproduces identically with this change stashed out -- host/container
site-derivation drift in kernel/vms_lock.c attribution, nothing to do
with vmslink).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-e5d: LINK.EXE resolves x86_64 GOTPCRELX (type 41), grounded against real musl/libgcc

is_got_reloc() only recognized GOTPCREL (9) and REX_GOTPCRELX (42); plain
GOTPCRELX (41, gas's non-REX relaxable GOT-load variant) hit patch_pcrel's
default die("unsupported .text relocation"). Adds it as a third case
alongside its already-handled siblings in is_got_reloc()/patch_got() --
same flat-disp32-write codegen (S(got)+A-P, addend -4), since LINK.EXE
performs no GOT-load-to-LEA relaxation for any of the three.

Grounded empirically, not from psABI text alone:
- The system's prebuilt musl libc.a carries ZERO GOTPCRELX/REX_GOTPCRELX
  (non-PIC static build) -- confirming the gap check needed a real PIC
  object set, not just this host's default libc.a.
- A `-fPIC -fno-plt` probe (`extern void f(void); void g(void){f();}`)
  reproduces the exact instruction shape: `jmp *sym@GOTPCREL(%rip)` (ff 25
  disp32, no REX prefix -- near indirect call/jmp defaults to 64-bit
  operand size without REX.W), matching readelf's R_X86_64_GOTPCRELX.
- Alpine x86_64 libgcc.a (the exact toolchain vms-cb5f's DECC$SHR build
  uses) carries 1521 real GOTPCRELX occurrences -- e.g. `call *abort@
  GOTPCREL(%rip)` in _absvdi2.o -- confirming the gap is in libgcc.a
  (GCC's runtime calling abort/etc. through the GOT under -fno-plt), not
  libc.a itself, and closely matches vms-cb5f's reported 1449 (after its
  TLS-subsystem filter narrows the count).

Proof (done condition):
- Pre-fix LINK.EXE reproducibly dies with "%LINK-F-ERROR, unsupported
  .text relocation" on a hand-built intra-image GOTPCRELX call
  (caller.o -> callee.o, both defined, forcing the GOT slot to resolve
  internally rather than deferring as an import).
- Post-fix LINK.EXE links it; a new REAL-LOAD harness
  (src/vmslink/test/gotpcrelx_activate.c) mmaps the shareable at a
  genuine non-zero ASLR'd base, applies the .vms$rel load-bias fixup
  IMGACT would perform, and calls in -- proving the GOT cell resolves to
  the correct address under a real load, not a readelf/byte check.
  Wired into run_test_x86_64.sh as a permanent regression case.
- Whole-archiving the real Alpine x86_64 musl libc.a + libgcc.a (TLS
  subsystem filtered per vms-cb5f's precedent, an unrelated gap) with
  pre-fix LINK.EXE reproduces the exact die(); post-fix it gets past
  that point and (with --allow-undefined for legitimately deferred
  externals) succeeds end-to-end: 1586 objects, 146 GOT slots, 9610
  relocs, EM_X86_64 -- confirming this specific gap is cleared at the
  scale vms-cb5f hit it.

Base: work/vms-b93-integration (vms-206 merged). Blocks vms-cb5f.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-cb5f: parameterize the x86_64 DCL.EXE VMS-native proof harness (ARCH=x86_64)

Reproduces vms-b65.6's aarch64 DCL.EXE-through-IMGACT proof for x86_64, per
vms-bdf's own done-condition (all four reloc/crt0/PLT beads merged):

- run_dcl_native.sh / lib_build_graph.sh: ARCH env var (default aarch64,
  unchanged) selects the one target-specific codegen flag each producer
  needs (-mno-outline-atomics on aarch64 vs -mtls-dialect=gnu2 on x86_64,
  the standing precedent from docs/design-link-x86_64-relocs.md) and the
  libvmssys arch/<ARCH>/syscall.S. Shared by run_login_native.sh unaffected
  (ARCH unset -> identical aarch64 defaults, verified against a real arm64
  musl container).
- mk_{vmsprocess,vmslnm,vmsfs,libvms,vmsrms}_shr.sh / mk_dcl.sh: CFLAGS is
  now env-overridable (${CFLAGS:-<same aarch64 default>}) so the x86_64
  caller can supply target-appropriate flags without a forked copy of each
  recipe.
- mk_decc_shr.sh: DECC$SHR must stay a non-TLS producer (LINK.EXE's
  one-TLS-object-per-image limit, vms-212 tracks the general fix). aarch64's
  libgcc.a empirically carries zero TLS-defining members; x86_64's whole-
  archives a dead-for-OVMX subsystem (GCC's IEEE 754-2008 decimal-float
  library + -fsplit-stack support) built on the TLSGD general-dynamic model
  LINK.EXE's x86_64 path doesn't implement (OVMX standardizes on gnu2/
  TLSDESC). Filtered architecture-generically by scanning archive members
  for .tdata/.tbss or TLSGD refs, not by hardcoding names -- a no-op on an
  archive with neither.
- .github/workflows/ci.yml: new dcl-native-x86_64 job, native amd64 (no
  QEMU binfmt needed -- the runner already is x86_64), ARCH=x86_64 through
  the same run_dcl_native.sh.

BLOCKED short of a green run: whole-archiving real musl libc.a on x86_64
(1345 objects) hits R_X86_64_GOTPCRELX (type 41), a relocation link.c's
x86_64 path does not recognize (is_got_reloc() only checks GOTPCREL/
REX_GOTPCRELX) -- confirmed empirically (1449 occurrences across ordinary,
load-bearing musl objects, not confinable to dead code the way the TLSGD
subsystem was). link.c is out of this item's file-domain per its own
repeated header comments ("do NOT edit them here"); see the escalation.

Also confirmed (fetched fresh): vms-206 is on work/vms-b93-integration
(16c7965) but NOT yet on main -- a second prerequisite for the executable
(not just shareable) link step once the GOTPCRELX gap is fixed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-cb5f: make DECC$SHR's x86_64 TLS filter transitive, closing the whole-archive gap

Re-dispatch, continuing from work/vms-cb5f now that vms-e5d (GOTPCRELX) is
merged into work/vms-b93-integration. Rebased cleanly onto
origin/work/vms-b93-integration (vms-206 crt0/PLT + vms-e5d GOTPCRELX both
present); the prior CI-job/ARCH-parameterization/CFLAGS work carried over
unchanged.

Whole-archiving real musl libc.a + libgcc.a on x86_64 got past GOTPCRELX and
hit a NEW gap: mk_decc_shr.sh's TLS filter (vms-cb5f's own prior commit)
removes members that directly define/reference TLS storage (bid64_add.o
etc, via TLSGD) but left non-TLS "glue" objects in place --
_addsub_dd.o/_addsub_sd.o/... call INTO the removed decimal-float subsystem
via a plain GOT reference to e.g. __bid64_add, which no longer has a
definer once bid64_add.o is dropped. LINK.EXE's "GOT symbol undefined"
strict die() was CORRECT given the archive it was handed -- the gap was in
the filter only removing the directly-tainted half of a connected dead-code
component, not link.c.

Fixed by making the filter a reference-graph fixed-point closure: after
seeding the direct TLS-tainted set (unchanged), repeatedly pull in any
surviving member whose undefined reference is satisfied ONLY by an already-
removed member, until nothing new is added. One nm pass over the whole
archive up front (not re-invoked per member per iteration) keeps this cheap
on libc.a's 1345 members. Verified as a no-op on aarch64's libgcc.a (366/366
members survive, 0 filtered, byte-identical DECC$SHR before/after) and pulls
in exactly the expected 148/241-surviving decimal-float + split-stack
subsystem on x86_64's.

With that fix, the full six-library producer graph + DCL.EXE (22 objects,
6 GOT, 2 TLS, 1053 ABS64-ptr, 145 imports) now link VMS-native and clean on
x86_64. Activation through IMGACT.EXE segfaults -- escalated (see PR/item
notes), not patched here: link.c/imgact.c are out of this item's file-domain
per the item's own repeated header comments, and the crash is upstream of
mk_decc_shr.sh (isolated: run_multiobj_exec_x86_64.sh's small-scale crt0/PLT
proof -- 3 imports, 1 GOT, 0 TLS -- still passes natively with correct
argc-computed exit codes, so this is scale/shape-specific to DCL.EXE's
import/reloc volume, not a general crt0/PLT regression).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-a66: LINK.EXE was dropping every read-only-section relocation

DCL.EXE segfaulted the moment it was activated on x86_64. The fault PC sat in
the anonymous RWX region IMGACT.EXE maps for DECC$SHR, executing non-instruction
bytes. Single-stepping from musl's printf_core into pop_arg caught the transfer:

    movslq (%rcx,%rsi,4),%rdx     ; rdx = jumptable[1]
    add    %rcx,%rdx              ; rdx = table_base + delta
    jmp    *%rdx

with the table entry ZERO, so the jump landed on the table's own address --
inside .rodata, on the "(null)" string constant.

Root cause: parse_obj() collected relocations only for sections it bucketed
B_TEXT or B_DATA. Every relocation whose target section was B_RODATA was
discarded with no diagnostic. gcc emits each `switch` jump table into a
per-function read-only section as `.long arm - table_base`; the arms are in
.text and the table is not, so the assembler cannot fold the difference and
leaves one real R_X86_64_PC32 per arm. All of them were dropped, so every jump
table in the image came out all zero.

Not a scale bug. Nothing about 145 imports, 6 shareables, 6 GOT slots, 2 TLSDESC
entries or the 6-deep --use chain is involved: vms-206/vms-cd1/vms-2e4 passed
through this because their specimens contained no switch large enough for gcc to
build a table and never called a printf-family function with a conversion. It is
a code SHAPE that first appeared when real musl and the real DCL sources entered
the link -- 902 such relocations in libc.a, 554 in DCL's own objects.

aarch64 was never affected: aarch64 gcc resolves its jump tables at assembly
time, so its only read-only-section relocations are .eh_frame PREL32 (never
executed). Confirmed empirically on both arches.

Fix: collect relocations for B_RODATA as well, via a single bucket_is_patchable()
predicate, and emit %LINK-W-RELSKIP for any RELA section whose target is
allocatable but not flat-placed -- a silent drop is exactly how this survived
four proofs. (That diagnostic immediately surfaces one pre-existing gap:
libgcc's cpuinfo.o .init_array, tracked separately, not executed today.)

Regression gate: src/vmslink/test/run_rodata_reloc_x86_64.sh + its specimen link
a jump-table-bearing image against the real whole-archive musl DECC$SHR, activate
it through a real IMGACT.EXE, and diff the transcript against the SAME source
built by the system toolchain. It asserts the specimen still produces .rela.rodata
(so it cannot rot into a vacuous pass) and fails LOUD -- verified: it dies with
"Illegal instruction" on the pre-fix linker and passes on the fixed one. Wired
into CI as job rodata-reloc-x86_64.

Verified green: dcl-native ARCH=x86_64 (SHOW TIME + A=5, exit 0) and ARCH=aarch64,
run_multiobj_exec_x86_64.sh, src/imgact/test/run_test_x86_64.sh,
src/vmslink/test/run_test_x86_64.sh, run_test_x86_64_tls.sh, run_decc_shr.sh,
run_test.sh + run_multiobj_exec.sh under arm64.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-b6a: wire the VMS-native LINK.EXE shareable graph into CMake (aarch64)

Wires the mk_*_shr.sh / mk_dcl.sh / mk_loginout.sh recipes (the vms-b65/c39
lib-migration chain) into `cmake --build` via a new OVMX_LINK_NATIVE option
that auto-detects on an aarch64 musl toolchain. `cmake -B build && cmake
--build build` now produces LIBVMSSYS$SHR.EXE, DECC$SHR.EXE, LIBVMSPROCESS$SHR.EXE,
LIBVMSLNM$SHR.EXE, LIBVMSFS$SHR.EXE, LIBVMS$SHR.EXE, LIBVMSRMS$SHR.EXE, DCL.EXE
and LOGINOUT.EXE via LINK.EXE -- verified EM_AARCH64 with zero DT_NEEDED
entries on all 9 artifacts.

Additive to (not a replacement of) each library's existing add_library()
target, which host ctest unit tests still link directly; on non-aarch64-musl
toolchains OVMX_LINK_NATIVE stays off with no behavior change.

Extracted the previously-inlined LIBVMSSYS$SHR recipe (duplicated in
lib_build_graph.sh) into mk_vmssys_shr.sh, the one place the mk_*_shr.sh
convention keeps it -- lib_build_graph.sh (run_dcl_native.sh/run_login_native.sh)
now calls it too, closing the exact drift risk mk_libvms_shr.sh's LIST
comment warns about. mk_vmssys_shr.sh exports vms_kif_setident
unconditionally (append-only vector), so run_login_native.sh's SYS_VEC_EXTRA
override is no longer needed. Both harnesses re-verified green end-to-end in
an aarch64 musl container after the refactor.

Added CI job link-native-cmake asserting the readelf ground-source condition
directly. x86_64 extension is vms-6da (separate item, unblocked).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-6da: extend the VMS-native LINK.EXE CMake graph to x86_64

Extends vms-b6a's OVMX_LINK_NATIVE mechanism (auto-detected from the
compiler's -dumpmachine triple) to also recognize an x86_64 musl
toolchain, not just aarch64 -- same CMake target (link_native_graph),
same build_link_native.sh entrypoint, no forked plumbing. ARCH is
threaded through as an env var to every mk_*_shr.sh recipe (CFLAGS
picks -mtls-dialect=gnu2 on x86_64 vs -mno-outline-atomics on aarch64,
the same convention lib_build_graph.sh's build_producer_graph()
established for the raw-harness path in vms-cb5f/vms-a66).

mk_vmssys_shr.sh (extracted by vms-b6a, so it hadn't picked up the
ARCH/CFLAGS env-override convention yet) and mk_loginout.sh (missed by
cb5f/a66) are brought in line with the rest of the mk_*_shr.sh recipes.

Fixed a real bug hit while proving this: `N=$(... | grep -c NEEDED)`
aborts under `set -e` in the CI job's alpine /bin/sh whenever the DT_NEEDED
count is legitimately zero (grep -c exits 1 on no match) -- present in
both the pre-existing aarch64 job and the new x86_64 one; both fixed.

Ground-truth proof, real alpine:3.20 musl containers (arm64 emulated,
amd64 native), `cmake --build . --target link_native_graph`:
  - x86_64:  9/9 artifacts EM_X86_64, zero DT_NEEDED, via LINK.EXE
  - aarch64: 9/9 artifacts EM_AARCH64, zero DT_NEEDED (regression, unchanged)

Adds CI job link-native-cmake-x86_64 alongside link-native-cmake,
mirroring dcl-native-x86_64's amd64-native (no QEMU) approach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-d0f5e: fat initramfs ships DCL.EXE/LOGINOUT.EXE VMS-native via LINK.EXE

Replaces vms-913.6 (cancelled -- DT_HASH/ld-based dynamic ELF was a proven
dead end). distro/Dockerfile.bootable now builds a real alpine:3.20 musl
link-native stage that runs `cmake --build --target link_native_graph`
(OVMX_LINK_NATIVE, vms-b6a/vms-6da) to produce the 7 shareables
(DECC$SHR, LIBVMSSYS$SHR, LIBVMSPROCESS$SHR, LIBVMSLNM$SHR, LIBVMSFS$SHR,
LIBVMS$SHR, LIBVMSRMS$SHR) + DCL.EXE + LOGINOUT.EXE via LINK.EXE, plus
IMGACT.EXE (its own standalone Makefile, x86_64). All 9 artifacts are
ET_DYN with a .vms$sv symbol vector, zero DT_NEEDED/DT_HASH -- ground-
truth readelf assertions are baked into the Docker build itself (both in
the link-native stage and against the actually-shipped DCL.EXE in the fat
initramfs), not just asserted by a separate CI job. STARTUP.EXE and
IMGACT.EXE stay static/freestanding; HELP/AUTHORIZE/MAIL/MONITOR/
INITIALIZE ship static for 0.1 (no mk_*.sh recipe yet, scope decision --
DCL is the flagship dynamic proof, not every utility).

Ground-truth verified locally: full `docker build` succeeds, QEMU x86_64
boot reaches login (tests/uat/vms_session_qemu.sh: SYSTEM and GUEST
sessions authenticate, DCL runs 50+ commands to VMS-correct output,
DIRECTORY SYS$SYSTEM: lists the new DCL.EXE/IMGACT.EXE/LOGINOUT.EXE),
and test_executive_integral.sh's negative controls (NOEXEC/NODEV,
rebuilt from the same fat initramfs) still pass 14/14.

KNOWN REGRESSION, not fixed here (out of this item's file-domain --
distro/Dockerfile.bootable + CMake wiring, not src/vmsdcl or
src/kernel/vmsfs): SPAWN's first invocation in a session now fails
(%DCL-E-CREPRC) where it previously succeeded. Root-caused via an A/B
rebuild swapping only DCL.EXE/LOGINOUT.EXE back to static (54/54 UAT
checks pass) vs the VMS-native pair (52/54, this SPAWN check newly red).
cmd_spawn() (src/vmsdcl/dcl_cmd_process.c) re-execs via
readlink("/proc/self/exe"), which resolves to a path vmsfs reports
"(deleted)" -- confirmed present already at the top of DCL's own main(),
before any DCL code runs, so the deletion happens during kernel PT_INTERP
+ IMGACT activation, not in DCL or LOGINOUT. No unlink()/rename() of
SYSEXE exists in the userspace boot path (checked ovmx_init.c,
vms_login.c, vmsfs_translate.c) -- the leading hypothesis is a vmsfs.ko
dentry-lifecycle interaction exposed by IMGACT's longer activation time,
which needs its own investigation. Login and the item's own done
condition are unaffected; test file left unmodified (weakening an
existing UAT assertion is outside this item's authority).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-00e: vmsfs must not unhash the dentry of a running image (SPAWN fix)

ROOT CAUSE. vmsfs.ko's ->d_revalidate answered "invalid" for EVERY
positive regular-file dentry, unconditionally. That is not revalidation,
it is permanent invalidation: a d_revalidate() of 0 makes the VFS call
d_invalidate(), which UNHASHES the dentry (fs/namei.c lookup_fast() /
lookup_open()); an unhashed non-root dentry satisfies d_unlinked(); and
d_path() renders any d_unlinked() path with a " (deleted)" suffix
(fs/d_path.c path_with_deleted()). /proc/<pid>/exe and /proc/<pid>/fd/<n>
are d_path() readers.

So the FIRST path walk of a running executable that lives on vmsfs made
that program's own /proc/self/exe read ".../DCL.EXE (deleted)" -- with
the file present and unmodified. mm->exe_file pins the dentry, so it
stayed unhashed for the life of the process. DCL's SPAWN re-execs itself
via readlink("/proc/self/exe") (cmd_spawn(), src/vmsdcl/dcl_cmd_process.c),
so it execl()'d a path with " (deleted)" on the end, got ENOENT, and
answered %DCL-E-CREPRC.

That single defect explains BOTH observed shapes, which differ only in
when the second walk happens:
  - static DCL.EXE: nothing re-walks the image during startup, so the
    first SPAWN's own execl() was the second walk -- spawn #1 worked,
    spawn #2 onward failed. (The "SECOND SPAWN fails" defect recorded in
    tests/uat/vms_session_qemu.sh, previously blamed on DCL.)
  - VMS-native, IMGACT-activated DCL.EXE (vms-d0f5e): IMGACT.EXE re-opens
    the image by AT_EXECFN to read its .vms$sv/.vms$imp sections
    (activate_symbol_vector(), src/imgact/imgact.c) BEFORE the image runs,
    so the dentry was already unhashed at the first line of main() and the
    FIRST SPAWN failed. IMGACT did not cause the bug; it reached it one
    walk earlier.

Nothing ever unlinked or renamed anything, which is why the userspace
audit of ovmx_init.c / vms_login.c / vmsfs_translate.c found nothing.

FIX. ->d_revalidate now asks the resolver the question a fresh ->lookup
would ask -- "what does this name resolve to right now?" -- and keeps the
dentry when the answer is unchanged:
  - block-device mode: re-resolve the name to a FID (vmsfs_blkdev_resolve(),
    factored out of vmsfs_blkdev_lookup() with no behaviour change) and
    compare against i_ino, which IS the FID (iget_locked(sb, fid)). Exact
    identity: catches a newer version AND a deletion.
  - overlay mode: compare the current highest version of the base name
    against the version this dentry resolved to (overlay mints a fresh
    inode per lookup, so version is the only stable identity).
  - create intent (LOOKUP_CREATE/LOOKUP_RENAME_TARGET) still returns 0, so
    O_CREAT cannot be satisfied from the cache and VMS still cuts a NEW
    VERSION rather than reopening the current one.
This is also strictly cheaper than the old behaviour, which paid for the
same resolution and then threw away the dentry and the inode anyway.

GROUND SOURCE, BOTH DIRECTIONS, ON THE REAL RUNTIME (Rule 6 -- no module
was loaded on the host; everything below ran under QEMU).

New suite tests/qemu/test_kmod_vmsfs_exepath.c, 28 assertions:
  pre-fix  3 phases red, incl. "child: /proc/self/exe after re-open =
           /mnt/.../CHILD.EXE (deleted)" -- the product symptom reproduced
           at the kernel layer -- then the kernel OOPSES in __fput()
           (NULL d_inode) when the held fd is closed.
  post-fix 28/28 green.
Phase 3 execs this program from a real block-device vmsfs and has the
child do exactly what IMGACT does (re-open its own image by path) and
then what SPAWN does (re-exec via /proc/self/exe). Phase order puts it
first precisely so it is REACHED before the pre-fix oops kills the
process. Phase 2 carries the POSITIVE CONTROL that makes the file
non-vacuous: once PROBE.TXT;2 exists, the fd held on ;1 MUST become
"(deleted)", because the unversioned name no longer names it -- a
d_revalidate that just answers "valid" passes everything else here and
goes red on that one.

tests/uat/vms_session_qemu.sh on the vms-d0f5e VMS-native fat initramfs:
  pre-fix  52/54 (both SPAWN assertions red)
  post-fix 54/54
A SECOND spawn ('SPAWN SHOW TIME') and two assertions on it are added
here, because the long-standing "second SPAWN" defect is fixed by
the same change -- measured, not assumed. With them: 52/56 pre-fix,
56/56 post-fix. The comment block that recorded that defect as a
DCL bug is corrected in place rather than removed.

No regression: full QEMU kernel harness 31/31 suites (incl. the existing
test_kmod_vmsfs and test_kmod_vmsfs_blkdev version-semantics suites),
test_persistent_boot.sh 14/14, test_executive_integral.sh 14/14, Rule 9
runtime-target gate, divider integrity, harness verdict, kif caller
census, identity census, facility manifest selftest+coverage.

tests/qemu/facility_defects.sh: the new suite joins the two existing
vmsfs suites in SCOPE_OUT_SUITES for the reason already stated there --
it never opens /dev/vms, so no executive mutation can turn it red.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-913.7: wire SYSTARTUP_VMS.COM's INSTALL ADD to the real Known Image DB

Checked current state first (per item instructions): vms-p78.3's install
unification and vms-d0f5e's real-file fat initramfs already held, and
test_persistent_boot.sh was passing -- but done condition #4 (SYSTARTUP_VMS.COM
carrying INSTALL ADD for each shareable) was not met, and fixing it surfaced
two real, ground-verified gaps beneath it:

1. DCL's INSTALL builtin (cmd_install) never dispatched to SYS$SYSTEM:INSTALL.EXE
   (src/install/install.c, vms-913.5's KFE-database utility). It reimplemented
   its own flat-text SYS$MANAGER:INSTALL_LIST.DAT that nothing ever read --
   IMGACT.EXE's known-image search (src/imgact/known_images.c) mmaps the binary
   VMS$KNOWN_IMAGES.DAT only INSTALL.EXE writes. Fixed by making cmd_install a
   thin wrapper that re-execs INSTALL.EXE via dcl_exec_utility(), the same
   pattern already used for ANALYZE/MAIL/SYSGEN/SYSMAN in this file -- matching
   install.c's own header comment ("deliberately NOT wired as a DCL builtin").

2. Once wired, real QEMU boot (docker build + test_persistent_boot.sh) caught
   that INSTALL.EXE was never copied into the fat initramfs, so every
   SYSTARTUP_VMS.COM INSTALL ADD failed with %INSTALL-F-NOIMG on live boot --
   and that failure silently aborted the rest of SYSTARTUP_VMS.COM despite
   SET NOON, dropping the "site startup ran" banner test_persistent_boot.sh
   checks for (14/14 -> 12/14). Fixed by adding INSTALL.EXE to
   Dockerfile.bootable's fat-initramfs SYSEXE copy list, alongside the other
   build-static utilities.

SYSTARTUP_VMS.COM now INSTALL ADDs exactly the 7 shareables the fat initramfs
actually ships (DECC$SHR, LIBVMSSYS$SHR, LIBVMS$SHR, LIBVMSPROCESS$SHR,
LIBVMSLNM$SHR, LIBVMSFS$SHR, LIBVMSRMS$SHR -- Dockerfile.bootable's own "9
VMS-native LINK.EXE artifacts" gate). LIBVMSQUEUE$SHR is deliberately excluded:
it builds via the ordinary CMake add_library() graph, not the VMS-native
LINK.EXE graph, and is not shipped in the fat initramfs -- INSTALLing it would
FILNOTFND on every boot.

Verified live: docker build -f distro/Dockerfile.bootable + test_persistent_boot.sh,
14/14 checks pass across both boots, with all 7 INSTALL-I-ADDED lines visible
each time (idempotent re-add on reboot, matching real VMS SYSTARTUP_VMS.COM
practice).

New test: tests/dcl/test_install_command.sh proves through real vmsdcl (not
around it) that INSTALL ADD/LIST/REMOVE write and clear the actual KFE binary
database (magic-byte check), not just matching text output -- the old stub
could print the same %INSTALL-I-ADDED text without touching the file IMGACT.EXE
reads.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-913.11: verify x86_64 boot-to-login is already proven, fix stale CI comments

Live-verified on this worktree's native x86_64 host: docker build
distro/Dockerfile.bootable + tests/uat/vms_session_qemu.sh boots real
QEMU (qemu-system-x86_64, no emulation) through IMGACT.EXE's x86_64
relocation path (RELATIVE/GLOB_DAT/JUMP_SLOT/TLSDESC) to a DCL login
prompt, drives a full scripted session, 56/56 checks passed. This is
already the uat-session CI job (Job 7), unconditional on every push
since GH runners are x86_64 natively.

Job 8/9 comments still said "aarch64-only until bead vms-913.11" as if
x86_64 support were still pending; it has its own native-runner
counterpart (Job 9b, imgact-x86_64) and the uat-session boot-to-login
proof. Corrected the comments to point at the now-complete state
instead of a stale forward reference.

Done condition satisfied by existing work (vms-8f5, vms-cd1, vms-2e4,
vms-a66, vms-00e, vms-d0f5e chain); no functional code change needed.

* vms-fbc: shipped-image ground-source gate now covers LOGINOUT.EXE too, not just DCL.EXE

vms-c39's done condition (STARTUP execs VMS-native LOGINOUT.EXE, which
authenticates against SYSUAF and execs VMS-native DCL.EXE, zero ld/ld.so)
was structurally unverifiable in the Docker CI container -- no /dev/vms
there. This item's job is proving the SUCCESSFUL leg under a real kernel.

Verified live, not assumed: that proof already exists and is repeatable.
- src/ovmx_init/ovmx_init.c execl()s SYS$SYSTEM:LOGINOUT.EXE.
- tools/vms_login.c (LOGINOUT) authenticates against SYSUAF
  (sysuaf_authenticate), then execl()s DCL.EXE --login.
- src/vmslink/link.c sets PT_INTERP=IMGACT.EXE on every LINK.EXE
  executable image -- ground truth that no ld.so is anywhere in this
  chain, not an inference.
- distro/Dockerfile.bootable's fat initramfs ships exactly one DCL.EXE
  and one LOGINOUT.EXE, both copied only from the VMS-native
  /link-native build (no static fallback exists for either anymore).
- tests/uat/vms_session_qemu.sh runs this exact chain under real QEMU
  in CI (job uat-session, .github/workflows/ci.yml), and vms-00e's own
  commit records a fresh 56/56 pass on this initramfs today.

The one real gap: the Dockerfile's own ground-source readelf gate (does
the SHIPPED image -- the actual bytes cp'd into the initramfs, not a
copy two directories away -- carry zero DT_NEEDED/DT_HASH) only checked
DCL.EXE. LOGINOUT.EXE is the FIRST VMS-native image in the login chain
and had no equivalent check on its shipped bytes; a regression that
silently reintroduced an ld-linked LOGINOUT.EXE ahead of DCL.EXE would
not have been caught by this gate (the generic 9-artifact loop in the
earlier link-native stage checks a build-output copy, not what actually
ships). Fixed by widening the existing gate to loop over both images.

Ground-truth verified locally: `docker build -f distro/Dockerfile.bootable
--target builder` -- real build, not mocked -- prints
"OK: shipped DCL.EXE is VMS-native (EM_X86_64, zero DT_NEEDED/DT_HASH)"
and "OK: shipped LOGINOUT.EXE is VMS-native (EM_X86_64, zero
DT_NEEDED/DT_HASH)" for the actual artifacts this build produced. The
new for-loop's shell logic was also unit-checked standalone: fails
correctly against a real dynamically-linked ELF (DT_NEEDED present) and
passes correctly against a statically-linked one, before spending a
build cycle on it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-913.10: prove slim-boot login+DCL activates from disk, not initramfs

The slim initramfs (STARTUP.EXE-only) and boot.sh --slim wiring already
existed (Dockerfile.bootable, boot.sh) but nothing exercised the boot
path: test_persistent_boot.sh's two boots both used the FAT initramfs.

Extends that harness with:
  - a static check that the slim initramfs cpio listing carries no
    DCL.EXE/LOGINOUT.EXE/IMGACT.EXE/SYSLIB (bootstrap-only, as designed)
  - Boot 3: boots the SAME installed disk with the SLIM initramfs, logs
    in as SYSTEM/MANAGER over the QEMU serial console (real SHA256-backed
    SYSUAF credentials, same as tests/uat/vms_session_qemu.sh), and runs
    SHOW TIME to a real DCL prompt

Since the slim initramfs structurally ships none of LOGINOUT.EXE,
IMGACT.EXE, DCL.EXE, or the SYSLIB shareables, a real login reaching a
working DCL prompt is functional proof they resolve from the mounted
system disk's SYS$SYSTEM:/SYS$LIBRARY:, not the initramfs. Measured
against a real QEMU boot: 25/25 checks pass.

Also adds `cpio` to the runner image's apt install list -- needed by the
new static check, absent from the base ubuntu:24.04 image (verified).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-0c9: docs/install-0.1.md -- 0.1 install/boot/login walkthrough

Light release-eng doc scoped to 0.1 (download/build the fat-initramfs
image, first-boot install, reboot into the slim initramfs, log in to
DCL). Cluster admin / license audit / trademark review stay under
vms-d5b R6 for 1.0.

Ground-sourced: a real `docker build -f distro/Dockerfile.bootable -t
ovmx-boot .` was run on this checkout and reached the builder stage
before this shared host's disk filled and the build was aborted for
safety. Every command and every piece of documented console output
(the %STARTUP-I-* banners, %OVMX-I-EXEC, Username:/Password: prompts,
Welcome to OVMX, SHOW TIME) is instead quoted verbatim from GitHub
Actions run 31128513528 (commit 8560fa7), where the "Persistent Boot
Smoke Test" and "VMS User Acceptance Test" CI jobs build and boot this
same image and passed 14/14 + all UAT assertions -- the proven,
passing path tests/qemu/test_persistent_boot.sh and
tests/uat/vms_session_qemu.sh already exercise on every push. Login
credential (SYSTEM/MANAGER) confirmed against the real hash in
distro/rootfs/.../SYSUAF.DAT, not invented.

No git tag created -- that step is reserved for the operator's final
sign-off.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-ade8: fix overclaimed CI citation in install-0.1.md ground-sourcing note

Run 31128513528 predates the slim-boot work (checked-out
test_persistent_boot.sh has zero slim references) and its overall
status was FAILURE (Build & Test + attribution negative-control jobs
red); only citing the two individually-passing jobs overclaimed
coverage. Rescope the note to state what was actually verified: this
swarm's own local docker build + real QEMU boots against the merged
tree (25/25 checks) for Section 3, and the UAT script's own run for
the DCL session content. No CI run number is cited as covering
slim-boot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* vms-9bc: regenerate docs/design-link-x86_64-relocs.md, stale after vms-913.7

Re-ran tools/survey_x86_64_relocs.sh to refresh the empirical R_X86_64_PC32/
PLT32 counts in src/libvms/descrip.c, no methodology change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 7, 2026
…x (10 items) (#158)

* vms-c9c: negative-control diagnostic prints the condition, not an inferred cause

rc!=77 (and rc!=0) for test_syssvc_* only means "77 was not reached" -- it
has two distinct causes (a fabricated success, or an unrelated assertion
failure), and the old message asserted the first as fact. Proven false on
PR #46 (run 30725753152): both fabricated-success assertions passed, the
real defect was DCL.EXE crashing. Now the message states the ambiguity and
pastes the suite's own FAIL line(s) so the reader attributes from evidence
already in the same output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-86a: shard the per-facility negctl job to fit CI under concurrent load

Root cause (measured, rd vms-86a trail): the job ran all 58 manifest
defects sequentially in ONE job -- 27m solo, 50m under moderate CI load,
>60m and timeout-killed with three PRs in flight. Raising timeout-minutes
was rejected (flaky-test rule): it hides the margin problem, it doesn't
fix it.

Splits the per-facility loop into 6 independent matrix shard jobs, each
running ~1/6 of `facility_defects.sh list` (partitioned by NR%6, so it
tracks the manifest as it grows/shrinks -- never a hand-maintained
sublist). Each shard still runs the SAME positive control and the SAME
per-defect equality check (red set EXACTLY require_fail+knock_on_fail,
attribution, blind-suite gaps) the single job did, just over a subset.

A new aggregate job (keeping the ORIGINAL job name for branch-protection/
doc continuity) unions every shard's emitted execution record and runs
the full-manifest comparison against the committed
tests/qemu/facility_negctl_observed.tsv in both directions -- the exact
check the single-job driver ran on a full run, just over the union
instead of one sequential execution.

Verified: the 6-way NR%6 partition covers the manifest exactly (58/58,
no gaps, no dupes); reconstructing the union from the real committed
record and running fnr_compare against it passes; dropping one shard's
rows from the union is correctly caught as a mismatch. Both existing
static selftests (facility_defects.sh selftest, facility_record_negctl.sh)
still pass unmodified. actionlint clean except pre-existing style-level
shellcheck notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-b3b: key the facility-negctl red-set equality on (suite, text), not text alone

run_facility_negctl.sh's per-defect equality (check 6) compared bare
assertion text against require_fail/knock_on_fail, discarding the suite
attribution fail_map() already carries (`cut -f2-`). MEASURED: the text
"child: a LOCAL flag set by the parent is NOT visible here (local clusters
stay per-process)" -- named by bind-client-no-register, expected from
test_syssvc_ef_mproc.c (in that defect's suites_red) -- is also printed
verbatim by test_kmod_eflag_mproc.c, which is NOT in suites_red. Under the
old equality, a red from either suite satisfied the requirement, so a red
from the wrong suite could mask the right suite's own red going missing.

Fix: tests/qemu/facility_negctl_equality.sh's fne_scope_map() scopes the
observed (suite, text) rows to the defect's suites_red glob (or "(harness)")
before the text comparison runs, so a same-text red from an out-of-scope
suite can no longer stand in for the suite the manifest actually named.

Swept the whole manifest at the same normalisation facility_defects.sh's own
selftest uses: every require_fail/knock_on_fail text in every defect is
still found within its own suites_red-scoped sources except this one already
measured case -- the fix does not narrow any other defect's requirement.

tests/qemu/facility_negctl_equality_negctl.sh is the negative control (no
QEMU needed): it pins the real collision as still-grounded, proves a red from
the right suite still satisfies the requirement, and proves a same-text red
from the wrong suite (test_kmod_eflag_mproc) no longer does -- reproducing
the driver's own comparison shape end to end. Registered as ctest
facility_negctl_equality (label "harness", no container/QEMU).

* vms-41b: root rule's header clause requires a NON-static declaration

The census credited any function prototyped in a header the build compiles
as a root (rule 2, "exported API surface"), because the P-record reading
never carried the static/extern qualifier. MEASURED exploit: a dead helper
declared AND defined `static` in a multi-includer header (dcl_cmd.h,
included by 9 TUs) bought a root exactly like the earlier two-edit recipes
this gate already closed -- a `static` declaration can never be an exported
entry point, since each includer gets its own private symbol.

Fix: call_edges() now tags each P record static|extern, and root rule 2
only seeds from non-static declarations in non-TU files. Verified by hand
against the prior recipe (now rc=1, naming vms_kif_chkpriv) and against the
pristine tree (unchanged, rc=0). Added negative control 48 to pin it; all
42 controls in test_kif_caller_census_negctl.sh pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-d33: close the header-residency root-rule loophole in the kif census

Rule 2 of the census's call graph grants a root to "every product function
prototyped in a header the build compiles" -- correct for a genuinely
exported symbol, but the (origin file, name) tagging that tells a `.c`
translation unit's private static from an extern definition only fired
when the origin was itself one of the compiled TUs. A header never is, so
a `static` function whose declaration AND body both lived directly in a
compiled header (e.g. src/vmsdcl/include/dcl/dcl_cmd.h) fell through
untagged and landed on the same bare-name node an actually-exported
symbol gets -- granting root status, and therefore a product path, to a
function with internal linkage that could never be called from outside
its own translation unit.

MEASURED before the fix: two edits (a static declaration+body in
dcl_cmd.h, plus retiring vms_kif_chkpriv's OVMX-UNWIRED token) bought
rc=0 at 44/32/12, one extra root (731 -> 732). Fixed by tracking
header-resident static definitions independent of the per-TU tagging and
excluding them from rule 2's grant. Pristine tree unaffected (731 roots,
1547 reached, 31/44 unchanged) because the loophole requires a function
that additionally carries a standalone forward declaration -- a shape no
existing static-inline header helper in the tree has. The same two-edit
recipe is now rc=1, naming vms_kif_chkpriv.

Captured as negative control 48 in test_kif_caller_census_negctl.sh (42
passed, 0 failed, no regressions across all pre-existing controls).

This closes one purely-static loophole in vms-d33's "product path, not
execution" question -- it does not close vms-d33 itself. A genuinely
extern function declared in a header and defined in one .c file is still
a root whether or not it is ever called at runtime, and is still
indistinguishable here from a real caller nobody exercises -- that gap is
execution, not linkage, and needs the per-assertion runtime-attribution
instrument's groundwork (docs/design-runtime-attribution.md, residual R7)
before it can close. Documented as a disclosed residual, not claimed
closed, in both the gate's header comment and the design doc addendum.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-cb5: $SETUAI's SYSPRV test comes from the executive, not the caller's own PCB

Round 5 of the Phase 3 security review, against origin/main c871334.

sys$setuai -- the one service that rewrites SYSUAF.DAT, UAI$_PWD included --
guarded itself with:

    struct vms_pcb *pcb = vms_pcb_get();
    if (pcb && !(pcb->cur_privs & PRV$M_SYSPRV)) return SS$_NOPRIV;

Two ways through. vms_pcb_get() returns NULL for a process that never called
vms_pcb_init(), so `pcb &&` made the condition false and NO privilege test
ran at all. And where a PCB did exist the mask was pcb->cur_privs, which
sys$setprv writes for the calling process with no validation -- the caller's
own claim about itself.

The test now reads the row the executive holds for the process
(vms_kif_getjpi_self), the same source tools/vms_authorize.c uses since
vms-b2e, and refuses when that read does not come back (Rule 9: no
absent-executive branch).

Also fixes the rewrite's UIC write-back base. parse_uaf_line() reads the two
UIC fields with strtoul(..., 8) after vms-e60; this fprintf still printed them
with %u, so rewriting any record whose UIC digits differ between the bases
changed that account's UIC. USER1 ships 200|202 and would have been written
128|130.

tests/qemu/test_syssvc_setuai.c drives all of it against a real /dev/vms:
a caller with no PCB, a caller with an authenticated non-SYSPRV identity, a
caller whose own PCB claims SYSPRV over an executive row that does not, and
the SYSPRV positive that keeps the three refusals from being blanket. The
SYSUAF.DAT evidence is read by the parent -- a process that neither
authenticated nor wrote.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-38c: re-measure runtime attribution post vms-2b2, fix stale anchors

vms-2b2 closed: all 16 wired vms_ioctl_* handlers are now MEASURED (25/33
total, 8 UNPROBED = the OVMX-UNWIRED exempt set). Re-measuring the register
against that finds 8 of 10 OVMX-EXECUTIVE claims MEASURED, exactly 2 still
UNMEASURED: sys$readef and sys$setef. Root cause is NOT unprobed handlers --
vms_ioctl_readef/setef are each measured-dependent elsewhere (test_syssvc_ef_
local, test_kmod_eflag, test_kmod_bind) -- it's a suite-scope mismatch: the
defects that mutate their WASSET/WASCLR status word never redden an assertion
in test_syssvc_ef_mproc.c, the suite cited as these two claims' own proof.

Fixes landed:
- test_userspace_service_register.sh: the UNMEASURED branch now distinguishes
  "handler measured elsewhere, suite mismatch" from "handler unprobed
  anywhere" instead of always citing the now-closed vms-2b2 as the reason.
  The stale "2 of 10 measured" / "9 of 33 handlers" comment block is replaced
  with a re-derivable description instead of a count that will drift again.
- facility_attribution.sh selftest checks 3 & 5 hardcoded vms_ioctl_wflor as
  a "known unprobed" anchor. vms-2b2's own follow-up (vms-2ed) later gave it
  real coverage in that exact suite, which silently broke the selftest (a
  stale hardcoded fact, the same mistake class this file argues against).
  Now derives the anchor from `handlers` output each run.
- facility_attribution_negctl.sh control B hit the same staleness (the
  recorded 2-edit sys$wflor buy no longer represents an unpaid claim, since
  wflor is now honestly measured in that suite). Control B now detects that
  organic graduation and falls through to a fresh, currently-live equivalent:
  one ignored call added to sys$readef's own already-declared EXECUTIVE proof
  does not flip its standing UNMEASURED to MEASURED -- the adversarial round
  this item required, run against current data instead of a resolved case.

Not enforced: a pristine tree would still red 2 of 10 claims, so the register
stays report-only per the item's done-condition. Verified: register (rc=0),
register negctl (49/49), facility_attribution selftest (6/6), facility_
attribution_negctl (8/8), facility_defects selftest, facility_record_negctl
(25/25).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-05e7: close the composed rename+shared-.inc census exfiltration

The census's third definition reading (vms-e2b) namespaced its unrestricted
region read to `vms_kif_` names, to tell an exfiltrated interface wrapper
apart from vms_syscall.h's 47 static inline syscall stubs. That name filter
left an escape: exfiltrate a wrapper's body into a .inc shared with a second
product TU (defeating the private-origin rule) AND rename it out of the
vms_kif_ namespace (defeating the name filter). MEASURED before this change:
7 edits, universe 44->43, rc=0, PASS -- a silent shrink.

Fix: call_edges() now tracks the `inline` keyword alongside `static` and
tags a defs-mode static definition "static-inline" when both are present.
A new fourth definition-reading term reads the interface TU's full region
with no name filter, excluding only "static-inline" definitions -- the
tell that separates vms_syscall.h's generic stubs (all `static inline`,
verified) from a real wrapper's body (plain `static`, verified against
vms_kif.c's own kif_bind/kif_call/etc.). The recipe now reds naming the
renamed entry point instead of silently leaving the universe.

Added negative control 48 reproducing the full 7-edit recipe; all 42
existing controls stay green; pristine tree rc=0, universe unchanged at 44.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-cb5: test_syssvc_setuai bootstraps the device table before resolving SYSUAF

SYSUAF_PATH is a VMS filespec; vmsfs_to_linux_path() cannot resolve it until
the system device is in this process's device table, which is what every
shipped image does at startup. Without it the suite failed on a missing file
instead of on $SETUAI's privilege test, so its refusals would have been
explained by the wrong thing. The resolved path is printed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-cb5: negative control setuai-sysprv-caller-declared, and the suite's logical-name bootstrap

VMS_SYSUAF_PATH is "SYS$SYSTEM:SYSUAF.DAT", so resolving it needs the
logical name table as well as the device table. MEASURED before this: the
path resolved to /vms/sysuaf.dat and the suite failed on a missing file
instead of on $SETUAI's privilege test.

The control deletes the mask test and nothing else -- the state $SETUAI was
in for every caller with no PCB. It names the three refusals in require_fail
and the file-unchanged check in knock_on_fail, with the reason.

facility_defects.sh selftest PASS; coverage PASS (59 defects >= floor 58, all
anchored, 27 suites named).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-e90: teach divider-integrity detector to recognize table separator rows

src/vmsscs/include/scs_mscp_srv.h:601 is a legitimate markdown-style
comment table ("header offset | size | field" / "---|------|---...") added
by vms-4e31 (ddce7ec) to document the SCA block-transfer header layout, not
a corrupted divider. The FUSED_RE detector was matching the dash run and
flagging the rest of the row as fused-onto-line text.

Fix by shape, not by allowlist: a tail made up of nothing but '|' and
divider characters (a table separator row's remaining cells) is exempted,
the same way a '*/' comment closer already is. Real fusion -- prose from
the next comment line -- still trips the check immediately, per new test
test_still_flags_fusion_immediately_after_a_table_style_run.

Proof suite: 14/14 pass (was 12; added the table-row true-negative and a
paired true-positive). Full-tree gate sweep: 777 files, 0 findings.

* vms-cb5: $GETUAI/$SETUAI stop losing every empty SYSUAF field

Found by the new suite, not by reading: test_syssvc_setuai read USER1's row
back out of SYSUAF.DAT after a $SETUAI and got uic_group=202, uic_member=0
where 200 and 202 belong.

parse_uaf_line() split the row with seven strtok_r(buf, "|") calls. strtok
treats a RUN of delimiters as ONE, so every empty field was dropped and every
field after it read one position early. Five of the six shipped rows have an
empty field, so $GETUAI answered the wrong hash, the wrong UIC and the wrong
privileges for those accounts, and $SETUAI wrote the misparse back.

  USER1||200|202|SYS$SYSDEVICE:[USERS.USER1]||TMPMBX,NETMBX
  -> password_hash="200", uic_group=202, uic_member=strtoul(defdir,8)=0

uic_member 0 is why this is more than a parsing bug: tools/vms_login.c does
setuid(rec->uic_member), and setuid(0) is not a drop. What stops that on the
shipped SYSUAF is that all four accounts this misparse gives member 0 carry
no password hash and cannot authenticate (vms-08f) -- not anything here.

The replacement split is the one src/libvms/rtl/sysuaf.c's sysuaf_scan()
already uses, so the two readers of this file now agree by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vms-ed8: close register-universe residuals #1-3 disclosed by vms-c19

Closes three of the five gaps test_userspace_service_register.sh disclosed
(not claimed closed) after vms-c19:

1. A target declared under tests/, holding its own sys$ definition in a
   source under tests/, that is nevertheless installed. The tests/ exclusion
   only asked "is the compiling target declared outside tests/ AND does it
   compile a non-tests/ source"; a target failing both halves but shipped by
   install(TARGETS ...) was never asked about. Fixed by scanning every
   CMakeLists.txt for install(TARGETS ...) and treating a named target as a
   product target regardless of which directory declared it.

2. A source CMake compiles only under an option this configure leaves OFF,
   living outside src/ and tools/ (inside those two the glob still catches
   it, e.g. src/imgact/ under OVMX_IMGACT=OFF). Fixed with a mechanical scan
   (register_optguard.awk) for add_subdirectory() calls gated by an OFF
   option that resolve outside src/+tools/; the gate now REFUSES rather than
   silently certifying a hole, naming the option and path.

3. compile_commands.json was parsed by line shape with no defense against a
   PARTIAL parse (a "file" field that never reaches a matching object close
   would have silently dropped that entry). The parser is pulled out into
   tests/integration/lib/register_buildset.awk, which now counts "file"
   fields seen vs. objects closed and refuses on a mismatch instead of
   certifying a shrunk set.

Each fix is measured before/after against the real gate on a sandboxed tree:
the pre-fix gate PASSes while missing the minted service; the post-fix gate
reds naming exactly it. register_buildset.awk's partial-parse path is also
unit-tested directly against a hand-built malformed compile_commands.json,
since no product-source mutation can perturb cmake's own JSON shape.

Pristine tree: rc=0, universe unchanged at 88 services. Negative controls
added to test_userspace_service_register_negctl.sh for all three; gate
header's "WHAT REMAINS OPEN" disclosure updated to drop the two closed
bullets (deleted, not reworded, per the standing prose ruling).

Residuals #4 (assembly aliasing) and #5 (shared broken-build-set message
prefix) are out of scope for this item and remain open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 10, 2026
…3, no bash (vms-62b) (#255)

Close the Build-native 1.0 self-hosting fixpoint (vms-678 gate): OVMX builds a
nontrivial OVMX component -- the OVMX linker LINK.EXE itself -- from WITHIN OVMX,
driven from a DCL session with ZERO bash / no host tool in the fixpoint build
path, and proves it byte-stable.

Proven under arm64 QEMU (new CI job link-selfhost-native, mirrors build-com-native):
  * A host bootstrap linker BUILDS gen1 LINK.EXE (Rule 9: a build step is not an
    activation proof); gen1 is an OVMX-native image.
  * DCL session #1 runs @sys$SYSTEM:BUILD LINKSH LINK OVMX_LINK_RMS_IO. Inside it
    the multi-TU BUILD.COM has the OVMX-native TCC.EXE compile link.c +
    ovmx_link_rms_io.c and the OVMX-native gen1 LINK.EXE link them into gen2 --
    every toolchain step an IMGACT-activated OVMX image, no bash in the path.
  * gen2 is installed as SYS$SYSTEM:LINK.EXE; DCL session #2 runs the SAME @build
    and the OVMX-BUILT gen2 LINK.EXE links LINK.EXE again into gen3.
  * FIXPOINT: gen2 and gen3 are BYTE-IDENTICAL (sha256), the tcc gen2==gen3
    pattern (vms-4ba) now for LINK.EXE. This is S4 = "OVMX builds OVMX from within".

Multi-TU BUILD.COM (distro/rootfs/.../SYSEXE/BUILD.COM): extends the single-TU
S3.2 driver to compile N source TUs and link them into one image, via a real DCL
build loop -- SRC = P'N' parameter indexing, IF SRC .EQS. "" bare-symbol
termination, OBJLIST accumulation. New optional symbols: CFLAGS (extra tokens
ahead of the TCC compile) and NORUN (skip activating the linked image -- a linker
cannot be RUN with no args). `@BUILD HELLO` (single-TU, runs the image) is
preserved.

DCL symbol evaluation (vms-5c1), grounded in the DCL User's Manual and needed for
the loop:
  - dcl_symbol.c: ''symbol' (doubled apostrophe) now substitutes INSIDE a "..."
    string; a single apostrophe stays literal, as VMS.
  - dcl_exec.c: an UNQUOTED operand that names a defined symbol is auto-resolved
    in IF/WHILE string comparisons (IF P2 .EQS. "" tests P2's value) and on the
    right-hand side of an `=`/`==` assignment (A = B assigns B's value; SRC = P'N'
    parameter indexing). Quoted operands stay literal; undefined symbols keep the
    literal token (lenient, no %DCL-W-UNDSYM), matching parse_primary()/the
    integer-operand path. Covered by tests/dcl/test_symbol_eval.sh.

Full ctest 121/121 (parts_demo_e2e skips w/o runtime, as at baseline); DCL native
jobs unaffected. Deferred on vms-62b/vms-678: route the DCL LINK builtin to the
native LINK.EXE (vms-d5b7); F$ lexical coverage / more BUILD.COM ergonomics.

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 10, 2026
* WIP vms-df9: shared kit reader lib + vmsfs.ko setattr for protection/UIC persistence

Progress checkpoint, not yet complete:
- src/product/ovmx_kit_reader.{h,c}: factored the kit open/validate/
  read-entries/read-file-verified logic out of tools/ovmx_kit_pack.c into a
  shared module so PRODUCT.EXE (still to come) does not hand-roll a second
  kit parser. ovmx_kit_pack.c's list/extract now call it.
- src/kernel/vmsfs/vmsfs.h + vmsfs_blkdev.c: added .setattr to the
  block-device file/dir inode_operations, plus persisting owner UIC in
  vmsfs_blkdev_flush_inode(). Needed because PRODUCT INSTALL must stamp
  per-file protection/UIC from kit metadata onto the target volume, and
  neither chmod(2) nor chown(2) against a real vmsfs blkdev mount reached
  disk before this (no .setattr existed at all; fh_protection was written
  only from a cached field .setattr never touched, and fh_uic_* was never
  written back after creation). Compile-checked against the host's own
  6.8.0-136-generic headers.

Still to do: ovmx_product_db.h, src/product/product.c (PRODUCT.EXE itself),
CMake wiring for the new target, DCL cmd_product rewired to dcl_exec_utility,
QEMU ground-source proof, rebase onto latest origin/main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-df9: PRODUCT INSTALL lands a real kit on a real vmsfs volume

PRODUCT.EXE, not a DCL builtin (constraint #1): cmd_product
(src/vmsdcl/dcl_cmd_misc.c) is now a thin dcl_exec_utility() forwarder,
same shape as cmd_analyze/cmd_install/cmd_mail. It resolves /SOURCE
through dcl_resolve_path() and canonicalizes /DESTINATION, then hands
off to SYS$SYSTEM:PRODUCT.EXE (src/product/product.c), which owns kit
reading, file placement, protection/UIC, and the product database.

Kit reading is NOT reimplemented: src/product/ovmx_kit_reader.{h,c} is
the open/validate/read-entries/read-file-verified logic factored out of
tools/ovmx_kit_pack.c (which is refactored to call it too), so there is
exactly one kit parser in the tree. src/product/ovmx_product_db.h is the
new, OVMX-defined and Rule-8-labeled product database format
(SYS$SYSTEM:VMS$PRODUCT_DATABASE.DAT, whole-file-struct idiom matching
known_images.h's KFE database).

Security (constraint #2): every installed file's protection and owner
UIC come from its ovmx_kit_entry (fchmod(2)/fchown(2)), never a
default. This needed a real kernel change: src/kernel/vmsfs/
vmsfs_blkdev.c had no .setattr on its block-device inode_operations at
all, so chmod(2)/chown(2) against a real vmsfs mount silently updated
only the in-core inode -- fh_protection was written from a separate
cached field .setattr never touched, and fh_uic_* was never written
back after creation. Added vmsfs_blkdev_setattr() (persists protection
via the new vmsfs_mode_to_vmsprot() in vmsfs.h) and UIC persistence in
vmsfs_blkdev_flush_inode(). This also fixes DCL's SET PROTECTION, which
has called plain chmod() since it was written and never worked against
a blkdev-mode mount. Kept deliberately minimal: no new ioctl, no format
change, no vms.ko involvement -- chown(2) to a UIC other than the
caller's own still requires CAP_CHOWN via ordinary Unix semantics
(setattr_prepare), which no kit ovmx_kit_pack produces today needs.
Rule 4 cascade tracked as vms-79b (API compat GO, test coverage
confirmed, no doc impact beyond the code's own comments).

distro/Dockerfile.bootable: added the missing PRODUCT.EXE copy into
SYSEXE (measured failing first: %PCSI-F-NOIMG with it absent), and
stages a copy of the already-built, byte-verified /boot/ovmx-os.kit
onto the distrib image itself at SYS$UPDATE:OVMX-OS.KIT -- not a raw
third virtio disk, which devtmpfs creates root:root mode 0600 with no
udev to relax it in this minimal initramfs, so SYSTEM (uid 4/gid 1)
gets EPERM reading one directly. Reading the kit as an ordinary
SYSTEM-owned file on the already-mounted boot disk sidesteps that
instead of granting a permission OVMX has no privileged path for.

Ground-source proof (tests/qemu/test_product_install_e2e.sh, real
vms.ko+vmsfs.ko): INITIALIZE+MOUNT a second virtio disk, PRODUCT
INSTALL the real OS kit onto it, RUN the installed HELP.EXE FROM THE
TARGET (the anti-LARP crux -- a foreign-command definition, since RUN
never forwards argv and HELP.EXE needs a topic to avoid blocking on
interactive input), PRODUCT SHOW PRODUCT /DESTINATION=DKA100: lists it
by the kit's own embedded name, then a full QEMU restart proves both
the installed files and the product database persist on disk. All
15 checks pass. NOTIMPL baseline (every PRODUCT op before this bead)
verified by reading the pre-change cmd_product, matching this item's
own measured-baseline text.

Also fixes an INV-1 regression this work introduced along the way: a
version-literal-shaped example in an ovmx_product_db.h comment tripped
the identity SSOT gate (test_identity_ssot.sh) by landing on a code
line rather than a comment-only line the gate's exclusion regex skips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-df9 fixup: revert the vmsfs.ko .setattr change, it broke boot fail-stop

CI (PR #278) caught a real regression: tests/qemu/test_release_e2e.sh's
`norecord` case (SYSUAF rewritten with no SYSTEM row) must HALT at boot
with %OVMX-F-EXECINIT, no SYSTEM record -- with vmsfs_blkdev.c/vmsfs.h's
.setattr addition present, it silently booted to a login prompt instead.

BISECTED (not just suspected): reverting only these two files, rebuilding
the bootable image, and re-running test_release_e2e.sh made all 5 cases
pass including norecord's halt (30/0). Re-applying them reproduces the
failure. Root cause not further isolated -- the fix is the revert, not a
patch, per the tradeoff below.

DECIDED TO REVERT, NOT FIX IN PLACE, because the .setattr hook bought
PRODUCT INSTALL almost nothing: vmsfs_blkdev_create() already assigns
every new file VMSFS_PROT_DEFAULT and the creating process's own UIC, and
tools/ovmx_kit_pack.c's OVMX_KIT_PROT_DEFAULT / OVMX_KIT_UIC_*_DEFAULT are
numerically IDENTICAL to those (0xAA00, SYSTEM [1,4]) -- so for every kit
ovmx_kit_pack produces today, PRODUCT INSTALL's fchmod(2)/fchown(2) calls
in src/product/product.c were already redundant with what create() sets
at file-creation time. Without the kernel change those calls still run,
still return success (the kernel's generic simple_setattr() fallback),
and still fail loudly on a real error -- they just would not durably
override a value that DIFFERS from the create()-time default, which no
current kit needs. That is a real, but narrow and non-security-relevant
(a new file can never land more permissive than VMSFS_PROT_DEFAULT
regardless), gap -- tracked as a follow-up (vms-738) rather than carried
as boot-breaking kernel surface. src/product/product.c's comments are
updated to describe this honestly instead of claiming the hook is needed.

RE-VERIFIED GREEN after the revert, same rebuilt image:
  - tests/qemu/test_release_e2e.sh: 30 passed, 0 failed (all 5 cases,
    including norecord's halt).
  - tests/qemu/test_product_install_e2e.sh (vms-df9's own ground-source
    proof): 15 passed, 0 failed -- PRODUCT INSTALL, RUN of the installed
    HELP.EXE from the target, PRODUCT SHOW PRODUCT, and persistence across
    a full QEMU restart all still hold with the kernel change gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 11, 2026
…ame (#316)

cmd_assign() (src/vmsdcl/dcl_cmd_io.c) used to write its equivalence
string into the DCL SYMBOL table via dcl_sym_set() for every invocation,
including the /PROCESS default -- the wrong subsystem entirely. It now
calls the real lnm_create() against the logical-name manager, mirroring
cmd_define()'s existing LNM path:

- /PROCESS (default) and /SYSTEM /GROUP /JOB all route to the four
  well-known LNM tables. LNM$SYSTEM/GROUP/JOB are executive-resident
  (lnm_create() -> vms_kif -> /dev/vms) and already load-bearing for
  DEFINE, so ASSIGN now wires to them instead of refusing (INV-6: no
  per-process fallback, honest SS$_NOSUCHDEV/LNMFAIL with no executive).
- /TABLE=name (an arbitrary caller-named table) stays an honest
  %DCL-W-NOTIMPL/SS$_UNSUPPORTED refusal -- no table-by-name registry
  exists anywhere in src/vmslnm, for ASSIGN or DEFINE.
- DEASSIGN already called the real lnm_delete() (independently REAL,
  not part of the ASSIGN facade) and needed no change.

Clean-room (Rule 8): ASSIGN's parameter order (equivalence-name THEN
logical-name), default table (LNM$PROCESS), and qualifier set are the
public OpenVMS DCL Dictionary's ASSIGN entry.

New veracity gate tests/dcl/test_assign_real_lnm_veracity.sh fails on
the old symbol-facade and passes on the fix: ASSIGN BAR FOO makes
F$TRNLNM("FOO")/SHOW LOGICAL FOO show "BAR" while SHOW SYMBOL FOO finds
nothing (%DCL-W-NOLCL), and DEASSIGN FOO removes it. Updated the Phase 0
facade canary (test_facade_gate_phase0.sh) from ASSIGN/SYSTEM (now wired)
to ASSIGN/TABLE (still honestly refused). Scoreboard: ASSIGN moves
PARTIAL -> REAL (49 REAL / 3 PARTIAL / 1 FACADE / 1 STUB).

Full DCL suite green: 100/100 (ctest dcl-integration + harness-selftest
+ foreign_command_dispatch_gate).

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 11, 2026
…319)

cmd_set_password() (src/vmsdcl/dcl_cmd_set.c) printed
"%SET-I-PASSWORD, password change not fully implemented" and returned
SS$_NORMAL without touching SYSUAF -- a success-toned lie for a no-op,
the banned class INV-DCL exists to kill.

It now implements the real public OpenVMS DCL Dictionary "SET PASSWORD"
self-service exchange: Old password / New password / Verification, no
terminal echo, verified against the real SYSUAF hash
(sysuaf_authenticate()), and on match writes a real new hash through a
new shared writer, sysuaf_write_record() (src/libvms/rtl/sysuaf.c) -- a
second caller of the one writer (sysuaf_format_record(), vms-9b7/INV-1),
never a second SYSUAF format. /SECONDARY and /SYSTEM (SECURITY-gated) and
/GENERATE honestly refuse; mismatch/blank/under-length (Dictionary
PWDMINIMUM default 6) refuse without writing.

Deviation from the item's initial framing (flagged, source-of-truth
hierarchy #1): the public Dictionary shows SET PASSWORD takes no
parameters and has no /USER= qualifier -- there is no DCL-level way to
change another account's password; that stays AUTHORIZE's job (already
SYSPRV-gated).

Veracity: tests/libvms/test_sysuaf_write_veracity.c drives
sysuaf_lookup -> sysuaf_authenticate -> sysuaf_write_record against a
real SYS$SYSTEM:SYSUAF.DAT in an isolated temp root, proving the new
password authenticates, the old one no longer does, and a bystander row
is untouched. tests/dcl/test_set_password_veracity.sh proves the DCL
surface no longer fakes success (bogus qualifier -> IVQUAL, /SECONDARY
and /GENERATE -> honest NOTIMPL, extra parameter -> MAXPARM). DCL suite:
101/101 (was 100/100 baseline + 1 new test) under canonical ctest.

docs/dcl-verb-fidelity-scoreboard.md updated: SET PASSWORD FACADE -> REAL.

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl pushed a commit that referenced this pull request Aug 13, 2026
The cut-release-reproducible gate (vms-d73, .github/workflows/ci.yml) cuts the
release twice with --no-cache and requires vmlinuz + the slim initramfs (which
now carries the from-source-built vms.ko/vmsfs.ko) byte-identical across both.
A stock kernel build stamps the wall-clock build time + build user/host into the
image (UTS_VERSION), so two cuts would differ.

Fix the kernel-build stage:
  - declare ARG/ENV SOURCE_DATE_EPOCH (cut-release.sh passes the commit time to
    every stage) and derive KBUILD_BUILD_TIMESTAMP from it via `date -u -d @EPOCH`
  - pin KBUILD_BUILD_USER=ovmx, KBUILD_BUILD_HOST=ovmx
  - config fragment: force CONFIG_RANDSTRUCT_NONE (no per-build struct-layout
    seed) alongside the already-off CONFIG_MODULE_SIG (no per-build signing key)

The build number (#1) is deterministic because --no-cache always builds from a
fresh tree; embedded absolute paths are identical across cuts (fixed build path).

Verified (foreground, all containerized):
  - two independent `docker build --no-cache --target kernel-build` produce a
    BYTE-IDENTICAL vmlinuz (sha256 6b522a39...); vms.ko (78183d35...) and
    vmsfs.ko (f2fe0817...) are byte-identical across both too
  - the nonzero commit-epoch path is also byte-identical (429705902e9b...); the
    embedded banner confirms `6.12.103-ovmx (ovmx@ovmx) #1 ... <epoch date>`
  - test_distrib_boot.sh still 17/17 -- no boot regression

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
…p stock Ubuntu vmlinuz (#450)

* vms-448: build the bootable kernel from pinned source (6.12.103 LTS), drop stock Ubuntu vmlinuz

distro/Dockerfile.bootable shipped whatever `apt install linux-image-generic`
resolved. Replace it with a `kernel-build` stage that fetches a PINNED upstream
LTS tarball (linux-6.12.103), verifies it against the kernel.org PGP-signed
sha256, applies a curated config (x86_64 defconfig + distro/kernel/ovmx-x86_64.config
merged via merge_config.sh + olddefconfig), builds the bzImage, and uses THAT
as /boot/vmlinuz. This is the concrete beachhead of the owns-kernel epic
(vms-19e): the shipped kernel is now ours, pinned, and reproducible.

Kernel-build stage (build tooling only, Rule 9 -- deps live in the container,
nothing on the host):
  - fail-closed sha256 gate on the download (no unpinned/tampered source)
  - a hard .config gate re-asserting every load-bearing option (serial console,
    virtio-blk/net, devtmpfs/proc/sysfs/tmpfs, initrd+gzip, modules, ELF, ext4)
    and refusing CONFIG_MODULE_SIG_FORCE (would reject unsigned vms.ko/vmsfs.ko)
  - records the exact kernelrelease (6.12.103-ovmx via CONFIG_LOCALVERSION)

Builder stage: drops linux-headers-generic/linux-image-generic; builds
vms.ko/vmsfs.ko against the from-source kernel tree and gates their vermagic
== the kernel's release string; adds libelf1 (the copied objtool needs
libelf.so.1 at runtime). The bootable vmlinuz is the from-source bzImage.

Module fixes needed to build against 6.12 (all version-guarded / harmless on the
stock 6.8 module-test harnesses in src/kernel/Dockerfile + tests/qemu/Dockerfile):
  - vms_internal.h: reword a comment whose "src/kernel-core/*.c" glob opened a
    nested comment (-Werror=comment under the kernel's CONFIG_WERROR=y)
  - vmsfs_blkdev.c: guard ->write_begin for the 6.12 folio conversion of
    block_write_begin() (struct page ** -> struct folio **); the <6.12 page
    signature is kept behind LINUX_VERSION_CODE so stock-6.8 builds are unchanged

WHY 6.12 and not the newest LTS (6.18): vms.ko builds clean on 6.18, but vmsfs.ko
uses the pre-6.14 VFS mount surface (super_block.s_d_op, mount_bdev/mount_nodev)
that 6.14 removed for set_default_d_op() + the fs_context get_tree_* API. Porting
vmsfs.ko across that boundary is separate work; advancing the kernel floor is
gated on it. 6.12 LTS (the item's stated floor) keeps both modules building.

Verified (x86_64, all containerized):
  - kernel builds from the pinned+checksummed source; .config gate passes
  - vms.ko + vmsfs.ko build against 6.12.103-ovmx (vermagic matches) AND still
    build against stock 6.8 (version guard holds both harnesses)
  - test_distrib_boot.sh: 17/17 -- executive attaches (vms.ko + /dev/vms),
    DKA0: mounts (vmsfs.ko), SYSTEM logs in, DCL/DIRECTORY work, clean re-boot
  - test_persistent_boot.sh: 24/24 -- positive login + honest mount-or-halt
    negative control, all on the from-source kernel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-448: make the from-source kernel build byte-reproducible (vms-d73)

The cut-release-reproducible gate (vms-d73, .github/workflows/ci.yml) cuts the
release twice with --no-cache and requires vmlinuz + the slim initramfs (which
now carries the from-source-built vms.ko/vmsfs.ko) byte-identical across both.
A stock kernel build stamps the wall-clock build time + build user/host into the
image (UTS_VERSION), so two cuts would differ.

Fix the kernel-build stage:
  - declare ARG/ENV SOURCE_DATE_EPOCH (cut-release.sh passes the commit time to
    every stage) and derive KBUILD_BUILD_TIMESTAMP from it via `date -u -d @EPOCH`
  - pin KBUILD_BUILD_USER=ovmx, KBUILD_BUILD_HOST=ovmx
  - config fragment: force CONFIG_RANDSTRUCT_NONE (no per-build struct-layout
    seed) alongside the already-off CONFIG_MODULE_SIG (no per-build signing key)

The build number (#1) is deterministic because --no-cache always builds from a
fresh tree; embedded absolute paths are identical across cuts (fixed build path).

Verified (foreground, all containerized):
  - two independent `docker build --no-cache --target kernel-build` produce a
    BYTE-IDENTICAL vmlinuz (sha256 6b522a39...); vms.ko (78183d35...) and
    vmsfs.ko (f2fe0817...) are byte-identical across both too
  - the nonzero commit-epoch path is also byte-identical (429705902e9b...); the
    embedded banner confirms `6.12.103-ovmx (ovmx@ovmx) #1 ... <epoch date>`
  - test_distrib_boot.sh still 17/17 -- no boot regression

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: alice <alice@workspace.local>
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
…ee head must be pointer-width (#463)

MMK.EXE SIGSEGV'd in the guest before sp_open (post-parse/pre-drive),
nondeterministically and only with a real executive. Prior work (vms-b23
#462) characterized it via a SIGSEGV handler but lacked a real backtrace.

Root cause (real backtrace, this commit): a 64-bit pointer-width bug.
objects.c declares the LIB$*_TREE root cell as `static unsigned int
objtree` — 4 bytes. On the VAX a longword IS a pointer so stock MMK is
correct; on a 64-bit OVMX target that cell is too small. lib$insert_tree /
lib$lookup_tree take the head by reference and dereference it as a full
8-byte pointer, so they:
  - over-READ 8 bytes of a 4-byte global (the adjacent global's 4 bytes
    become the high half of a bogus root pointer), and
  - on insert, over-WRITE, truncating the stored root node address.
The reconstructed garbage pointer is later dereferenced -> SIGSEGV. Whether
it faults depends on address-space layout, which is exactly why it was
nondeterministic and "executive-dependent" (the executive changes the heap
layout); on the host the reconstructed pointer happened not to fault, so
MMK reached sp_open cleanly.

Pinned with a real backtrace via ASan on the host mmk_native ELF, run with
the identical input the guest capstone uses
(VMS_FOREIGN_CMD="/DESCRIPTION=OVMXB23.MMS OVMXB23.OUT"):

  ERROR: AddressSanitizer: global-buffer-overflow ... READ of size 8
    #0 lib$lookup_tree            src/libvms/rtl/lib_tree.c:108
    #1 Find_Object               tests/corpus/tier3-mmk/objects.c:102
    #2 make_objrefs              tests/corpus/tier3-mmk/parse_descrip.c:1135
    #3 parse_store               tests/corpus/tier3-mmk/parse_descrip.c:1051
    #4 act_prs                   tests/libvms/mmk_parse_tables.c:85
    ... lib$table_parse -> parse_descrip -> Read_Description
    #9 main                      tests/corpus/tier3-mmk/mmk.c:705
  0 bytes after global variable 'objtree' (size 4)

Fix: declare objtree pointer-width (`void *`), matching symbols.c's
apply_sort() `void *tree` and the LIB$ manual's quadword tree head on
64-bit architectures. After the fix ASan is clean on the same input and
MMK proceeds through the object tree to the drive (sp_open).

Clean-room (Rule 8): objects.c is stock MadGoat freeware; the one-line
width change is an OVMX portability fix, tagged inline.

Proof:
  - ASan before: global-buffer-overflow at lib_tree.c:108 (above).
  - ASan after: clean; MMK reaches the drive (same as host baseline).
  - toolchain-mmk-parse ctest: PASS (no host regression).
  - build-static (musl) mmk_native: builds clean.

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl pushed a commit that referenced this pull request Aug 13, 2026
…ous bound), not a 2s reap grace

CI's Kernel Executive job went red from a clean build: mmk_build produced the
object and echoed the marker (build-#1 assertions GREEN) but MMK had not yet
finished tearing down its spawned DCL within the tight 2s REAP_GRACE, so reap1
stayed 0, the reap1 short-circuit skipped drive #2, and the completion +
byte-identity assertions reddened. CI's TCG is much slower than the dev host,
where reap1 was always 1.

Restructure drive_build to a SINGLE generous bounded wait (40s) that drains
output (detecting the marker) AND polls for MMK to exit, returning the instant
MMK exits -- so a green drive costs only its real runtime and the bound is only
ever hit by a genuine hang. Removes the split 10s-marker / 2s-reap phases that
were sized for a fast dev host.

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
…byte-identical, zero-bash (CI gate) (#476)

* vms-d1b: self-host spine #6 — MMK drives a real TCC compile in QEMU, byte-identical, zero-bash (CI gate)

The shipped MMK.EXE now drives a REAL toolchain step inside OVMX in QEMU,
against a real /dev/vms: it spawns a persistent DCL over VMS mailboxes and
streams a descrip.mms whose action defines a foreign command TCC :== "$..." and
invokes it to compile the REAL src/libvmssys freestanding runtime TU
vms_string.c to an object. DCL activates the foreign command by fork()+execve()
of the staged static TCC.EXE (a plain static image is not in-process-eligible,
so imgact_activate returns SS$_UNSUPPORTED and DCL forks it) — the FIRST-EVER
TCC.EXE run inside QEMU and the first time MMK drives a real compiler (not a DCL
builtin) end to end. The parent (which never runs a compiler) asserts the driven
object is a valid ELF relocatable carrying vms_strlen and is BYTE-IDENTICAL
across two independent in-guest MMK-driven builds. Zero bash in the build path.

This closes spine #6 (vms-d1b, the CI gate) and the MMK-driven-EXECUTION residual
of spine #5 (vms-fe4) for the COMPILE stage.

- tests/toolchain/mk_tcc_static.sh: builds tinycc as a PLAIN STATIC (musl)
  foreign-command image (distinct from mk_tcc.sh's IMGACT-packaged self-host
  image) — the binary DCL fork+execve activates, no IMGACT/shareable staging.
- tests/toolchain/run_tcc_static_component.sh + CMake test
  toolchain-tcc-static-component: host proof the static TCC.EXE compiles the real
  runtime TUs (vms_string/vms_snprintf/driver) to valid, byte-identical objects;
  asserts vms_math.c is the documented x86 tcc-blocked TU (SSE "x" inline asm).
- tests/qemu/test_syssvc_mmk_build.c: the QEMU suite (extends spine #4's
  test_syssvc_mmk_drive.c); honest-skips 77 with no /dev/vms.
- tests/qemu/Dockerfile: stages static TCC.EXE at SYS$SYSTEM, tinycc's headers +
  musl's stdint.h closure beside it, and the real component source. The suite
  plugs into the STANDING kernel-executive CI barrier (builds the image from the
  checked-out tree, a clean context), so the MMK-driven native build is gated on
  every run — no new job.
- facility_defects.sh: new per-facility control mmk-build-image-not-activated —
  dcl_exec_foreign_command reports success WITHOUT activating the image, so the
  driven TCC command completes but runs no compiler; reddens exactly the suite's
  five object/byte-identity assertions FAST (no $HIBER wedge), attributable to
  the build drive alone. A DEDICATED control, not a second suite on the
  sp_send=0 drive control: two ~50s $HIBER wedges do not fit run_tests.sh's 120s
  QEMU budget in one boot. Floor 96 -> 97.

Verified in QEMU on this host: kernel-executive 76/76 (mmk_build 7/7);
mmk-build-image-not-activated reddens exactly the 5 object assertions with no
strays and the harness completes (no timeout); executive-absent mmk_build rc=77.

BUILD.COM retirement is NOT done: the full compile->archive->LINK-to-image chain
in-guest remains (vms_math not tcc-compilable on x86_64; LINK needs the
SYS$LIBRARY shareables staged + logical-name resolution in LINK.EXE + IMGACT
activation) — the precise residual for spine #7, documented in
docs/design-self-host-spine5-mmk-component.md. No red gate shipped (Rule 6/7).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-d1b: fix mmk_build CI timing — wait for MMK to EXIT (single generous bound), not a 2s reap grace

CI's Kernel Executive job went red from a clean build: mmk_build produced the
object and echoed the marker (build-#1 assertions GREEN) but MMK had not yet
finished tearing down its spawned DCL within the tight 2s REAP_GRACE, so reap1
stayed 0, the reap1 short-circuit skipped drive #2, and the completion +
byte-identity assertions reddened. CI's TCG is much slower than the dev host,
where reap1 was always 1.

Restructure drive_build to a SINGLE generous bounded wait (40s) that drains
output (detecting the marker) AND polls for MMK to exit, returning the instant
MMK exits -- so a green drive costs only its real runtime and the bound is only
ever hit by a genuine hang. Removes the split 10s-marker / 2s-reap phases that
were sized for a fast dev host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-d1b: make mmk_build load-robust — capture proof (marker+object) instead of waiting on MMK's exit

The clean-build repro showed mmk_build's remaining flake was MMK's slow
self-exit under contended TCG: the compile finished, the marker was echoed and
the byte-identical object was on disk, but MMK had not yet torn down its spawned
DCL and exited within the bound, so the reap-based assertion reddened. MMK's
exit timing is not a property this suite tests.

drive_build now stops the instant the PROOF is captured -- the DCL echoed
OVMXD1B:COMPILED AND the object exists on disk -- and kills MMK as cleanup rather
than gating on its self-exit. A genuine mid-drive $HIBER deadlock still fails
hard (no marker is ever echoed). The completion assertion is now the marker
(reliable), the reap-exit assertion is dropped, and the drive-#2 short-circuit is
keyed on the object (robust under load), not on MMK's exit. The negctl declared
set is unchanged (the 5 object/byte-identity assertions; the marker stays green).

Verified: two consecutive clean green runs (mmk_build 6/6), and
mmk-build-image-not-activated reddens exactly the 5 object assertions with the
marker green. (A QEMU timeout / mmk_drive reap-flake seen intermittently here is
this 10-container dev host's load, not the code: on CI the harness completed and
mmk_drive passed.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl pushed a commit that referenced this pull request Aug 13, 2026
…aise per-drive bound

The first cut wired the LINK+activate onto BOTH in-guest MMK drives. Under slow/
contended TCG that reddened intermittently: the 40s per-drive bound elapsed AFTER
the archive but BEFORE the LINK (the drive was progressing, not wedged), killing
MMK mid-drive; and two heavy full-chain drives pushed the whole suite toward
run_tests.sh's 120s whole-VM budget. mmk_build passed 3x then failed once (7/11)
— a Rule 8 flake, not shippable.

Fix (mmk_build now 16/0 across repeated runs, whole VM 76/76 + green when the host
has capacity):
- Drive #1 is the lighter compile+archive drive (vms-6be); drive #2 runs the full
  compile→archive→LINK chain + activation. The compile+archive OUTPUTS are still
  asserted byte-identical across the two drives; the LINK runs once.
- The LINK OUTPUT's byte-identity is proven byte-identical on the host
  (run_mmk_component_build.sh links the image twice, cmp-clean); in-guest the rung
  proves the driven LINK yields a real image that ACTIVATES and RUNS (exit 216) —
  the property that could not be shown before. This keeps the suite close to the
  vms-6be weight the kernel-executive barrier already carries reliably.
- Per-drive bound raised 40s→60s so a slow-but-progressing full drive completes
  the LINK before cleanup.

Negctl mmk-build-image-not-activated: the two dropped image assertions
(build #2 / image byte-identity) removed from knock_on_fail; the four LINK+
activate assertions kept (produced / valid ET_DYN / PT_INTERP / activated 216),
knock_on_why updated. Assertion set: 1 require + 14 knock = the 15 non-completion
assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 14, 2026
…arams (#550)

* vms-46c (gap #2): prove & CI-wire conversational boot for cluster params

The conversational-boot MECHANISM (SYSBOOT> halts pre-banner on the boot
flag; SHOW/SET/USE/WRITE/CONTINUE against SYS$SYSTEM:OVMXVMSSYS.PAR) landed
with vms-b81. Two gaps remained against gap #2's outcome, both closed here:

1. test_sysboot_conversational.sh (vms-b81's own proof: pre-banner halt,
   byte-shaped SHOW table, SET SCSNODE + CONTINUE, in-memory persistence
   semantics) was NEVER wired into any CI job -- an unrun test is an absent
   test (Rule 7). Wired into the persistent-boot (boot-smoke) job + the
   `boot` paths filter; job budget bumped 30->40m for the added expect run.

2. The NUMERIC cluster-param path through SYSBOOT was untested -- the
   existing proof only drives SET SCSNODE (string). New e2e
   (test_sysboot_cluster_params_e2e.sh + run wrapper + ctest reg + CI job
   sysboot-cluster-params-e2e) authors a string (SCSNODE) AND a numeric
   cluster-identity param (SCSSYSTEMID) at the SYSBOOT> prompt, WRITEs a real
   vmsfs ;2, CONTINUEs, then proves in the booted logged-in guest that
   F$GETSYI reads BOTH authored values back -- with a flagless bracket boot
   on a fresh disk showing the seeded defaults. This is the "cluster params
   authored interactively before boot" proof (docs/design-boot-faithful.md
   sec 2.2/4.2) -- the clustering relevance of conversational boot.

No source, boot goldens, or the seeded OVMXVMSSYS.PAR were touched; the
pinned faithful-boot conformance sequence is unchanged. Gaps #1/#3/#4 of the
epic remain open (separate dispatches).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-46c gap #2: fix two never-run SYSBOOT> proof assertions to match real boot

Both boot proofs the PR wires into CI failed on genuine ASSERTION bugs, not
boot bugs. The conversational-boot mechanism and cluster-param authoring are
correct; the authored values (SCSNODE=CLUX, SCSSYSTEMID=1027) are provably in
effect in the booted guest.

1. test_sysboot_cluster_params_e2e.sh — F$GETSYI("SCSSYSTEMID") returns an
   INTEGER (unlike the string NODENAME), so DCL renders the symbol UNQUOTED
   with Hex/Octal columns: "SIDP = 1027   Hex = 00000403  Octal = ...". The
   test wrongly expected the string form 'SIDP = "1027"' / 'SIDD = "0"'. Anchor
   on the value AND its hex (0x403 == 1027, 0x0 == 0) so a wrong value cannot
   pass. CASE 1 + CASE 2 both fixed.

2. test_sysboot_conversational.sh (Boot B, vms-b81) — the "nothing precedes
   SYSBOOT>" check demanded the pre-prompt console region be byte-empty, which
   is impossible: expect's own spawn echo, SeaBIOS, "Booting from ROM", the ANSI
   clear-screen, and the substrate identity line "OVMX/Linux -- SYSKRNL" always
   precede it. That is why it never passed — it had never been RUN in CI (the
   gap this PR closes). The design's real claim (design-boot-faithful.md §3.1:
   "No banner precedes SYSBOOT>") and the oracle itself (the SRM `P00>>>`
   bootstrap block precedes SYSBOOT>) show the load-bearing proof is: no VMS
   BANNER and no executive narration precedes the prompt. Assert exactly that —
   the pre-prompt slice carries no `%OVMX-` line and no `OpenVMX Vx.x` banner —
   which still fails hard if the executive-attach line leaks before SYSBOOT>.

No source, boot goldens, or seeded OVMXVMSSYS.PAR touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 17, 2026
…k1 (kill the slow-TCG flake, no wall bump) (#636)

test_syssvc_mmk_build mark1 (drive #1 = compile+archive) gave each drive a
FIXED 60s host-wall budget (300x poll(200ms)). Drive #1 has MMK.EXE spawn DCL
which forks TCC.EXE to compile the real vms_string.c IN-GUEST; under GitHub's
~10x-slow TCG runner that compile's host-wall can exceed 60s -> OVMXD1B:COMPILED
marker arrives late -> mark1 FAILs intermittently. This is a pre-existing,
emulation-speed-dependent flake (bisect found NO first-red commit: pristine is
green at the pre-ACP baseline AND current tip; the "#630 first-red" was a
nightly-cadence artifact). Not a regression in the alpha or ACP work.

Fix (Rule 8 root cause, NOT a wall bump): budget the drives in MEASURED guest
compile time instead of host wall. calibrate_tcc_ms() times a direct fork+exec
TCC compile of vms_string.c right before the drives; drive_budget_ms() =
DRIVE_COMPILE_MARGIN(3) * t_cal + DRIVE_FIXED_OVERHEAD_MS(8s) + (link ? 20s : 0).
On a fast host this reproduces the historical ~60s neighborhood (no behavior
change); on a 10x-slow host the budget scales with the SAME multiplier the
guest's own compiler runs under, so it can't be outrun by TCG speed. The old
60s constant survives ONLY as CAL_FALLBACK_MS -- a floor when calibration can't
measure -- never as the ceiling a slow run is held to. Calibration is bounded
by CAL_TIMEOUT_MS(180s) so a wedged TCC is a named failure, not a hang.

The SEMANTIC assertion is unchanged: MMK.EXE must still drive the real
spawn + mailbox + write-attention AST + $HIBER + IO$M_NOW + $STATUS path and the
test still requires the real OVMXD1B:COMPILED marker. facility_defects negctl
anchor intact.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 17, 2026
…t add-on (#638)

Fast-follow to #636 (vms-9d4f). #636 fixed drive #1's (compile+archive)
budget to scale with the measured in-guest compile speed, but left drive #2
(do_link=1: compile+archive+LINK+activate) with a flat +20000ms add-on atop
the calibrated compile term instead of scaling that extra work too.

Robustness testing (docker run --cpus=<throttle>, simulating slow TCG) found
the gap: at ~8.5x throttle (t_cal 897ms vs ~106ms baseline), drive #2's
LINK+activate stage missed its budget under the OLD fully-fixed 60000ms
constant -- a real instance of the fixed-host-wall failure mode #636 targets.
But #636's shipped design (flat +20000) did not close this either: since the
flat add-on is capped and doesn't grow with slowdown, it degrades to the same
floor-dominated behavior as the old constant at any throttle level that
doesn't already clear the compile-margin term alone. This file's own history
already says the LINK+activate cost is TCG-speed-dependent (the 40000->60000
raise was explicitly "under slow/contended TCG the whole chain can exceed the
old bound") -- so it should scale with t_cal like the compile term does, not
sit behind a flat constant.

Fix: replace DRIVE_LINK_OVERHEAD_MS (flat +20000) with
DRIVE_COMPILE_MARGIN_LINK (6, vs drive #1's margin of 3), applied to the SAME
calibrated t_cal. Both drives now scale proportionally with measured guest
speed; only DRIVE_FIXED_OVERHEAD_MS (spawn/mailbox/dispatch/marker, 8000ms)
stays flat. On a fast host this is unchanged (both drives still floor at the
historical 60000ms neighborhood -- verified 16/16 green under the faithful
gate, docker build -f tests/qemu/Dockerfile + docker run, 1533/1533
assertions passing). On a slow host both drives' budgets now grow with the
SAME multiplier the guest is actually running under.

SEMANTIC assertion unchanged: MMK.EXE still drives the real spawn + mailbox +
write-attention AST + $HIBER + IO$M_NOW + $STATUS path to the real
OVMXD1B:COMPILED marker; facility_defects negctl anchor (mmk-build-image-not-
activated) untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 18, 2026
…fix SYSUAF encrypt byte to 3, drop false purdy claim

BLOCKER #1 (rms-put-wrong-vbn negctl was a dead no-op): the refactor moved
the a.vbn computation out of the rms_io_write() dispatch wrapper into the
static rms_io_write_acp() backend, so the sed anchored to /^ssize_t
rms_io_write/,/^}$/ matched nothing -- cmd_apply reported BROKEN FIXTURE
(rc=3), reddening the kernel shard, and the correct-VBN $PUT-over-ACP
guarantee went unguarded. Re-anchor to /^static ssize_t
rms_io_write_acp/,/^}$/. Verified: apply now injects (rc=0) on the +1u->+2u
line in write_acp only (read_acp untouched); a second apply is BROKEN FIXTURE
(idempotency intact); facility_defects.sh selftest now reports the defect ok.

BLOCKER #2 (SYSUAF UAF$B_ENCRYPT off-by-one, resolves vms-722): uaidef.h had
AD_II=1/PURDY=2/PURDY_V=3/PURDY_S=4, so OVMX stamped 4 on disk and rejected
!=4 -- a non-authentic byte that would reject a genuine VMS SYSUAF. The oracle
DUMP measured 0x03 on VAX 7.3 + Alpha 8.4 and public $UAIDEF enumerates
PURDY_S=3; corrected uaidef.h to AD_II=0/PURDY=1/PURDY_V=2/PURDY_S=3 (CUST=128,
the customer-algorithm base). Regenerated the seed SYSUAF.DAT via mksysuaf --
SYSTEM data record now carries encrypt=0x03; byte-reproducible. Reader/writer
both key off UAI$C_PURDY_S so they agree at 3. Pinned the test to the literal
oracle value (b[0x168]==3, UAI$C_PURDY_S==3). Reconciled the sysuaf.h
source-of-truth note (conflict RESOLVED, answer 3).

SMELL (purdy.c overclaim): the header claimed verification against "200
published DecHpwd PURDY_S calibration vectors -- see test_purdy.c", but
test_purdy.c carries only the 7 real-VMS oracle vectors. Sourcing/fabricating a
200-vector corpus in-rung is infeasible/forbidden (circular self-assertion),
so the false cross-reference is DELETED; the true 7-oracle-vector claim stays.

Verify: 46/46 libvms/rms functional ctests green (incl. sysuaf record byte==3,
vmsrms_sysuaf_live SYSTEM/MANAGER, purdy 7-vector); rms-put-wrong-vbn selftest
ok. Pre-existing selftest FAILs (rightslist-general-hex-as-decimal,
sysuaf-uic-writeback-decimal stale anchors; rms_p3 no-anchor; missing assertion
text) are OUT OF SCOPE for this rung -- flagged to conductor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 21, 2026
…river forcing function) (#672)

Grounded design for the subprocess-pipeline facility the GCC driver
(cpp->cc1->as->ld) will force, surfaced by the GCC-oracle lane (vms-da0).
Finding: OVMX has three divergent process-creation paths; lib$spawn does a
real fork/exec of DCL.EXE but never registers with the executive (prcnam/efn/
astadr discarded, lib_misc.c:275) -> spawned procs invisible to $GETJPI/SHOW
SYSTEM/$DELPRC, no /NOWAIT completion AST. Design unifies onto SYS$CREPRC +
adds exit EF/AST notification; GCC driver calls LIB$SPAWN, not musl vfork.
This lib$spawn->raw-fork bypass is also instance #1 in the executive-boundary
program (vms-040). rd item: vms-e9a.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 21, 2026
…ite set reaches FINAL RESULTS

The 6->10 shard split fixed run #1's 30m outer timeout, but that UNMASKED
a per-defect failure the timeout had hidden. Run 32487119712 (389d129):
shards 1, 2, 3 FAILED (genuine, not cancelled) -- shard 1 on
eflag-readef-status-inverted, shard 2 on lnm-manager-delete-noop, shard 3
on eflag-dacefc-status-wrong. Read on the rail + from the CI logs: in
ALL three the control WORKED (the facility's own suites reddened, red set
EXACTLY the manifest's) -- the FAIL is uniformly "the harness never
reached FINAL RESULTS", with ~20 later suites NEVER RAN. It is a
wall-timeout TRUNCATION, not a stale control (eflag = kernel event flags,
lnm = name manager -- neither is a flip-retired /vms path; NONE of these
is the rms/setuai re-anchor class).

Root cause: the per-facility negctl runs the ENTIRE suite set in ONE VM
per defect (check 5's isolation attribution must see every suite, so it
cannot shard suites like the positive kernel-executive job does). The
0.5 flip grew that set ~76 -> ~97, and a mutation that makes event-flag
$WAITFRs wait out their timeouts adds enough per-suite slack that the
full run passes ~600s under CI contention and truncates at ~75/97 suites.
My rail run of the same shard passed only because a quiet node fit under
600s -- a marginal wall problem, exactly what run_tests.sh's own comment
says to fix by raising TIMEOUT ("never drop a suite").

Fix (faithful -- no control weakened, no defect deleted, no suite
dropped; every control still must redden AND reach FINAL RESULTS):
  - run_tests.sh: TIMEOUT is now env-overridable (KE_WALL_TIMEOUT),
    default 600 unchanged -- the positive job shards suites and keeps it.
  - inject_and_run.sh (the per-facility full-suite path): sets
    KE_WALL_TIMEOUT=1200 (~776s extrapolated full run + ~1.5x margin) so
    a slowed boot COMPLETES instead of truncating.
  - per-facility shard timeout-minutes 30 -> 50, so a shard holding a
    now-~13m slow defect still finishes.
A genuine hang (>1200s or a panic) still fails the defect and the 50m
outer bound backstops a wedged shard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 21, 2026
…ite set reaches FINAL RESULTS

The 6->10 shard split fixed run #1's 30m outer timeout, but that UNMASKED
a per-defect failure the timeout had hidden. Run 32487119712 (389d129):
shards 1, 2, 3, 5 FAILED (genuine, not cancelled) -- shard 1 on
eflag-readef-status-inverted, shard 2 on lnm-manager-delete-noop, shard 3
on eflag-dacefc-status-wrong, shard 5 on image-rundown-without-entry.
Read from the CI logs: in ALL FOUR the control WORKED (the facility's own
suites reddened, red set EXACTLY the manifest's, "13 passed, 1 failed")
-- the FAIL is uniformly "the harness never reached FINAL RESULTS", with
~20 later suites NEVER RAN. It is a wall-timeout TRUNCATION, not a stale
control: eflag = kernel event flags, lnm = name manager, image-rundown =
image activation -- none is a flip-retired /vms path, NONE is the
rms/setuai re-anchor class. facility_defects.sh needs no reconciliation.

Root cause: the per-facility negctl runs the ENTIRE suite set in ONE VM
per defect (check 5's isolation attribution must see every suite, so it
cannot shard suites like the positive kernel-executive job does). The
0.5 flip grew that set ~76 -> ~97, and a mutation that makes event-flag
$WAITFRs wait out their timeouts adds enough per-suite slack that the
full run passes ~600s on GitHub's TCG runner and truncates at ~75/97
suites. Measured on real CI: ~75 suites at the 600s wall -> the full 97
need ~776s under that slowdown.

Fix (faithful -- no control weakened, no defect deleted, no suite
dropped; every control still must redden AND reach FINAL RESULTS, exactly
what run_tests.sh's own comment prescribes: "raise TIMEOUT, never drop a
suite"):
  - run_tests.sh: TIMEOUT is now env-overridable (KE_WALL_TIMEOUT),
    default 600 unchanged -- the positive job shards suites and keeps it.
  - inject_and_run.sh (the per-facility full-suite path): sets
    KE_WALL_TIMEOUT=1800 (~2.3x the measured ~776s -- margin for defects
    slower than eflag-readef, e.g. eflag-dacefc, which needed >1200s on a
    contended rail slice) so a slowed boot COMPLETES instead of truncating.
  - per-facility shard timeout-minutes 30 -> 50: with the wall raised, a
    formerly-truncated defect now runs ~13m (completes) instead of 10m
    (fails), so a shard holding one or two needs headroom past 30m; CI
    shard durations (failing shards ran 24-27m) confirm ~30m actual, 50m
    ceiling. A genuine hang (>1800s or a panic) still fails the defect and
    the 50m outer bound backstops a wedged shard.

Rail-verified (KVM, so a different speed profile than CI TCG, but the
control-reddens + reaches-FINAL-RESULTS behaviour is what is checked):
eflag-readef, lnm-manager-delete and eflag-dacefc all PASS with the raised
wall -- red set EXACTLY the manifest's, harness reaches FINAL RESULTS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 22, 2026
…_service budget

Two of the three B-rung-#1 reds are pure-harness, root-caused and fixed:
- test_syssvc_initialize (blank-device precondition): the harness reused a
  cached /work/d1.img, and truncate on an existing file keeps its bytes, so a
  prior run's INITIALIZE home block persisted -> 'no home block before' failed.
  rm the blank disk before truncate so it is genuinely all-zero each run.
- test_syssvc_startup_service (150s watchdog kill, signal 9 not SEGV): a long
  multi-phase RUN/DETACHED driver, genuinely slow under TCG not hung. Raise the
  per-suite watchdog 150->240s (subject-stub alarm 240->300 to stay > watchdog).

Neither is an Alpha runtime bug. procnam's 3 remaining reds are NOT force-fixed
here -- under investigation (ptrace-held-race-window under Alpha/TCG vs a real
LP64  teardown-ordering bug); reported to the conductor, pending an x86
comparison, never allowlisted to fake green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 28, 2026
… (block-then-grant on the requester) (#875)

* vms-6ca: DLM H5 executive core — requester-side GRANT RECEIVE + deferred-grant report

The cross-node contention semantics (#873) proved block-then-grant on a single
real /dev/vms. This adds the two executive halves the async-reply SCS wire needs:

- MASTER side: vms_lock_dlm_xnode_deq now REPORTS the queued cross-node waiter
  that a release flipped to granted (its requester CSID + original req_lkid +
  master handle + granted mode), through the fields a DEQ otherwise leaves 0, so
  the daemon can WIRE a deferred GRANT to the requester without a second probe.
  The master lock entry now carries the requester's own lock handle (req_lkid).

- REQUESTER side: VMS_DLM_OP_GRANT is implemented (was SS$_UNSUPPORTED). A GRANT
  / queued-reply the master sent back completes an executive-resident ORIGIN
  record — the requester-side proxy of the outstanding request, on its own list,
  never touched by the local lock manager. Its granted mode is set ONLY from what
  the master genuinely sent over SCS: a queued-reply (mode NL) leaves it pending,
  a deferred GRANT (mode EX) flips it NL->EX. GETLKI falls through to it, so the
  status flip is observable on the REQUESTER node. INV-6: genuine executive
  state, no fabricated grant.

BLKAST as a receive op stays SS$_UNSUPPORTED — the BLKAST wire is deferred
honestly on this rung (the block-then-grant round-trip is proven without it).

test_syssvc_dlm_xnode.c extended to prove both halves against a real /dev/vms
(x86_64 + Alpha LP64): the deferred-grant report on the block-then-grant $DEQ,
and the requester-side queued->granted origin flip. vms.ko builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 SCS wire — queued-reply + deferred-GRANT delivery over the live VC

The scsd daemon now carries the async DLM replies over the real SCS wire, so the
block-then-grant completes on the REQUESTER node across the network — not just in
the master's local executive (#873).

Node B (master): the GRANT frame it sends back now carries the granted mode
honestly — a queued-reply carries NL (the requester stays genuinely pending), a
grant carries the granted mode — plus the master's lock handle, so the requester
can $DEQ the holder. On a cross-node $DEQ that flips a queued waiter to granted,
B reads the executive's deferred-grant report and WIRES an unprompted deferred
GRANT (SS$_NORMAL, the granted mode) to that requester (SCSD-I-DLMDEFER).

Node A (requester, armed by OVMX_DLM_H5): drives block-then-grant over the wire —
holds RESONE EX (#1), sends a second incompatible $ENQ (#2) that QUEUES on B,
dispatches B's queued-reply into its OWN executive so #2's origin record is
genuinely PENDING (GETLKI->NL, SCSD-I-DLMPEND), releases the holder (#1), and on
B's deferred GRANT dispatches it into its executive — the origin record FLIPS
NL->EX (GETLKI->EX, SCSD-I-DLMH5FLIP). The status flip is read from A's real
executive, driven only by what the master sent over SCS. INV-6: no fabricated
wire reply or grant; the BLKAST wire stays deferred (the holder releases on its
own). scsd builds clean; vmsscs unit tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 two-node QEMU harness + CI job — block-then-grant over the wire

Extends the H4 two-node real-/dev/vms harness to H5. Both nodes boot vms.ko and
complete the VMS$VAXcluster join; node A is armed OVMX_DLM_ENQ=RESONE
OVMX_DLM_H5=1 and drives the block-then-grant sequence over the live SCS wire:
holds RESONE EX, a second incompatible $ENQ QUEUES on node B (B WIREs a
queued-reply -> A's requester origin record PENDING, SCSD-I-DLMPEND), A releases
the holder, node B WIREs the deferred GRANT off that real $DEQ (SCSD-I-DLMDEFER),
and A's origin record FLIPS NL->EX (SCSD-I-DLMH5FLIP granted_mode=EX).

H5 PASS asserts the status flip on the REQUESTER node A across the wire, plus B's
deferred-grant wire — read from the nodes' own SCSD logs, never fabricated
(INV-6). New Dockerfile.dlm-harness-h5 + init_dlm_h5.sh + run_dlm_harness_h5.sh;
new dlm-harness-h5 CI job (path-filtered on the DLM sources + harness files); the
existing dlm_harness filter already covers vms_lock.c + src/vmsscs/**. Shell +
YAML syntax validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 — record the async-reply wire + requester-side completion in the compat ledger

docs/compat/facilities/cluster-dlm.yaml (single-ledger source, INV-LEDGER): new
cluster-dlm$async-reply-wire item for the H5 rung — the master WIREs the
queued-reply + deferred GRANT over SCS, the requester-side GRANT RECEIVE completes
an executive-resident origin record, and the NL->EX flip is observed on the
requester. Notes the BLKAST wire deferred honestly and multi-peer deferred-grant
routing as a later rung. Summary updated; H5 evidence points at the two-node
harness. YAML validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: regenerate compat surface register for the H5 async-reply-wire item

Derived docs/compatibility-surface.md re-rendered from docs/compat/*.yaml via
tools/compat/render_compat.py (INV-LEDGER: yaml is source, md is generated).
407 items now catalogued.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: quote the job name with a mid-value colon — repairs ci.yml parse (dark since #850)

The job name added today by #850 (vms-dec) contained an unquoted mid-value colon
('... before Username: (vms-dec)'), which is invalid YAML. GitHub Actions could
not parse ci.yml, so EVERY run of it — on main and every branch — completed with
ZERO jobs (no Build & Test, no harnesses, nothing); the only checks still firing
were the separate Ledger-drift and CodeQL workflows. Quoting the value restores
the whole workflow. Pre-existing project-wide breakage, surfaced while wiring the
H5 CI job (which could not run until ci.yml parsed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: register the H5 client sender in the send-site census + repair H0's stale selftest op

Two fixes surfaced by re-enabling ci.yml:

1. scs_send_sites census: the new scsd_dlm_client_send_op() sender (node A's
   contending $ENQ + holder $DEQ) is added to the CHOKED half of the SEND SITE
   TABLE in scsd.c; the scsd_dlm_srv_msg_input entry now notes the deferred GRANT
   it also wires. test_scsd_send_sites: 109 checks, 0 failures.

2. DLM Harness H0 selftest: it drove a DEQ expecting SS$_UNSUPPORTED (2296), but
   rung 3 (#873, vms-904c) made DEQ real — a DEQ of an unknown handle now returns
   SS$_IVLOCKID (8484), so H0 had been red since #873 (hidden by the dark CI). The
   side-effect-free "executive reached, honest 2296" probe now uses BLKAST, whose
   receive op still honestly returns SS$_UNSUPPORTED (its wire is deferred, vms-6ca)
   and mutates no lock state. No run/init/ci grep change needed — still rc=2296 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 30, 2026
…VAX-lane guardrail catch)

VAX-lane cross-review of 269d662 flagged a guardrail-2 hole: the SHOW CPU MP-STATE
mask used '.*', which matches an EMPTY value -- so a future hollow "Multiprocessing
is " (label present, state BLANK) would pass the mask, unlike the model mask (bounded
by the required ' (Series|system)$' suffix) and the CPU-list masks (bounded by
'[#0-9 ]+'). Not a current fidelity issue (OVMX prints a real ENABLED/DISABLED, and a
MISSING line still reds) -- a regression-protection hole a future empty MP-state would
slip. Fix: '.*' -> '.+' so a blank MP-state reds like the other two masks, making the
present+non-empty (anti-hollow) property UNIFORM across all three cpu masks.

Adds the matching selftest case (mirrors guardrail 3): a '.+'-terminated value mask
matches a present MP-state (ENABLED -> MATCH) but keeps a BLANK one RED -- a future
regression back to '.*' fails this test. diff_surface selftest 14/14; show-cpu still
MATCHes with the '.+' mask (acceptance leg re-passes identically, OVMX's MP-state is
non-empty). Structure VAX already OK'd (#1 anti-facade, #3 note()-driver) unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 30, 2026
…nuous oracle golden-diff gate (part B) (#952)

* vms-c38 part B: wire the DCL/SHOW acceptance battery to diff_surface — continuous oracle golden-diff gate

The gate itself: dcl_acceptance_battery.sh now runs a golden_diff over the 5 core
SHOW-family goldens (MEMORY/SYSTEM/CPU/DEVICE/PROCESS), upgrading the piecewise
must_haves to a continuous whole-layout diff against the real-VMS oracle.

Two required proofs, per surface, inside the battery:
- GREENS: run the surface's OWN commands (self-contained -- the battery ran
  'SHOW DEVICE DKA0:' not the golden's 'SHOW DEVICE D', so golden_diff runs the
  golden's exact commands), capture $SEG, and diff_surface -> MATCH (modulo the
  surface's grounded MAY_OMIT).
- REDS: a golden_diff_negctl injects a divergence into the SAME output and asserts
  it does NOT MATCH -- proving the gate can actually fail (not vacuously green).

diff_surface strip_console: the oracle golden ("$ CMD\n<out>", capture_oracle's
prompt-prefixed echo, no trailing prompt) and run_cmd's $SEG ("CMD\n<out>\n$ ",
bare echo + returned prompt) frame the console differently; strip_console drops
the command-echo + bare-prompt lines from BOTH so the gate compares the OUTPUT
LAYOUT, not console framing. Validated locally: a simulated run_cmd $SEG
faithful-subset -> MATCH; an injected divergence -> FORMAT-DIVERGENT.

Builds on part A (diff_surface MAY_OMIT, #951 -- stacked until it reaps, then
rebased to main). selftest 8/8 still green. The GREENS proof + any ADDITIONAL
grounded MAY_OMIT (Dynamic Memory / Paging File, only if substrate-absent) are
CI-verified on the real OVMX boot -- multi-round expected, never a MAY_OMIT added
just to turn a red green (INV-6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-c38 round 2: mount the oracle tooling into the acceptance container so golden_diff can find it

Round-1 CI reds were NOT a GREENS content mismatch -- all 5 golden_diff calls
failed "surface/tooling not found". Root cause: the acceptance test runs in a
Docker container where run_dcl_acceptance_e2e.sh mounts ONLY /test.sh and
/lib/dcl_acceptance_battery.sh -- tools/oracle (diff_surface + surfaces) and the
goldens are not present inside, so the battery's repo-relative _ORACLE_DIR
resolved empty.

- run_dcl_acceptance_e2e.sh: mount tools/oracle -> /oracle/tools/oracle and
  docs/oracle/golden -> /oracle/docs/oracle/golden (a repo-root-like /oracle
  prefix so diff_surface's own HERE/REPO/GOLDEN_DIR path math resolves), and pass
  OVMX_ORACLE_DIR=/oracle/tools/oracle.
- dcl_acceptance_battery.sh: _ORACLE_DIR = ${OVMX_ORACLE_DIR:-<repo-relative
  fallback>} so the container uses the mount and a local checked-out-tree run
  still uses the relative path.

selftest 8/8 + golden-self MATCH unchanged (the tooling is untouched; only its
availability inside the container). This unblocks the actual GREENS proof --
whether OVMX's output MATCHes each golden -- for the next CI round.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-c38 round 3: log the golden_diff FORMAT-DIVERGENT diff for diagnosis

Round 2 (oracle mount fixed) ran the REAL gate: all 5 REDS-negctl PASS (the gate
can fail -- REDS proof done), and all 5 GREENS are FORMAT-DIVERGENT. All-five
diverging is a systematic cross-system tell (not per-surface substrate-omission),
but the battery only logged the classification, not the diff. This logs the full
diff_surface output (normalized golden < vs OVMX >) on a red so the exact
diverging line/section is diagnosable -- to tell a grounded substrate-absent
omission from a value-width/machine-string difference from a real gap, before
choosing the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-c38: structure_norm — cross-system value-tolerant compare (mechanism, selftest 12/12)

The conductor-ruled Option-1 core (2026-08-30): a byte-exact column-geometry gate
is impossible cross-system (OVMX's values legitimately differ from the VAX/Alpha
oracle: wider numbers, different machine strings). structure_norm proves STRUCTURAL
fidelity -- same sections/labels/headers/field-structure, value-tolerant -- via
three symmetric transforms applied to BOTH golden and OVMX after MAY_OMIT/
strip_console: (1) grounded per-surface MACHINE_MASK, (2) collapse-digit-RUN->one
token, (3) whitespace-normalize. Guardrails (selftest): a HOLLOW numeric field
(blank, no digits) STILL reds; a MACHINE_MASK'd field that is blank/absent STILL
reds -- a mask means "value varies," never "ignore the field" (INV-6).

Mechanism only; per-surface MACHINE_MASK values await the conductor's sign-off on
the round-3 diff finding (the divergence is MIXED: value/machine-string AND real
structural fidelity gaps that must stay red). NOT pushed until that sign-off.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-c38: Option-A standing gate — hard-gate CLAIMED-FAITHFUL, report-only the rest; graduate show-cpu

Conductor ruling 2026-08-30 (Option A). Round-3's diff proved the cross-system
divergence is MIXED — value/machine-string AND real structural fidelity gaps — so
normalization alone cannot green all 5, and masking the real gaps green would be the
exact INV-6 allowlist-cheat. Structure the standing gate in two honest tiers:

- HARD-GATE only CLAIMED-FAITHFUL surfaces (a divergence FAILS the leg =
  regression-proof). vax-show-cpu graduates here: it is genuinely structurally
  faithful — the model line, the "Multiprocessing is ..." state, and the
  Active/Configured CPU-ID list are machine-varying VALUES (masked via grounded,
  label-preserving MACHINE_MASK, DCL-Dictionary-pinned per src/vmsdcl/dcl_cmd_show.c);
  the labelled structure MATCHes the oracle through the full pipeline (proven, not
  masked-to-hide-a-gap). (Refutes the round-3 "Active-CPUs extra token = gap"
  sub-hypothesis: "## ##" is the faithful 2-CPU ID list, not a bug.)
- REPORT-only the not-yet-faithful surfaces (new note() primitive: loud, logged,
  routed to a fidelity item every run, but NO PASS/FAIL touch). Round-3 findings:
  vax-show-memory HOLLOW (omits Dynamic Memory + Paging File sections; OVMX has
  pool+pagefile) -> vms-352; vax-show-system HOLLOW (omits State/Pri/I/O columns)
  -> vms-6b8e; vax-show-device MISSING (%NOSUCHDEV, device-name model) -> vms-ddc
  (+vms-9f5); vax-show-process HOLLOW (omits Terminal/Base priority/Devices
  allocated; UIC not resolved to [SYSTEM]) -> vms-1f7. These are TRUE findings the
  gate exists to drive (vms-050 backlog); each graduates to hard-gate when its item
  lands and it genuinely MATCHes. This tracks + names every gap loudly (anti-LARP),
  it does not silently pass them — and it can't permanently-red main's green-by-SHA.

GREENS proof = show-cpu greens through the full pipeline (hard-gated) + the
diff_surface selftest MATCH cases. REDS proof = the show-cpu negctl (an injected
divergence does NOT MATCH) + the selftest, both retained. Register (docs/compat/
ux-surface-register.md) records the structure-tolerant bar + each surface's honest
status. No brittle per-defect red-set .tsv (vms-49f).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-c38: tighten MP-STATE mask .* -> .+ so a blank value still reds (VAX-lane guardrail catch)

VAX-lane cross-review of 269d662 flagged a guardrail-2 hole: the SHOW CPU MP-STATE
mask used '.*', which matches an EMPTY value -- so a future hollow "Multiprocessing
is " (label present, state BLANK) would pass the mask, unlike the model mask (bounded
by the required ' (Series|system)$' suffix) and the CPU-list masks (bounded by
'[#0-9 ]+'). Not a current fidelity issue (OVMX prints a real ENABLED/DISABLED, and a
MISSING line still reds) -- a regression-protection hole a future empty MP-state would
slip. Fix: '.*' -> '.+' so a blank MP-state reds like the other two masks, making the
present+non-empty (anti-hollow) property UNIFORM across all three cpu masks.

Adds the matching selftest case (mirrors guardrail 3): a '.+'-terminated value mask
matches a present MP-state (ENABLED -> MATCH) but keeps a BLANK one RED -- a future
regression back to '.*' fails this test. diff_surface selftest 14/14; show-cpu still
MATCHes with the '.+' mask (acceptance leg re-passes identically, OVMX's MP-state is
non-empty). Structure VAX already OK'd (#1 anti-facade, #3 note()-driver) unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 31, 2026
… + HELLO tx/rx + adjacency drive (Option B, Rule-1-hidden) (#997)

Operator ruling 2026-08-31 (rd vms-a1c): the DECnet engine is Option B — a
userspace NSP/routing engine over AF_PACKET SOCK_RAW, forking the proven
src/vmsscs/scsd.c raw-Ethernet datalink, NOT an in-kernel AF_DECnet
forward-port. Rung 1 (rd vms-449d) lands the engine that MOVES FRAMES using the
three landed engine-agnostic codecs.

New: src/vmsdecnet/engine/
- dnet_engine.{c,h}: the socketless engine core (pure logic over frame buffers
  + an injected clock, same discipline as the codecs). Binds the HELLO codec +
  the adjacency state machine into a live routing endnode: builds the full
  on-wire endnode-HELLO frame, drives the T3 emission cadence and the listen
  timer, consumes received frames, and owns the VMS-faithful presentation
  surface (executor / circuit / SHOW ADJACENT NODES). Touches NO socket.
- decnetd.c (DECNETD.EXE): the daemon — the DECnet analogue of SCSD.EXE and the
  ONLY place the raw datalink (scs_datalink_{open,send,recv}, the generic
  raw-L2 abstraction written engine-agnostic for exactly this second consumer)
  is touched. Fail-honest if the netdev can't be opened (INV-6). A --self-test
  proves tx/rx/adjacency with no CAP_NET_RAW (DECnet analogue of scsd
  --dlm-selftest); --show-executor reports identity without a socket.

Rule 1 — the AF_PACKET socket and the Linux interface name are HIDDEN behind
the DECnet routing surface an NCP user sees (executor node area.node + State =
on, a circuit over the datalink device, SHOW ADJACENT NODES), exactly as scsd
hides its SCA socket behind SCS. INV-0: identification is OVMX-branded, never
"DECnet for OpenVMS".

Proven three ways:
- ORACLE BYTE-EXACT: the engine's endnode-HELLO for node 1.1 is byte-identical
  to the vms-3be VAX capture (register §4.6 specimen #1), incl the rtr-0.0
  neighbour field encoded as aa:00:04:00:00:00 (tests/vmsdecnet/test_dnet_engine.c).
- REAL SEND/RECV: two engine instances drive DOWN→INITIALIZING→UP→DOWN over a
  real socketpair(2) and via DECNETD.EXE --self-test.
- LIVE AF_PACKET on-wire: two DECNETD.EXE endnodes over a veth pair in an
  isolated netns exchanged 60-byte HELLOs on the T3 cadence and each listed the
  other in SHOW ADJACENT NODES (frames_dropped=0).

Clean-room (Rule 8): wire form from public DNA Phase IV + the vms-3be lab
capture + OVMX's own scsd datalink pattern. No VSI/HPE source.

Decision recorded in docs/decnet-provenance-register.md §6.0. Deferred to
children of vms-30e: NSP logical-link connection service (rd vms-c23) and the
live-VAX oracle adjacency bracket (rd vms-aac0, the §4.4 done-bar).


Claude-Session: https://claude.ai/code/session_01V2jrHU9fdTTfKhB5HBQNQH

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Sep 2, 2026
… coordinator (vms-74f)

Layer 3 (faithful cluster DLM registration, vms-3eb) frame builder #1:
scs_member_build_dlm_reg_enq -- the cat 0x02 op 0x01 ENQ that registers one of
OVMX's REAL standing system locks (the F11B$v<label> volume lock a MOUNT holds,
Layer 1) to the COORDINATOR during a directory rebuild.

Unlike db20-b's NL-only presence ENQ (which zeroed the id fields because it held
nothing), this carries OVMX's OWN real values, at the reference-confirmed offsets
(conductor trace of F11B$aSYSDSK1 op-01):
  - req_lkid @ body[4:8]  = OVMX's real local lock handle (from the vms-1f4
    DLM_ENUM_STANDING accessor -- a lock the executive genuinely holds);
  - mst_csid @ body[20:24] = the real coordinator's csid;
  - resname @ body[48], NL mode @ body[30] (the mode OVMX genuinely holds).
Built on db20-b's VALIDATED frame (VAX1 granted 48/48). INV-6: only OVMX's own
real values; the ungrounded per-lock lock-mgmt fields (body[24:30]) stay ZERO --
VAX3's kernel-assigned handles are un-replayable and inventing them would be the
fabrication trap inverted (conductor's principle -- the coordinator keys on the
resource + requester identity, not a foreign handle match).

test_scs_member: new test_dlm_reg_enq_carries_real_values pins req_lkid@[4:8],
mst_csid@[20:24], cat/op, NL mode, resname, and the ungrounded-fields-zero
guarantee. Member suite ALL PASSED.

Next: op-04/op-03 real-handle builders + the await-grant receive FSM (scsd calls
the accessor, sends op-01 per lock, and on the coord's cat-82 op-01 grant ARRIVAL
-- a pure trigger, no value extracted -- sends op-04 -> op-03 with OVMX's own
handles).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
baron-3dl added a commit that referenced this pull request Sep 8, 2026
…in fixes; strip diagnostics

Rung 4 of the vms-b4f ladder. Boots the CRTL->RMS veneer-wired alpha-dec-vms
GCC-port image on qemu-system-alpha + the real /dev/vms executive: its
decc$fopen -> ovmx_crtl veneer -> sys$create -> LIBVMSRMS$SHR -> ioctl(/dev/vms)
-> Files-11 ACP writes PORTTEST.DAT, then an INDEPENDENT reader (DCL
DIRECTORY/FULL, a different accessor than the writer's own CRTL/RMS handle)
asserts PORTTEST.DAT;1 landed on the real ODS-2 volume with a genuine File ID
AND the full 8192-byte content (16 blocks) -- something a ramfs/POSIX write can
never produce in the ACP directory. The gate keys on that fid+content landing
(strictly stronger than a same-CRTL round-trip a ramfs satisfies), with a 7/7
can-fail selftest.

Three first-exercise toolchain bugs the forcing function exposed:

- #1 emutls control-object width (LLP64): unsigned long is 32-bit on
  alpha-dec-vms, so __emutls_object {size,align,loc,templ} packed loc at offset
  8 instead of 16 and __emutls_get_address returned 4 -> SIGSEGV. emutls_word
  widened to unsigned long long. (committed earlier as e2c6cf5)

- #2 DECC$SHR symbol-vector index skew (mk_decc_shr.sh): the veneer pass dropped
  the 4 fopen/fwrite/fread/fclose entries from the middle of the sorted vector
  and re-appended the aliases at the tail, shifting every higher sv# down by 4.
  IMGACT binds cross-image imports BY INDEX, so producers linked against the
  bootstrap DECC dispatched e.g. decc$strlen[sv#414] to decc$strspn at runtime
  (NULL-arg SIGSEGV). Rewrite the 4 entries IN PLACE at their sorted slot,
  restoring the append-only sv# stability the recipe already documented.

- #3 calloc weak-override reloc (link.c): the vms-430 strong-over-weak
  base-redirect matched a section-relative reloc's section BASE before the
  addend was added, so every sibling symbol in a $CODE$ whose offset-0 proc is
  an overridden weak def (calloc.o's __malloc_allzerop) was pulled onto the
  strong def + addend -- decc$_calloc64 (real calloc at $CODE$+0x008) mis-bound
  onto strong __malloc_allzerop+0x008. Match base+addend and consume the addend
  on a hit; the symbol-target path is byte-identical. run_muldef_evax.sh green
  (incl. weak-first + strong-first self-bind redirect).

All diagnostic scaffolding stripped (IMGACT SIGSEGV handler / IMGACT-MAP probe /
qemu -d int injection) for a clean production activator.

The writer program's post-commit mallocng cleanup crash (free -> free_group ->
free(g->mem) hitting get_meta's `assert(meta->mem==base)` with a NULL group
meta) is a separate mallocng-group-release issue on the alpha-dec-vms substrate,
tracked as bug #4 (blocks vms-fd1); it fires AFTER the content commits and does
not affect the proven landing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ
baron-3dl added a commit that referenced this pull request Sep 9, 2026
…fc LLP64 width fix) (#1063)

* vms-f49: rung 4 — un-fakeable ODS-2 independent-reader proof + vms-1fc LLP64 width fix

Lands the MILESTONE of the vms-b4f ladder: prove the alpha-dec-vms GCC-port
image's CRTL fopen genuinely writes to a real Files-11 ODS-2 volume over the
executive ACP, not musl-ramfs — the anti-fabrication payoff (INV-6). Two
coupled items in one PR, because the width fix is VALIDATED by the proof.

PART A — vms-1fc (LLP64 syscall width fix). On the alpha-dec-vms C model
`long`/`unsigned long` are 32 bits while pointers are 64 (LLP64), so the
libvmssys raw-syscall path truncated every pointer argument to the /dev/vms
transport — the ioctl(/dev/vms, ...) RMS-over-ACP write landed on a garbage
address and reached nothing.
  - vms_syscall.h: widen __vms_syscall0..6 params + return to a guaranteed-
    64-bit `vms_reg_t` (== long long), and the vms_sys_* pointer casts with it;
    widen vms_sys_ioctl's `arg` param from `unsigned long` to vms_reg_t.
  - arch/alpha/syscall_vms.c: match the widened prototypes (long long).
  - kif_transport_linux.c: cast the request-block pointer through vms_reg_t,
    not `unsigned long` — THIS is the /dev/vms pointer the proof exercises.
  - vms_bgsock.c: widen its ioctl pointer casts to match.
  No-op on the LP64 targets (x86_64/aarch64/alpha-linux-gnu: long long == long,
  byte-identical codegen); the actual fix only on alpha-dec-vms. VAX is
  untouched by construction — it takes the __NetBSD__ branch
  (arch/vax/vms_syscall_netbsd.h) and compiles none of these declarations.

PART B — vms-f49 (rung 4, the un-fakeable gate). New `crtl-rms-veneer-gate`
mode of run-module-gp-activation-alpha.sh boots the veneer-wired crtl_rms port
image (JOINT_CRTL_RMS_VENEER=1) on the real /dev/vms + qemu-system-alpha; its
decc$fopen -> the crtl_rms_stdio veneer -> sys$create/$put -> LIBVMSRMS$SHR ->
ioctl(/dev/vms) -> ACP writes PORTTEST.DAT. Then an INDEPENDENT reader — DCL
DIRECTORY/FULL, a DIFFERENT accessor than the writer's CRTL/RMS handle, running
its own sys$search over the ACP directory — asserts PORTTEST.DAT;1 exists on the
ODS-2 volume with a genuine ODS-2 File ID that a ramfs write cannot produce.
  - SYSTARTUP_VMS_VENEER_PROOF.COM: RUN JOINT_E2E, then DIRECTORY/FULL PORTTEST.DAT.
  - build-alpha-bootimage.sh: stage LIBVMSRMS$SHR.EXE into SYS$SHARE and swap in
    the veneer-proof SYSTARTUP when a veneer build is present (keyed on the
    shareable), verify it on the mastered volume.
  - assert_veneer(): gates on the independent File-ID reader, NOT console/CRTL
    state; a can-fail selftest proves teeth incl. the NEGATIVE/REJECTION case —
    a same-CRTL success that ramfs satisfies (%DIRECT-W-NOFILES) must FAIL.
  - ci.yml: new PR job alpha-crtl-rms-veneer (alpha_activation scope, 150m). The
    non-veneer alpha-crtl-rms-n7 gate stays green as the control.

Local build+link verified: the veneer graph links zero-deferred under the
alpha-dec-vms cc1 with the width fix (decc$fopen->DECC$SHR, veneer
sys$create/$put/$get->LIBVMSRMS$SHR), LIBVMSRMS$SHR.EXE emitted; assert_veneer
selftest passes all six fixtures. The qemu-alpha runtime proof runs in CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: fix apostrophe quote-break in build-alpha-bootimage.sh staging block

The vms-f49 staging comments landed inside the assemble `docker run ... bash -c
'...'` SINGLE-QUOTED block with apostrophes ("image's"), which closed the quote
mid-body and exposed `decc$fopen` to the outer shell -> `line 91: fopen: unbound
variable` under `set -u`. This broke the boot-image assembly for EVERY alpha
activation gate that calls assemble_boot_image (gate/crtl-rms-gate/mf-gate/
crtl-rms-veneer-gate), before any qemu boot -- NOT a Part A regression (all three
reds died at the identical line-91 quote-break after "step 1 staged", pre-boot;
Part A links clean, proven by the green alpha RMS-substrate STRICT-link + DECC$SHR
jobs). The script's own header warns "no apostrophes in this block". Reworded the
two added comments apostrophe-free; verified the docker -c body now has balanced
single-quotes with zero outer-shell `$` exposure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: stage the full RMS producer graph + fix its DECC producer name (rung-4 activation)

The rung-4 veneer gate booted this time (N=7 control PASSED -> Part A width fix
does NOT regress alpha activation) but the veneer image RUN drew
%IMGACT-F-IMGNOTFND: LIBVMSRMS$SHR is not self-contained. It transitively imports
from the whole executive producer graph (LIBVMS/LIBVMSFS/LIBVMSLNM/LIBVMSPROCESS/
LIBVMSSYS$SHR) AND recorded its DECC producer as the phantom pass-1 bootstrap name
"DECC1$SHR.EXE" -- neither on SYS$SHARE, so IMGACT could not resolve them.

Two in-scope Part-B wiring fixes (no new executive facility):
 - build-joint-image.sh: build the pass-1 bootstrap DECC under $WORK/p1 with the
   BASENAME DECC$SHR.EXE (not DECC1$SHR.EXE). LINK records producers by basename,
   so the graph + LIBVMSRMS$SHR now record "DECC$SHR.EXE" and, at activation,
   IMGACT name-keyed binding resolves them against the SINGLE staged pass-2
   (veneer) DECC$SHR.EXE (GSMATCH LEQUAL). One DECC$SHR at runtime, no duplicate
   musl C-RTL. Also emit the whole producer graph to OUTDIR.
 - run-module-gp-activation-alpha.sh + build-alpha-bootimage.sh: stage the full
   graph (LIBVMSRMS/LIBVMS/LIBVMSFS/LIBVMSLNM/LIBVMSPROCESS/LIBVMSSYS$SHR) into
   SYS$SHARE and verify each on the mastered ODS-2 volume.

Also fixes a second single-quote apostrophe break ("IMGACT's", and 'DECC$SHR.EXE'
exposing $SHR) in the build-joint-image.sh docker bash -c block.

Local verify: veneer graph links zero-deferred; LIBVMSRMS$SHR + all 7 sibling
shareables now record DECC$SHR.EXE (grep DECC1 = 0 across the whole staged set);
full graph emitted to OUTDIR. Runtime activation runs in CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: surface the guest-kernel faulting PC in the veneer-gate failure output

The veneer image now activates (producer graph staged) but SIGSEGVs
(%DCL-F-ABORT signal 11) in the first-ever runtime execution of the alpha RMS
substrate over the veneer. The Alpha guest kernel prints the faulting user
PC/RA/VA to the console at fault time, but the gate's fixed pattern-grep never
surfaced it. Dump the guest fault-signature line(s) + the last 60 console lines
in the veneer-gate FAIL path so the authoritative fault PC is captured in CI
(disk-safe -- no qemu -d flags; the guest kernel already emitted it). This
localizes the crash so it can be fixed. Not a gate weakening -- failure path only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: capture qemu CPU-exception log to pin the veneer SIGSEGV faulting PC

The previous fault-capture confirmed the veneer image crashes at/near ACTIVATION
(no OVMX-CRTL-RMS sys$create trace precedes the %DCL-F-ABORT signal 11), and the
Alpha guest kernel prints no userspace fault line. So add qemu exception logging
(-d int,cpu_reset,guest_errors -D /work/qint.log), enabled ONLY for the
crtl-rms-veneer-gate via QEMU_DBG, and dump the last exceptions (faulting PC/VA)
in the veneer FAIL path. Disk-safe: the boot reaches Username: within ~30-60s so
qint.log stays small; other gates pass QEMU_DBG empty (unchanged). This is the
gdb-equivalent authoritative fault PC needed to decide RMS-substrate truncation
(hyp 1) vs IMGACT mutual-producer-cycle activation crash (hyp 2, the leading
hypothesis: the veneer DECC$SHR<->LIBVMSRMS$SHR cycle the non-veneer control lacks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: filter qemu exception log (clk-interrupt firehose hid the fault)

The -d int log is dominated by clk_interrupt; the veneer SIGSEGV's Dfault/MMFAULT
exception with the faulting user pc= is buried. Filter out clk/dev interrupts and
add an exception-type histogram so the fault exception + PC is surfaced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: IMGACT-MAP producer base logging to resolve the veneer fault PC

The qemu -d int log localized the veneer SIGSEGV to a repeated mmfault loop at
user pc=0x12005eb00 (and 0x12005a7f4). IMGACT is only ~37KB so the fault is in a
mmap'd producer (likely DECC$SHR, which holds the veneer ovmx_crtl_fopen). Print
each producer's runtime base (IMGACT-MAP: <name> base=0x..) at load so the
faulting pc can be resolved to <image>+offset and then to a symbol. Diagnostic
only; other gates grep their own patterns so the extra lines are inert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: log MAIN-EXE + IMGACT-INTERP bases to place the 0x120000000 fault region

Producer bases are all 0x20000xxxxx, but the veneer fault PCs cluster in the
0x120000000 range (0x12005eb00 repeated 18x = the unrecoverable fault; other
single-hit pcs are benign TLB fills). Log the kernel-mapped main-exe bias and the
PT_INTERP (IMGACT) base so the faulting region can be attributed to the main
image, the interp, or an unmapped bad-jump target (mis-resolved cross-image
linkage = hyp 2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: gate IMGACT-MAP behind OVMX_IMGACT_MAP=1 (silent by default)

Housekeeping: the IMGACT-MAP producer-base logging now emits only when the boot
cmdline carries OVMX_IMGACT_MAP=1 (kept for fault-localization, silent in
production). run_boot_a gains an optional QEMU_APPEND injection; the veneer gate
sets QEMU_APPEND=OVMX_IMGACT_MAP=1 so it still gets the map. The QEMU_DBG -d int
fault capture is retained (inert for other gates).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: Option-1 probe — log wild (0x120000000-region) import bindings in IMGACT

The veneer SIGSEGV jumps to 0x120000000+offset (default/stack-top base) while all
images map at 0x200_xxxx_xxxx; the value is COMPUTED at runtime (not stored). Add
a gated (OVMX_IMGACT_MAP=1) probe in bind_imports that logs any binding whose
resolved PV or filled code entry *(PV+8) lands in the wild region -- with the
importing image, the symbol, the cell, the PV, and the entry. A wild PV isolates
an SV-value fault; a sane PV with a wild entry isolates a producer PDSC-entry
rebase fault. Surface IMGACT-WILD/IMGACT-MAP in the veneer-gate failure dump. If
nothing fires, the wild target is code/GP-computed (not a linkage fill) and the
next step is a register (RA) capture. Probe only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: correct the Option-1 probe wild-region range (0x1_xx, not the 0x200xxx image region)

The probe flagged all valid 0x200_xxxx_xxxx bindings as wild because the upper
bound (0x200000000000) sat above the real image region. Narrow it to
[0x1_0000_0000, 0x100_0000_0000) -- the 0x120000000 default/stack-top region --
so only genuinely-wild values fire. Confirmed against the CI run: with the correct
range NO binding is wild (all PVs/entries resolve to 0x200xxx), proving the veneer
SIGSEGV is NOT a linkage fill but a wrong-base code jump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: fix emutls control-object width (LLP64) — the veneer rung-4 crash root cause

The alpha emulated-TLS runtime typed the control-object fields as `unsigned long`
(emutls_word), which on the alpha-dec-vms LLP64 target is 32 BITS — but the cc1
emits the control with 64-bit .quad fields (.quad size; .quad align; .quad loc;
.quad templ). So struct __emutls_object packed size+align into the first 8 bytes
and put `loc` at offset 8 (the align field) instead of 16. __emutls_get_address
returned obj->loc = the align value (4), and the first __thread access on the
veneer's sys$create path (a vms_kif 'vms_bound_pid == getpid()' check) then
dereferenced 4 -> SIGSEGV at 0x4 — the rung-4 (vms-f49) blocker, pinned via a
local qemu-system-alpha boot + an IMGACT SIGSEGV-handler RA capture.

Fix: emutls_word -> unsigned long long (64-bit on every target), so loc lands at
offset 16 and templ at 24, matching the .quad emission. Same LLP64 bug class as
vms-1fc. Arch-scoped: the whole file is #if defined(__alpha__), so x86_64/aarch64
(musl TLS) and VAX (NetBSD) never compile it — the non-veneer + VAX/x86_64
controls stay byte-identical.

Verified locally: the v0=4 / gp-as-stack SIGSEGV is gone; the veneer image now
advances past the emutls point into the Files-11 path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-f49: un-fakeable ODS-2 landing proof (rung 4) + bug #2/#3 toolchain fixes; strip diagnostics

Rung 4 of the vms-b4f ladder. Boots the CRTL->RMS veneer-wired alpha-dec-vms
GCC-port image on qemu-system-alpha + the real /dev/vms executive: its
decc$fopen -> ovmx_crtl veneer -> sys$create -> LIBVMSRMS$SHR -> ioctl(/dev/vms)
-> Files-11 ACP writes PORTTEST.DAT, then an INDEPENDENT reader (DCL
DIRECTORY/FULL, a different accessor than the writer's own CRTL/RMS handle)
asserts PORTTEST.DAT;1 landed on the real ODS-2 volume with a genuine File ID
AND the full 8192-byte content (16 blocks) -- something a ramfs/POSIX write can
never produce in the ACP directory. The gate keys on that fid+content landing
(strictly stronger than a same-CRTL round-trip a ramfs satisfies), with a 7/7
can-fail selftest.

Three first-exercise toolchain bugs the forcing function exposed:

- #1 emutls control-object width (LLP64): unsigned long is 32-bit on
  alpha-dec-vms, so __emutls_object {size,align,loc,templ} packed loc at offset
  8 instead of 16 and __emutls_get_address returned 4 -> SIGSEGV. emutls_word
  widened to unsigned long long. (committed earlier as e2c6cf5)

- #2 DECC$SHR symbol-vector index skew (mk_decc_shr.sh): the veneer pass dropped
  the 4 fopen/fwrite/fread/fclose entries from the middle of the sorted vector
  and re-appended the aliases at the tail, shifting every higher sv# down by 4.
  IMGACT binds cross-image imports BY INDEX, so producers linked against the
  bootstrap DECC dispatched e.g. decc$strlen[sv#414] to decc$strspn at runtime
  (NULL-arg SIGSEGV). Rewrite the 4 entries IN PLACE at their sorted slot,
  restoring the append-only sv# stability the recipe already documented.

- #3 calloc weak-override reloc (link.c): the vms-430 strong-over-weak
  base-redirect matched a section-relative reloc's section BASE before the
  addend was added, so every sibling symbol in a $CODE$ whose offset-0 proc is
  an overridden weak def (calloc.o's __malloc_allzerop) was pulled onto the
  strong def + addend -- decc$_calloc64 (real calloc at $CODE$+0x008) mis-bound
  onto strong __malloc_allzerop+0x008. Match base+addend and consume the addend
  on a hit; the symbol-target path is byte-identical. run_muldef_evax.sh green
  (incl. weak-first + strong-first self-bind redirect).

All diagnostic scaffolding stripped (IMGACT SIGSEGV handler / IMGACT-MAP probe /
qemu -d int injection) for a clean production activator.

The writer program's post-commit mallocng cleanup crash (free -> free_group ->
free(g->mem) hitting get_meta's `assert(meta->mem==base)` with a NULL group
meta) is a separate mallocng-group-release issue on the alpha-dec-vms substrate,
tracked as bug #4 (blocks vms-fd1); it fires AFTER the content commits and does
not affect the proven landing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

* vms-b14: bounded calloc-family exception in the section-relative weak-override — land the alpha CRTL->RMS ODS-2 proof without regressing N=7

The rung-4 branch's earlier link.c change (base+addend full-target match)
regressed the crtl_rms N=7 gate (gated-green at #958/#959): it dropped the
weak-alias thunk redirects the mallocng heap needs. Reverting to the base-only
redirect greened N=7 but crashed the veneer's calloc. Root-caused via
print-fatal-signals PC capture (gdb isn't in the images): TWO oppositely-signed
strong-sibling relocs that no reloc-local field can separate —

  - decc$_calloc64 (real calloc at $CODE$+0x8, past the offset-0 overridden weak
    __malloc_allzerop): base-only wrongly redirects it onto strong
    __malloc_allzerop+0x8 and calloc a_crash()es (veneer's early 0x4c618 crash).
    Must be LEFT.
  - the mallocng syscall/stdio thunks (decc$munmap/mremap/mmap/__syscall_cp,
    decc$fclose, __stdio_close): base-only correctly redirects them to their
    strong def; leaving them wild-jumps into the fork/execve code region (the
    0x4a354 crash shared by N=7). Must be REDIRECTED.

Both are byte-identical in every reloc field (psect/to_section/addend/type/
site-sym/target-sym/weak/overridden/self_ref), so this applies base-only to
every base-coincident section-relative reloc EXCEPT the precise, structurally-
detected calloc case: base-only's redirect would land inside strong
__malloc_allzerop while the reloc's real target is a distinct sibling. Bounded
workaround pending the weak_alias-granularity export-path fix (vms-f59).

Gates: OVMX/Alpha crtl_rms N=7 = clean sentinel 7 ($STATUS=%X0035A039,
port_ok=1); CRTL->RMS veneer = decc$fopen lands PORTTEST.DAT;1 on the ODS-2
volume, File ID (71,1,0) + full 16/16 blocks, confirmed by an INDEPENDENT
DIRECTORY/FULL reader (un-fakeable). Known-tracked: the veneer image still
signal-11s AFTER the write commits (pre-existing under every link variant; in
the RMS-veneer cleanup path, not exercised by the clean N=7 round-trip) — filed
as a follow-up under vms-fd1; the veneer gate proves the ODS-2 landing
independent of that post-commit crash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Sep 11, 2026
… (proven red)

First of the vms-050c Bucket-A paydown: give kernel-core/vms_l2.c a real injected
negative control so it leaves the §1 "executive TU with NO negative control"
FAIL set (39->38), per docs/design/negctl-coverage-paydown.md.

Defect l2-open-bypasses-phy-io: l2_priv_check() (vms_l2.c:116) flips
`bool ok = (cur_privs & VMS_PRV_M_PHY_IO) != 0;` -> `bool ok = true;`, so
VMS_IOCTL_L2_OPEN admits a caller WITHOUT PHY_IO and mints a handle — the
CAP_NET_RAW-class self-authorization the executive exists to refuse. suites_red:
test_syssvc_l2_datalink; require_fail "L2_OPEN without PHY_IO -> SS$_NOPRIV" +
knock_on "L2_OPEN without PHY_IO mints no handle" (both anchored in the suite).

PROVEN (not just anchored — the INV-6 teeth):
- Static (host): facility_defects.sh selftest ok (injects + idempotent
  no-op-reapply teeth), coverage §1 drops vms_l2.c (39->38) + §2 clears
  test_syssvc_l2_datalink; dash -n clean.
- QEMU /dev/vms negctl gate (k3s-worker, run_facility_negctl.sh): positive
  control = all 122 suites green; injected defect turns EXACTLY
  test_syssvc_l2_datalink red on EXACTLY the 2 named assertions, no other suite
  affected -> "PASS: turns the harness red ... exactly the ones the manifest names".

Buckets B/C/D/E and the remaining Bucket-A TUs follow; the durable close
(graduate the report-only §1 gate to blocking) is tracked on vms-050c.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
baron-3dl added a commit that referenced this pull request Sep 12, 2026
… (proven red)

First of the vms-050c Bucket-A paydown: give kernel-core/vms_l2.c a real injected
negative control so it leaves the §1 "executive TU with NO negative control"
FAIL set (39->38), per docs/design/negctl-coverage-paydown.md.

Defect l2-open-bypasses-phy-io: l2_priv_check() (vms_l2.c:116) flips
`bool ok = (cur_privs & VMS_PRV_M_PHY_IO) != 0;` -> `bool ok = true;`, so
VMS_IOCTL_L2_OPEN admits a caller WITHOUT PHY_IO and mints a handle — the
CAP_NET_RAW-class self-authorization the executive exists to refuse. suites_red:
test_syssvc_l2_datalink; require_fail "L2_OPEN without PHY_IO -> SS$_NOPRIV" +
knock_on "L2_OPEN without PHY_IO mints no handle" (both anchored in the suite).

PROVEN (not just anchored — the INV-6 teeth):
- Static (host): facility_defects.sh selftest ok (injects + idempotent
  no-op-reapply teeth), coverage §1 drops vms_l2.c (39->38) + §2 clears
  test_syssvc_l2_datalink; dash -n clean.
- QEMU /dev/vms negctl gate (k3s-worker, run_facility_negctl.sh): positive
  control = all 122 suites green; injected defect turns EXACTLY
  test_syssvc_l2_datalink red on EXACTLY the 2 named assertions, no other suite
  affected -> "PASS: turns the harness red ... exactly the ones the manifest names".

Buckets B/C/D/E and the remaining Bucket-A TUs follow; the durable close
(graduate the report-only §1 gate to blocking) is tracked on vms-050c.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
baron-3dl added a commit that referenced this pull request Sep 12, 2026
… (proven red)

First of the vms-050c Bucket-A paydown: give kernel-core/vms_l2.c a real injected
negative control so it leaves the §1 "executive TU with NO negative control"
FAIL set (39->38), per docs/design/negctl-coverage-paydown.md.

Defect l2-open-bypasses-phy-io: l2_priv_check() (vms_l2.c:116) flips
`bool ok = (cur_privs & VMS_PRV_M_PHY_IO) != 0;` -> `bool ok = true;`, so
VMS_IOCTL_L2_OPEN admits a caller WITHOUT PHY_IO and mints a handle — the
CAP_NET_RAW-class self-authorization the executive exists to refuse. suites_red:
test_syssvc_l2_datalink; require_fail "L2_OPEN without PHY_IO -> SS$_NOPRIV" +
knock_on "L2_OPEN without PHY_IO mints no handle" (both anchored in the suite).

PROVEN (not just anchored — the INV-6 teeth):
- Static (host): facility_defects.sh selftest ok (injects + idempotent
  no-op-reapply teeth), coverage §1 drops vms_l2.c (39->38) + §2 clears
  test_syssvc_l2_datalink; dash -n clean.
- QEMU /dev/vms negctl gate (k3s-worker, run_facility_negctl.sh): positive
  control = all 122 suites green; injected defect turns EXACTLY
  test_syssvc_l2_datalink red on EXACTLY the 2 named assertions, no other suite
  affected -> "PASS: turns the harness red ... exactly the ones the manifest names".

Buckets B/C/D/E and the remaining Bucket-A TUs follow; the durable close
(graduate the report-only §1 gate to blocking) is tracked on vms-050c.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant