Skip to content

SET PROCESS/NAME writes the executive process table, untruncated, and refuses like VMS (vms-fbe) - #46

Merged
baron-3dl merged 4 commits into
mainfrom
work/vms-fbe-r3
Aug 2, 2026
Merged

baron-3dl merged 4 commits into
mainfrom
work/vms-fbe-r3

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

Closes vms-fbeSET PROCESS/NAME writes the process name into per-process memory.

Three rounds. Rounds 1 and 2 were each refused by an adversary; round 3 was verified MERGEABLE by the same adversary that found the round-2 defect.

What lands

SET PROCESS/NAME now writes the executive's process table through the same vms_kif_setprn path $CREPRC uses; ctx->process_name updates on success only, so a refused rename cannot locally claim a name the executive rejected.

tests/qemu/test_syssvc_setname.c is a genuine A-writes / B-reads proof across four separate DCL.EXE processes (Rule 11): one sets its name and stays alive; the executive's own row carries it via vms_kif_procscan — the same reader SHOW SYSTEM uses; a second independent DCL.EXE names it at the right pid; a third is refused for the duplicate; a fourth succeeds once the holder exits.

Round 2 fixed the truncation defect: upname was sized sizeof(ctx->process_name) (16) instead of VMS_PRCNAM_XFER (64), so an oversized name was truncated before it reached the executive and the executive never got the chance to refuse it — the exact defect that constant exists to prevent. The refusal now renders in the oracle's two-line shape (%SET-E-NOTSET / -SYSTEM-F-<ident>) per the VAX V7.3 transcript at src/kernel/vms_ioctl.h:653-658. No constant was self-certified: SS$_DUPLNAM (148) and SS$_IVLOGNAM (340) were oracle-pinned by vms-8019.

Round 3 is test-only — git diff 91b0770..7108c16 -- src/ is empty. Round 2 had shipped a new vacuous assertion of the class it was opened to kill: P6's "name unchanged after refusal" scanned the whole capture, which holds two SHOW SYSTEM outputs, and matched the row printed before the rename. Round 3 cuts the capture at the refusal message and searches only the tail.

Evidence

Rebuilt from a clean git archive, judged by the FINAL RESULTS line and the SUITE banners:

  • Clean: 27 SUITE banners, none non-zero · FINAL RESULTS: 29 suites passed, 0 suites failed · 713 PASS / 0 FAIL · test_syssvc_setname 18/18
  • Mutation FACILITY_DEFECT=proctab-duplicate-name: red set exactly {test_kmod_procnam, test_syssvc_procnam, test_syssvc_setname, test_syssvc_startup_service} — 5 unique FAIL lines matching require_fail(2) + knock_on_fail(3) verbatim; setname 17/1 (P4 only)
  • nm on /initramfs/bin/DCL.EXE confirms vms_kif_setprn is genuinely imported

The P6 fix was settled by execution, not by review — sys_row_for copied verbatim into a standalone harness, round 2 vs round 3 over six inputs:

input r2 r3
faithful capture GREEN GREEN
refusal wiped the name GREEN RED
refusal message absent entirely GREEN RED
round-1 single-line shape GREEN RED
name truncated GREEN RED
control row under another pid GREEN GREEN

Round 2 was green on all four defect shapes — fully vacuous, as charged. The strstr cut returns NULL rather than re-searching from byte 0, so there is no silent fallback.

Known and named, not hidden

  • Hardcoded continuation strings are not derived from run_print_condition() / sys$getmsg — out of scope, left named.
  • test_syssvc_setname.c:423 prose still describes the superseded single-line shape.
  • P6 does not pid-check its row the way P3 does.
  • SET PROCESS/NAME="" is a silent no-op and SET PROCESS/NAME="OVMXFBE!BAD" is accepted — both observed, not oracle-pinned, reported as facts rather than asserted as right or wrong.

Judge CI by its own result: vms-b1f records that tests/qemu/run_tests.sh can exit 1 on a fully green run via pipefail+SIGPIPE, but that is host-sensitive and does not fire on GitHub runners.

🤖 Generated with Claude Code

