Skip to content

M5-2c2 stage b: retire the ORPHAN verb, leave the fields reserved - #91

Merged
mgrossmann merged 4 commits into
mainfrom
m5-2c2-orphan-retire
Aug 31, 2026
Merged

M5-2c2 stage b: retire the ORPHAN verb, leave the fields reserved#91
mgrossmann merged 4 commits into
mainfrom
m5-2c2-orphan-retire

Conversation

@mgrossmann

@mgrossmann mgrossmann commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

M5-2c2 stage b — retire the ORPHAN verb, leave the fields

Implements the decisions locked on the stage-a map (PR #90). Detail in
docs/measurements/m5-2c2/stage-b.md.

Nothing is closed. Obligation #4 is discharged in substance for the identity half
only
— the rest of the probe scaffolding is c3, after (e). #67 is narrowed, not closed.
The TSTDEATH restructuring is priced and left open.

Anchor layout unmoved, ANCVERNO 3, NSFRQE frozen, no field removed.


The change

ORPHAN stored a request-supplied identity into the slot verbatim — a forged identity
taken from an unauthorised caller, which is what the guard must never trust for a real
client. It is retired.

It is rejected, not just deleted, and the placement carries the argument. The staging
dispatch is a fall-through chain ending in ECHO, so deleting the test alone would have made
FNORPH be serviced as an ordinary ECHO. It is now rejected by name, first in the
pre-claim chain
, so a retired verb costs no slot and no in-flight count — true by
position, not by argument
. That is also what narrows #67.

The rejection writes the rc into the caller's block (BADFUNC, mirroring BADANC)
rather than reusing BADREQ, which leaves it in R15 only. BADREQ would have left the
client reading its own initialised -1 — indistinguishable from "the SVC never ran".

pascb/pasid stay as rsvd_pascb/rsvd_pasid at identical offsets: no layout change,
so the client/router skew stage a identified cannot arise.

NSFV_REQ_ORPHAN is kept, with the rule written at the declaration: a mechanical change
must not settle an open design question as a side effect.
Deleting it breaks tstdeath.c's
compile and would force the TSTDEATH decision; the constant is cleaned up with that
decision, not before it.


Verified host-side (survives without a stand)

  • make test-host 3342 PASS / 0 FAIL, unchanged.
  • The classifier's logic is still fully pinned: src/nsfreqx.c and test/tstreqx.c are
    untouched by this branch, so all four rows and all four UNKNOWN branches keep their host
    coverage. The retirement removes a live driver, not host coverage.
  • Layout proven identical to main by comparing every NSFV_OFF_ASSERT value and
    NSF_SIZE_ASSERT(NSFV_REQ, 64) — no field removed, no offset moved.
  • tools/check-card-columns.sh OK — after it caught one of my own cards at 72 bytes
    mid-edit, which is the documented failure mode found before the toolchain saw it.
  • as370 -a= listing: C R3,=A(FNORPH)5930 64F8, BE BADFUNC4780 64A4, base R6
    not dropped to 0
    , target matching BADFUNC at 0004A4.
  • All 1268 source cards present, byte-identical, in source order (matched as a
    subsequence — macro expansion makes listing statement numbers non-1:1 with source lines).
  • The check discriminates: a deliberate 89-byte card gave check-card-columns FAILED,
    as370 rc=8, and the statement check named both the overlong card and the BE BADFUNC
    it ate.
  • XFERIN still ends B DOPOST; only comment text sits where ORPHIN was — no orphaned
    code, no new fall-through.

Verified live (MVSCE)

round result
NSFV — TSTSVC/TSTMVCK/TSTUBUF/TSTXFW 412 PASS / 0 FAIL, CC 0 batch+TSO
NSFS — TSTRQXC/TSTRQXF 122 PASS / 0 FAIL, CC 0 batch+TSO
NSFS — TSTRQXM batch CC 0, host peer 9353 bytes byte-exact

TSTDEATH's absence is stated, not quiet: the NSFV figure moves 484 → 412, exactly
its 72 assertions. TSTMVCD stays excluded (#53). TSTRQXM's TSO arm fails by design
(one-shot listener consumed by the batch run — verified to be exactly CONNECT + its
dependent CLOSE).

The rejection, from an unauthorised clientTSTDEATH is that client, now FAIL CC 1
("ran and failed", not the CC 20 "did not run" idiom):

FAIL: LIVE orphan: SVC accepted and returned without waiting  (got 4, want 0)
FAIL: LIVE client: request SERVICED (state DONE), not reaped  (got 0, want 2)
FAIL: LIVE client: in-flight count NOT given back             (got 0, want 1)

rc = 4 in the caller's block; slot FREE; inflight 0. No slot, no count — observed.

Revert test, three states, one assertion moving:

state TSTDEATH rc slot claimed forged reaps
retired FAIL CC 1 (52/20) 4 no 0
reverted ok CC 0 (72/0) 0 yes 6
restored FAIL CC 1 (52/20) 4 no 0

State 2 is rendered positively — the forged identities shown taking: ASID=0020 (a
free ASID nothing owns), ASCB=00FD0F20 (the real ASCB +8), ASCB=00000000 (row 4a).

Zero dumps; both STCs start/stop clean; SVC 239 stolen and restored every cycle; no
NSF054W; stand left with nothing running.


Reported, not done

  • TSTDEATH: five options priced against the thing that actually matters — a Stage-0
    probe going red names a mechanism, a socket op going red names nothing. Option B turns
    out not to be possible as stated, for this round's own reason (a batch client reads LIVE
    forever). Option E — split by ROW rather than moving or retiring the test — carries the
    lowest isolation cost, on the reasoning that isolation is a property of the probe, not of
    the round
    ; its real cost is that row 2's live proof stops being a test and becomes an
    operator-driven procedure
    with no deterministic batch form. Decision left open.
  • NSFV_REQ_ORPHAN: kept with a retired-marker comment. Cost stated both ways — deleting
    or renaming it breaks tstdeath.c's compile and would force the TSTDEATH decision as a
    side effect
    , which this round is told not to pre-empt. The honest cost of keeping it is a
    header that names a verb which no longer works.
  • Probe verbs against NSFS strand a slot and the in-flight count, and hang the client #67 updated and retitled — a comment records that the sharp variant is gone and the
    issue stays open (ECHO/XFER still strand a slot each and hang the client), and the
    title drops the now-false ORPHAN clause while restoring the hang. Still OPEN.
  • The STATS truncation filed as F NSFS,STATS silently truncates its own report, and the host capture ring drops the tail -- both block (e) #92, with the (e) dependency stated and no fix proposed.

ORPHAN was the one place a request-supplied identity was stored into a
slot verbatim -- a forged identity taken from an unauthorised caller,
which is exactly what the guard must never trust for a real client. The
map (PR #90) priced its removal: row 2 is reproduced six times out of six
with a real dying STC, row 3 was never producible live at all, and row 4
was always four branches of which ORPHAN drove one.

The verb is rejected rather than deleted outright, and the placement is
the point. Deleting the staging test alone would have let FNORPH fall
through to the ECHO default and be serviced as an ordinary ECHO, so the
code is now rejected by name in the pre-claim chain, ahead of the slot
claim. A retired verb therefore costs no slot and no in-flight count, and
that is true by position rather than by argument -- which is also what
narrows the stranding case #67 describes.

The rejection writes the return code into the caller's block rather than
leaving it in R15 alone. The existing bad-request path does the latter,
which would have left the client reading whatever it had initialised the
field to -- indistinguishable from the SVC never having run.

pascb and pasid stay, renamed reserved, at their existing offsets. That
is deliberate: they sit mid-struct with seven fields after them, two of
which carry real requests, and nothing version-checks this block -- the
router validates a layout-invariant eyecatcher, and the anchor version
guards a different contract. Clients link separately from the runtime
library and the runbook replaces only the latter, so a router-only deploy
would hand a new client an old router reading these offsets silently.
They can go when the block gains a version check or a round relinks every
client with the router.

Obligation #4 is discharged in substance for the identity half only. The
rest of the probe scaffolding remains, so #4 is not met overall.
The verb is retired and the gates are green; this records them and the
two questions the round is told to report rather than answer.

TSTDEATH's four options are priced against the property that matters,
which is not test count: an isolated Stage-0 probe going red names a
mechanism, while a socket operation going red names nothing. One of the
four turns out not to be possible as stated, for this round's own reason
-- keeping the probe on NSFV with rows 1 and 2 needs a client whose
address space really dies, and a batch client in an initiator reads LIVE
forever. Row 1 alone is keepable, because the blocking ECHO already
witnesses it with a real identity and never needed ORPHAN.

Also states the cost of keeping the ORPHAN constant, which is not
neutral: the header still names a verb that no longer works. Deleting or
renaming it breaks the test's compile and would settle the TSTDEATH
question as a side effect, which is the one thing this round must not do.

The #67 narrowing and the STATS truncation issue are drafted here and
deliberately not applied or filed.
Both were drafted in this branch and are now posted: the #67 comment
saying the sharp variant is gone while the issue stays open, and the
STATS truncation as #92 with its dependency on (e) stated.
A fifth TSTDEATH option: split the test by row rather than moving or
retiring it -- row 1 stays on NSFV where it is already witnessed by the
blocking ECHO, row 2's live proof lives on the NSFS side where the death
rig exists, rows 3 and 4 stay host-pinned and documented as not
producible. None of the first four keeps the probe named while splitting
the rows, so it is genuinely a fifth.

It carries the lowest isolation cost, and the reasoning holds up: the
isolation being protected is a property of the probe rather than of the
round, so proving each row where it is producible preserves it better
than moving a whole test into a round where half its scenarios cannot
run. Its real cost is that row 2's live proof stops being a test and
becomes an operator-driven procedure with no deterministic batch form.
It also differs from retiring the test by more than it appears: an
incidental witness still fails when the guard breaks, but it does not
name the mechanism, which is the property at stake.

The ORPHAN constant keeps its name, and the reason is now at the
declaration rather than only in the round's notes: a mechanical change
must not settle an open design question as a side effect, so the constant
is cleaned up together with the TSTDEATH decision and not before it.
Comment-only -- no code, no layout, no constant value changed.
@mgrossmann
mgrossmann merged commit 9357290 into main Aug 31, 2026
3 checks passed
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