ITADN

`subos.env` has no conditional op, so a package cannot offer "user's value wins"

#508Closedspeak-agent 创建于 15 天前
S
speak-agentcommented
## What `subos.env` accepts two ops (`src/core/subos/manifest.cppm:45-46`): ```cpp inline constexpr std::string_view OP_SET = "set"; inline constexpr std::string_view OP_PREPEND = "prepend"; ``` Neither is conditional. `set` overwrites whatever the user exported, and `prepend` only makes sense for list-valued variables. There is no way for a package to say *"export this **unless** the user already chose a value"*. ## Why it matters A package that needs to steer a subsystem has to force the value, and then the user has no override. `pkgs/w/wsl-gl-host-link.lua` documented an escape hatch that does not exist: > `set`, not `prepend`: this is a single value, not a list. A user who exported GALLIUM_DRIVER themselves keeps it (UC-1), so `GALLIUM_DRIVER=llvmpipe` remains the escape hatch when the GPU path misbehaves — **and that escape is why forcing the value here is safe.** Measured in mcpp-community/mcpp#382: `export GALLIUM_DRIVER=llvmpipe; mcpp run` still uses the package's value. So the recipe's own safety argument rested on a capability that was never implemented — the comment was the only place the escape hatch existed. That recipe is now conditional on the driver being present, which removes the immediate breakage, but it still cannot be overridden by a user who wants llvmpipe on a machine where d3d12 *is* present. ## Proposal A third op — `default` (or `set-if-unset`): ```lua subos.env{ var = "GALLIUM_DRIVER", op = "default", value = "d3d12", binding = tag } ``` Semantics: export only if the variable is unset in the caller's environment at subos-entry time. That makes "the package suggests, the user decides" expressible, which is what several sentinel-shaped packages actually want. Two details worth deciding explicitly: 1. **Unset vs empty.** `GALLIUM_DRIVER=` (empty) is a deliberate "no override" in some subsystems. Treating empty as unset would clobber that; treating it as set is probably right, but it should be stated rather than inherited from an implementation. 2. **Two packages both declaring `default` for one var.** `set` presumably already has a conflict rule; `default` should follow whatever that is rather than invent a second one. ## Capability probing Recipes must be able to detect it. Per `docs/V2/xpackage-spec.md`, a new *op* on an existing module is not probeable the way a new function is — `subos.env` already exists, so `if subos.env then` says nothing about which ops it accepts. Either: - expose the accepted ops (e.g. `subos.env_ops`), which a recipe can probe as a function/table, or - make an unknown op a hard error rather than a silent no-op, so an old client fails loudly instead of dropping the declaration. The second matters either way: today an unknown op is rejected by the manifest validator (`manifest.cppm:336`) as `EnvDeclMalformed`, which is good — worth confirming that path is reached at *install* time on an old client and not just at validate time.
关闭于 15 天前 1 条评论