baron-3dl and others added 4 commits August 1, 2026 18:19
DCL's SET PROCESS/NAME qualifier only ever wrote ctx->process_name, a
struct private to the DCL process that ran the command -- nm on the
built DCL.EXE showed it imported vms_kif_getjpi_self, vms_kif_open and
vms_kif_procscan, and no vms_kif_setprn symbol at all, even though
VMS_IOCTL_SETPRN already exists in the kernel and $CREPRC already uses
it. SET PROCESS/NAME=X then F$PROCESS() read "X" back out of the same
private struct it had just written: a single-process test that passed
perfectly while sharing nothing with any other process (Rule 11).

cmd_set_process() (src/vmsdcl/dcl_cmd_set.c) now calls
vms_kif_setprn() -- the same VMS_IOCTL_SETPRN path $CREPRC already
uses -- and lets the executive decide legality (SS$_IVLOGNAM) and
UIC-group uniqueness (SS$_DUPLNAM), both already oracle-pinned
(vms-8019) and already enforced kernel-side. ctx->process_name is
still updated on success only, so dcl_lexical.c's F$PROCESS() (out of
scope this round; a sibling item owns that file) does not go stale,
and a refused rename does not locally claim a name the executive
refused to record.

tests/qemu/test_syssvc_setname.c proves the property with the
required A-writes/B-reads shape: one DCL.EXE runs SET PROCESS/NAME and
stays alive; the executive's own row (vms_kif_procscan, the same
reader SHOW SYSTEM uses) carries the name; a SECOND, independent
DCL.EXE's SHOW SYSTEM names it at the right pid; a THIRD DCL.EXE's SET
PROCESS/NAME for the same name is refused with the oracle-pinned
%SYSTEM-F-DUPLNAM through DCL's own error path (proven separately from
the kernel-level refusal already covered elsewhere); a FOURTH DCL.EXE
succeeds once the holder exits and the name frees. Reverting the fix
(git stash) turns the suite red; the full run_facility_negctl.sh
proctab-duplicate-name mutation reddens exactly this suite's DUPLNAM
assertion plus the three pre-existing ones, and nothing else --
verified as an exact red-set equality against a real /dev/vms, not
just against a locally reverted fix.

tests/qemu/facility_defects.sh: wires the new suite into the
proctab-duplicate-name negative control (suites_red + knock_on_fail +
knock_on_why), which the manifest's own coverage self-test now
requires for every derived suite.

Full host ctest: 53/53. Full QEMU harness: 27/27 suites, 705/705
assertions. Facility negative controls: 3/3 (coverage, positive
control, proctab-duplicate-name red-set equality).

Duplicate-name refusal status/message were already oracle-pinned by
vms-8019 (SS$_DUPLNAM 148, SS$_IVLOGNAM 340); this item does not
introduce or self-certify any new VMS constant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed name (vms-fbe round 2)

Round 1 fixed the primary defect (DCL now calls vms_kif_setprn() through
the same executive path $CREPRC uses) but introduced a second one: upname
was sized sizeof(ctx->process_name) (16) instead of VMS_PRCNAM_XFER (64),
so an oversized name was truncated to 15 characters BEFORE it ever reached
the executive. The executive then received an already-legal-looking name
and could never refuse it -- a 16-char SET PROCESS/NAME=X "succeeded",
truncated, with no message, where real VMS refuses it outright
(src/kernel/vms_ioctl.h:653-658, VAX1 OpenVMS VAX V7.3 oracle transcript).

Three changes, matching the item:

1. upname is now sized VMS_PRCNAM_XFER, so an oversized name arrives at
   vms_kif_setprn() intact and name_is_valid() (vms_proctab.c) can see it
   is not NUL-terminated within VMS_PRCNAM_SIZE and refuse it.

2. SET PROCESS/NAME's DUPLNAM/IVLOGNAM refusal now renders in the
   oracle's two-line shape -- "%SET-E-NOTSET, error modifying process
   name" followed by a "-SYSTEM-F-<ident>, <text>" continuation line --
   instead of the bare single-line "%SYSTEM-F-DUPLNAM" round 1 guessed
   and was never observed on VAX1.

