Skip to content

docs(V2): subos.env, and why a new module needs a type() probe - #496

Merged
Sunrisepeak merged 2 commits into
mainfrom
docs/subos-env-probe-rule
Aug 4, 2026
Merged

docs(V2): subos.env, and why a new module needs a type() probe#496
Sunrisepeak merged 2 commits into
mainfrom
docs/subos-env-probe-rule

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

libxpkg 0.0.48 adds subos.env{} — a package declaring an environment variable its subos must export. This documents the API and, more importantly, corrects the probe rule for it.

The correction

The spec's existing guidance is if xvm.files then. That is correct only because xvm is a module older clients already ship, so the missing field really is nil.

A missing module never is. import() answers an unknown module with a permissive proxy whose every key returns a truthy, callable table:

import("xim.libxpkg.subos")   -- older client gets a STUB
if subos.env then             -- <- true on EVERY client

The old client takes the new branch, calls the function, and the call evaporates. Install succeeds, nothing is configured, nothing complains.

For a capability introduced as a new module the probe must test the type — the stub is a table with __call, the real entry point is a function:

if type(subos.env) == "function" then ... end

xlings E2E-61 runs one recipe through a real released binary and the current build and asserts both readings (truthiness true on both, type() false then true). If import() ever stops stubbing unknown modules, that test is what says the rule can be relaxed.

Also documented

subos.env{var, op, value, binding}: fields, that append/set-if-unset are refused rather than silently downgraded, the placeholder requirement (${pkgdir} etc.) and why an unresolvable one is left verbatim instead of blanked, that recipes must not write uninstall cleanup (declarations are provider-scoped), and that conflicts resolve by binding order with every conflict reported.

Upstream: openxlings/libxpkg#32 · mcpplibs/mcpp-index#153

speak-agent and others added 2 commits August 4, 2026 14:00
Two bugs the same reinstall command hit at once:

1. In-place `patchelf --set-rpath <exe>` fails with
     patchelf: open: Text file busy
   whenever the user reinstalls godot with an editor still running
   -- Linux refuses to truncate a mapped, executing binary.  Switch
   to `patchelf --output <tmp> && mv <tmp> <exe>`: rename(2) unlinks
   the old inode and the running process keeps its own reference,
   so live editors keep working while new invocations pick up the
   patched file at the same path.

2. Every reinstall re-appended the probed host GUI dirs onto an
   rpath that already contained them, growing it unboundedly:
     .../lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu
     :/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu   ← 2nd install
     :/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu   ← 3rd install
     ...
   The shell helper now dedupes per-directory (case match on
   `:$new:`), and short-circuits when the target rpath and DT_RPATH
   tag are already in place -- so a reinstall that has nothing to
   change makes no patchelf call at all, sidestepping ETXTBSY too.

Verified locally:
  * fresh install: RPATH set once, no duplicates
  * reinstall (already correct): no patchelf invocation, RPATH
    unchanged, install reports success
  * reinstall while `godot` is running: no ETXTBSY, no error
`if xvm.files then` is correct only because `xvm` is a module older clients
already ship, so the missing FIELD really is nil. A missing MODULE never is:
import() answers an unknown module with a permissive proxy whose every key is
a truthy, callable table. A recipe probing `if subos.env then` takes the new
branch on every client, the call evaporates on the ones that predate it, and
the install reports success having configured nothing.

Documents both the rule (`type(subos.env) == "function"`) and the
`subos.env{}` API it gates: fields, the placeholder requirement, why an
unresolvable placeholder is left verbatim rather than blanked, that uninstall
cleanup must NOT be written by the recipe, and how conflicts resolve.

Pinned by xlings E2E-61, which runs one recipe through a real released binary
and the current build and asserts both readings.
@Sunrisepeak
Sunrisepeak merged commit 5e49d19 into main Aug 4, 2026
2 checks passed
@Sunrisepeak
Sunrisepeak deleted the docs/subos-env-probe-rule branch August 4, 2026 21:37
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.

2 participants