3. test_syssvc_setname.c: P5 no longer asserts only that the DUPLNAM
   message is absent (satisfiable by SET PROCESS/NAME doing nothing at
   all) -- it now spawns a second holder and reads the executive's own
   row back through vms_kif_procscan() to prove the name was ACTUALLY
   SET. New P6 covers the 16-char blocker directly: a 15-char boundary
   name is accepted, a 16-char name is refused in the oracle's two-line
   shape, the truncated form never appears anywhere in the output, and
   SHOW SYSTEM still names the process with its pre-existing name,
   unchanged, after the refusal.

METHOD 5: probed two other inputs the truncation could have been hiding.
Neither is oracle-pinned in this repo, so both are reported as OBSERVED
facts (printf), not asserted as right or wrong:
  - SET PROCESS/NAME="" is a SILENT NO-OP -- a pre-existing
    `if (name_val && *name_val)` guard skips the whole block before
    vms_kif_setprn() is ever called. Different code path from the
    upname buffer this item fixes; flagged, not fixed, here.
  - SET PROCESS/NAME="OVMXFBE!BAD" is ACCEPTED -- name_is_valid() only
    checks for a NUL within VMS_PRCNAM_SIZE bytes, never validates
    character set.

Verified in QEMU (podman -f tests/qemu/Dockerfile): clean run 27 suites,
713 PASS, 0 FAIL (test_syssvc_setname: 18/18). Mutation-control run
(FACILITY_DEFECT=proctab-duplicate-name) reproduces the exact red set
{test_kmod_procnam, test_syssvc_procnam, test_syssvc_setname,
test_syssvc_startup_service} with test_syssvc_setname reddening exactly
one assertion (P4's DUPLNAM check) -- both unchanged from round 1,
confirming the existing facility_defects.sh wiring needed no changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…us (vms-fbe round 3)

Round 2's P6 built its capture from FOUR DCL commands -- SET(15-char),
SHOW SYSTEM, SET(16-char, refused), SHOW SYSTEM -- so `out` holds TWO
SHOW SYSTEM outputs. sys_row_for() scans from byte 0, so the assertion
matched the row printed BEFORE the refused rename, while its own text
claims the name survived the rename AFTER it. An implementation whose
refusal WIPED the name passed it.

Round 2's commit message asserted that P6 proves "SHOW SYSTEM still
names the process with its pre-existing name, unchanged, after the
refusal". That claim was not supported by the assertion it described.
It is withdrawn, not restated.

FIX (test only, no behaviour change): cut the capture at the
MSG_IVLOGNAM refusal message and search only what follows, so the
POST-refusal SHOW SYSTEM is the only thing that can satisfy the check.
If the refusal message is absent the assertion goes red rather than
falling back to the whole capture.

PROVEN BY EXECUTION, not by a green run. sys_row_for() was copied
verbatim into a standalone harness with round 2's expression and round
3's side by side, and fed two inputs: a faithful capture, and the same
capture with the POST-refusal row's name field wiped (row shape from
src/vmsdcl/dcl_cmd_show.c:545, "%08X %-15s %s"). Result:

  faithful capture            round2 = GREEN   round3 = GREEN
  MUTATED (refusal wiped it)  round2 = GREEN   round3 = RED

Also, in lockstep so the negative-control equality check still holds:
P4's assertion label said "the oracle-pinned %SYSTEM-F-DUPLNAM message"
-- round 1's single-line shape, which the code has not checked since
round 2 -- and is now "the oracle-pinned two-line %SET-E-NOTSET /
-SYSTEM-F-DUPLNAM shape", matching MSG_DUPLNAM. facility_defects.sh's
knock_on_fail line and one comment carry the same edit;
`facility_defects.sh selftest` re-confirms every require_fail/
knock_on_fail text exists literally in a suite source.

NOT fixed, and named rather than silently left: the two-line message
text is still hardcoded in the test instead of being derived from
run_print_condition()/sys$getmsg. That is a different change (the test
would have to link or invoke the message formatter) and is out of this
round's scope.

Verified in QEMU (docker -f tests/qemu/Dockerfile -t ovmx-fbe-r3).
Clean run: 27 SUITE banners all rc=0, FINAL RESULTS 29 suites passed /
0 failed, 713 PASS / 0 FAIL, test_syssvc_setname 18/18 -- identical to
round 2. Mutation control (FACILITY_DEFECT=proctab-duplicate-name via
/inject_and_run.sh): red set is EXACTLY {test_kmod_procnam,
test_syssvc_procnam, test_syssvc_setname, test_syssvc_startup_service},
5 unique FAIL lines equal to require_fail(2) + knock_on_fail(3)
verbatim, test_syssvc_setname 17/1 -- exactly one assertion, P4's.

The container exits 1 (run_tests.sh:66, `set -o pipefail` with
`echo | grep -q` -> EPIPE) on a run with zero failures. Pre-existing,
reproduced on unmodified main, filed separately; the run is judged by
the FINAL RESULTS line and the SUITE banners.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Test-only. src/ is untouched: the product was already correct on both
counts, and both CI failures were defects in the new suite itself.

1. NEGATIVE CONTROL (executive absent): the suite exited 1, not the
   contract's 77. CI diagnosed "a fabricated success"; that diagnosis is
   wrong and is filed as vms-c9c -- rc=1 only means "77 was not reached",
   and both fabricated-success assertions PASSED in that run.

   OBSERVED BY EXECUTION, not read off the source: with no /dev/vms the
   shipped DCL.EXE does not crash. It prints, in 139 bytes, and exits 0:
     %OVMX-E-SETPRNFAIL, SET PROCESS/NAME could not reach the executive (status %X000002A4)
       SYSTEM      logged out at ...
   The failing assertion was a crash PROXY -- "%DCL-" or "%SYSTEM-" or
   "$" -- whose three substrings do not include the message the product
   actually emits, and a piped DCL prints no "$" prompt. It tested the
   harness's guess about DCL's output, not DCL's behaviour.

   LEGAL ANSWER TAKEN: the command already fails honestly, and the
   OVMX-branded (not %SYSTEM-) shape is correct precisely because an
   absent executive is a condition OpenVMS never faces and has no message
   for -- inventing one would be the Rule 10 self-certification this epic
   exists to kill. dcl_cmd_set.c:552-600 already routes it that way. No
   degraded mode is resurrected: the product cannot reach this state at
   all (vms-0ff), which is why only the executive-less rig observes it.
   The assertion now pins that honest refusal POSITIVELY.

   NON-VACUITY, MEASURED: the predicate was evaluated standalone against
   the real capture and five defect shapes. GREEN on the real capture
   only; RED on a silent success, a crash, a truncated message and an
   invented %SET-E-NOTSET. The disjunction it replaces was GREEN on a
   silent userspace fallback printing a bare "$" -- the exact Rule 9
   defect the gate exists to catch. It also went RED unaided in an
   intermediate run where DCL.EXE was staged without a shared library.

2. ATTRIBUTION (bind-client-no-register), a control rounds 1-3 also never
   ran: the suite died at rc=141 = SIGPIPE. It writes a DCL script into a
   pipe whose only reader is a DCL.EXE that, with the bind deleted, exits
   before the write. A suite whose verdict is a signal number attributes
   nothing. Fixed with the remedy test_syssvc_showterm.c:392-405 already
   applied for the identical failure under the identical control:
   signal(SIGPIPE, SIG_IGN), so the writes fall into their checked
   branches. Nothing is weakened -- a signal death becomes named failures.

   Then the same thing happened that showterm records: THE DECLARED SET
   GREW BECAUSE THE OBSERVATION GOT BETTER. The suite now runs to
   completion under the mutation (8 passed, 7 failed, rc=1), so CI's three
   truncated reds are seven. All seven are one missing bind reaching the
   command layer -- DCL reaches the process table only through kif_bind()
   -- and are declared in knock_on_fail with a knock_on_why, not narrowed
   away: the value of this suite is that it drives the real DCL.EXE end to
   end, so it SHOULD be sensitive to the stack beneath the command. Eight
   assertions stay GREEN under it, including the round-1 truncation check.

VERIFIED, BOTH BUILDS RUN THIS TIME (docker, workshop):
  negative control  every test_syssvc_* suite rc=77 including setname
                    (5 passed, 0 failed); fabricated-success assertions
                    still PASS.
  clean run         27 SUITE banners all rc=0; FINAL RESULTS 29 passed,
                    0 failed; 713 PASS / 0 FAIL; setname 18/18.
  bind-client-no-register  setname rc=1 (was 141); replaying the gate's
                    own checks 4/5/6/7: no stray suites, red set EXACTLY
                    118 named = 118 observed, all five blind suites green.
  proctab-duplicate-name   PRESERVED EXACTLY: red set is still
                    {test_kmod_procnam, test_syssvc_procnam,
                    test_syssvc_setname, test_syssvc_startup_service};
                    5 unique FAIL lines == require_fail(2) +
                    knock_on_fail(3) verbatim; setname 17/1, the 1 being P4.
  facility_defects.sh selftest green, including "every
                    require_fail/knock_on_fail text exists literally in a
                    suite source".

NOT DONE, NAMED: run_facility_negctl.sh's own POSITIVE control cannot pass
on this host -- the pristine container exits 1 through the pre-existing
run_tests.sh pipefail/SIGPIPE bug (rd vms-b1f), unrelated to either defect
here -- so checks 4/5/6/7 were replayed against the real captured run
using that script's own extractors verbatim, rather than driven by it.
Still open from round 3: the two-line message text is hardcoded in
dcl_cmd_set.c rather than rendered via run_print_condition/sys$getmsg, and
the stale single-line prose at test_syssvc_setname.c:492.

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

Copy link
Copy Markdown
Contributor Author

Round 4 — f4a59d7, both CI gates closed

The first push of this PR failed two checks. Both were test defects; src/ is untouched by this round — the product was already correct on both counts.

1. Kernel Executive — Negative Control. CI reported "a public sys$ entry point reported SUCCESS with no executive present: a fabricated success." That diagnosis was wrong — both fabricated-success assertions passed. Filed as vms-c9c: the job infers one cause from rc=1, but its own comment says rc=1 has at least two.

What DCL.EXE actually does with no /dev/vms, executed rather than reasoned — it does not crash; 139 bytes, exit 0:

%OVMX-E-SETPRNFAIL, SET PROCESS/NAME could not reach the executive (status %X000002A4)

The failing assertion was a crash proxy matching %DCL- / %SYSTEM- / $, whose substrings don't include the message the product emits — and a piped DCL prints no $ prompt. The OVMX-branded shape is correct: an absent executive is a condition VMS never faces, so inventing a %SYSTEM- message would itself be the Rule 10 defect.

The assertion now pins that refusal positively — green on the real capture only, red on silent success, on a crash, on truncation, and on an invented %SET-E-NOTSET. The old disjunction was green on a silent userspace fallback printing a bare $ — precisely the Rule 9 defect this gate exists to catch.

2. Per-Facility Negative Controlsbind-client-no-register. test_syssvc_setname exited rc=141 (SIGPIPE): the suite writes a script into a pipe whose reader DCL.EXE exits first once the bind is deleted. Fixed with the remedy test_syssvc_showterm.c:392-405 already uses for the identical failure under the identical control. The suite then runs to completion, so CI's 3 truncated reds became 7 — all declared in knock_on_fail / knock_on_why, none narrowed away.

Measured, both builds run this time

run result
Negative control test_syssvc_setname rc=77, 5 passed / 0 failed; every test_syssvc_* at 77
Clean 27 SUITE banners rc=0 · FINAL RESULTS: 29 suites passed, 0 suites failed · 713 PASS / 0 FAIL · setname 18/18
bind-client-no-register setname rc=1 (was 141); no strays; red set 118 named = 118 observed; all 5 blind suites green
proctab-duplicate-name red set preserved exactly — {test_kmod_procnam, test_syssvc_procnam, test_syssvc_setname, test_syssvc_startup_service}, setname 17/1 (P4 only)

Note for reviewers

run_facility_negctl.sh cannot be driven on the workshop dev host at all: its own pristine positive control exits 1 through the pre-existing pipefail+SIGPIPE bug in run_tests.sh, so it refuses to inject. Round 4 replayed its checks 4/5/6/7 against the real captured run using the script's own extractors verbatim. That is now the second agent forced into the same manual substitute, so vms-b1f has been raised to p1. It is host-sensitive and does not fire on GitHub runners — judge this PR by CI, not by a local container exit code.

🤖 Generated with Claude Code

@baron-3dl

Copy link
Copy Markdown
Contributor Author

CI is green at f4a59d735/35 checks pass, including the two that refused round 3: Kernel Executive — Negative Control and Kernel Executive — Per-Facility Negative Controls (attribution).

Left as a draft deliberately: the merge decision is the operator's. vms-fbe is the only one of the three vms-14f frontier branches at this state — vms-ecf and vms-cb5 are both in round 4.

baron-3dl pushed a commit that referenced this pull request Aug 2, 2026
Four statements in the 2026-08-01 revision were measurably wrong and are
corrected in a new section 0: podman (the host has docker), vms-a30 being
outside the closure (it blocks vms-150), the lab path (/data/training/vax),
and main's SHA (moved to ce11330). Several origin branches are duplicate
refs at the same SHA, so the doc now says to establish a round by SHA.

Frontier state: vms-fbe is draft PR #46 at 35/35 green; vms-ecf + vms-f26 are
draft PR #47, adversary-cleared but CI-red on a source-scan refusal that is
the gate working correctly; vms-cb5 round 4 is pushed and in verification.
The executive-resident count is now 7, not 10 -- sys$enq/sys$enqw/sys$deq
were demoted to PARTIAL on measured evidence (vms-82a).

Section 5 gains the lessons earned this round: open the draft PR early
because CI is a different and cheaper oracle; a passing test can conceal the
failure mode it was written to detect; distinguish a container's exit status
from a suite's; a gate printing a confident wrong cause is worse than one
printing none; test a disqualifier for over-firing, since under-claiming is
also a wrong measurement; and go to the lab before declaring a VMS value
unpinnable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@baron-3dl
baron-3dl marked this pull request as ready for review August 2, 2026 02:06
@baron-3dl
baron-3dl merged commit c243992 into main Aug 2, 2026
35 checks passed
baron-3dl pushed a commit that referenced this pull request Aug 2, 2026
The eight defects found in passing while settling the three branches were
filed unwired; this records where triage put them. A new parent vms-95f
groups the four harness-integrity items (vms-b1f, vms-c9c, vms-215,
vms-008) and blocks vms-150. vms-38c also blocks vms-150. vms-82a, vms-e60
and vms-2f8 block vms-042; vms-2d37 blocks vms-898.

The harness parent blocks vms-150 and deliberately NOT vms-b33: Phase 2
rests on per-suite evidence these defects do not corrupt, and it is the
near-term unblock. Phase 3 veracity is exactly the claim that our
measurements mean what they say, so it waits.

Closure goes 17 -> 26 open, no cycles. That is a sequencing decision, not a
scope decision, and section 3 records the undo: un-wiring the five items
returns it to 17 and ships the epic sooner on softer evidence. That trade
is the operator's.

All three branches are now PRs #46, #47 and #48, marked ready for review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baron-3dl pushed a commit that referenced this pull request Aug 2, 2026
PR #46 (vms-fbe, c243992) and PR #47 (vms-ecf + vms-f26, 57fc164) are
merged; main is eff4fe3 and all three items are closed. PR #48 (vms-cb5)
remains open at 1598ef5 with main merged in and the knock_on_fail fix for
the two scenario-G/OPCOM+ reds under bind-client-no-register.

vms-b33 is unblocked and active: its last two blockers closed with #47, so
the fifth Phase 2 verdict is the next thing to run. vms-150 does NOT unblock
when #48 merges -- vms-a30, vms-95f and vms-38c also block it.

Closure is 23 open, not 26: eight defects triaged in against three items
closed on merge, from a starting 17.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 4, 2026
* docs: handoff for the vms-14f executive-residency dispatch

Records the execution pointer, the three open branches and their named
blockers, the method rulings that were paid for during the dispatch, and
the harness arithmetic reconciled in vms-215.

Wave 47 outcomes are appended in a follow-up commit.

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

* docs vms-14f: rewrite the handoff after the 2026-08-02 frontier round

Four statements in the 2026-08-01 revision were measurably wrong and are
corrected in a new section 0: podman (the host has docker), vms-a30 being
outside the closure (it blocks vms-150), the lab path (/data/training/vax),
and main's SHA (moved to ce11330). Several origin branches are duplicate
refs at the same SHA, so the doc now says to establish a round by SHA.

Frontier state: vms-fbe is draft PR #46 at 35/35 green; vms-ecf + vms-f26 are
draft PR #47, adversary-cleared but CI-red on a source-scan refusal that is
the gate working correctly; vms-cb5 round 4 is pushed and in verification.
The executive-resident count is now 7, not 10 -- sys$enq/sys$enqw/sys$deq
were demoted to PARTIAL on measured evidence (vms-82a).

Section 5 gains the lessons earned this round: open the draft PR early
because CI is a different and cheaper oracle; a passing test can conceal the
failure mode it was written to detect; distinguish a container's exit status
from a suite's; a gate printing a confident wrong cause is worse than one
printing none; test a disqualifier for over-firing, since under-claiming is
also a wrong measurement; and go to the lab before declaring a VMS value
unpinnable.

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

* docs vms-14f: record the PM triage of the eight items found this round

The eight defects found in passing while settling the three branches were
filed unwired; this records where triage put them. A new parent vms-95f
groups the four harness-integrity items (vms-b1f, vms-c9c, vms-215,
vms-008) and blocks vms-150. vms-38c also blocks vms-150. vms-82a, vms-e60
and vms-2f8 block vms-042; vms-2d37 blocks vms-898.

The harness parent blocks vms-150 and deliberately NOT vms-b33: Phase 2
rests on per-suite evidence these defects do not corrupt, and it is the
near-term unblock. Phase 3 veracity is exactly the claim that our
measurements mean what they say, so it waits.

Closure goes 17 -> 26 open, no cycles. That is a sequencing decision, not a
scope decision, and section 3 records the undo: un-wiring the five items
returns it to 17 and ships the epic sooner on softer evidence. That trade
is the operator's.

All three branches are now PRs #46, #47 and #48, marked ready for review.

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

* docs vms-14f: two of the three frontier branches are merged

PR #46 (vms-fbe, c243992) and PR #47 (vms-ecf + vms-f26, 57fc164) are
merged; main is eff4fe3 and all three items are closed. PR #48 (vms-cb5)
remains open at 1598ef5 with main merged in and the knock_on_fail fix for
the two scenario-G/OPCOM+ reds under bind-client-no-register.

vms-b33 is unblocked and active: its last two blockers closed with #47, so
the fifth Phase 2 verdict is the next thing to run. vms-150 does NOT unblock
when #48 merges -- vms-a30, vms-95f and vms-38c also block it.

Closure is 23 open, not 26: eight defects triaged in against three items
closed on merge, from a starting 17.

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

* docs vms-14f: rewrite the handoff for the seventh Phase 2 verdict

The 2026-08-02 revision named PR #48 as its only loose end; #48 merged
twelve minutes after that revision was committed, so the document was
stale within the hour. Main has since moved eff4fe3 -> 8e8be98 through
eight merges closing vms-b33 blockers, plus four SCS merges from another
dispatch.

vms-b33 is blocked again, by exactly five items -- vms-c79, vms-004e,
vms-659, vms-d894, vms-35f -- every one a two-edit escape from a static
gate, all filed by the seventh verdict's adversary. Closure re-derived by
DAG walk from vms-14f: 35 open, counting the epic, up from 23.

Numbers re-derived rather than carried: the eight merge SHAs against
gh api (all green on Build & Test and the per-facility negative control
job); the ctest counts at 44870ac / 8552c29 / d9b69cc / 8e8be98 (56 / 56 /
58 / 59) by configure-only enumeration, which is what makes "a green on a
stale base is not evidence" a measurement rather than an assertion; the
CI timeout raise 20 -> 35; the ledger's 22 rows checked against rd's live
open set by hand. What was NOT re-measured is labelled as such -- the
ctest 2-failure pair and the rd_citations_fresh flake are carried from
run 7, and the round ran under a no-builds constraint at 95% disk.

Keeps visible: 13 of 44 entry points remain unwired, so Phase 2 closing
means what is wired is proven, not that the executive is complete; and
the vms-1e1 (B) ruling with its one-command undo, whose binding side
condition is now itself under attack by vms-004e and vms-35f.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: alice <alice@workspace.local>
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>
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