@@ -907,3 +907,189 @@ rather than half-fixed.
907907 (`xlings subos use N --sandbox --cmd ...`) with the CN mirror configured,
908908 because a sandbox is the only thing that tests what was published rather than
909909 what is in the working tree.
910+
911+ ## 14. What is open after the release, and what each one's fix is
912+
913+ Section 7 listed what was open before the work. This lists what is open after
914+ it. Two entries were found by measurement while assembling the comparison in
915+ section 10, and the first of them produces a wrong artifact rather than a
916+ failure.
917+
918+ ### 14.1 Object storage does not rebuild when its payload changes
919+
920+ Measured on `tests/spirv-object-storage` against the released 2026.9.7.1, by
921+ editing the shader so its compiled output must differ and reading the byte count
922+ the program prints:
923+
924+ | storage | before the edit | after |
925+ |---|---|---|
926+ | `header` (the default) | `bytes=1480` | `bytes=1776` |
927+ | `object` | `bytes=1480` | **`bytes=1480`** |
928+
929+ The build succeeds and the artifact is the old one. This is not the `#include`
930+ case in 14.2 -- it is the DIRECT source, which the shader action does declare.
931+
932+ **Mechanism.** `rules/spirv.cppm` hands the generated `.S` to `mcpp::generated`,
933+ so its compile edge knows the `.S` and nothing else. The `.S` names the payload
934+ in `.incbin` but its own text does not change when the payload does, so
935+ `write_if_different` leaves the file alone, its mtime does not move, and no edge
936+ is dirty. The shader itself recompiles; the object that carries its bytes does
937+ not.
938+
939+ **A depfile does not close this, and that was measured rather than assumed:**
940+
941+ ```
942+ gcc: p.o: p.S /usr/include/stdc-predef.h
943+ clang: p.o: p.S
944+ ```
945+
946+ Neither assembler names an `.incbin`'d file in its dependency output. The
947+ channel added in this release is the right one for 14.2 and the wrong one here.
948+
949+ **The fix is to stop handing the `.S` to the engine as an ordinary source.**
950+ Object storage should emit a `role = "object"` `mcpp::action` that runs the
951+ assembler itself and declares BOTH inputs -- the `.S` and the payload -- which
952+ is the shape `mcpp.rules.cuda` already uses for a `.cu`. The dependency then
953+ exists in the graph rather than in a comment.
954+
955+ `storage::sidecar` copies the payload beside the artifact and has not been
956+ checked for the same shape. It should be, in the same change.
957+
958+ ### 14.2 No rule passes a depfile
959+
960+ `mcpp::action::depfile` shipped in 2026.9.7.1 and `grep depfile rules/` in
961+ `mcpp:plugins` returns nothing across all six rules; each declares
962+ `a.input(source)` alone. A shader or kernel that `#include`s another file does
963+ not rebuild when that file changes, which is the same behaviour xmake has and
964+ the one thing CMake's `add_custom_command(DEPFILE)` gets right.
965+
966+ Every compiler involved already emits one: `glslangValidator --depfile`,
967+ `glslc -MD -MF`, `slangc -depfile`, and `-MD -MF` for the clang-family drivers
968+ behind cuda, hip, sycl and ascendc. The work is one flag and one
969+ `a.depfile(...)` per rule, plus a fixture whose criterion is that editing an
970+ included file rebuilds -- the criterion has to be the artifact's content, not
971+ the build's exit code, because the defect is a green build over stale bytes.
972+
973+ 14.1 and 14.2 belong in one release. They are the same class of defect and the
974+ same fixture shape answers both.
975+
976+ ### 14.3 A device on Windows, and who decides it
977+
978+ The cause is settled and it is not a packaging defect. Under `VK_LOADER_DEBUG=all`
979+ on a GitHub runner:
980+
981+ ```
982+ INFO: Loader is running with elevated permissions.
983+ Environment variable VK_DRIVER_FILES will be ignored
984+ INFO: Loader is running with elevated permissions.
985+ Environment variable VK_ICD_FILENAMES will be ignored
986+ DRIVER: Found no registry files in HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\Drivers
987+ ERROR | DRIVER: Registry lookup failed to get ICD manifest files.
988+ ```
989+
990+ A process running elevated is not permitted to be told where its drivers are,
991+ because a path a non-administrator can write would then decide what code an
992+ elevated process loads. The loader falls back to the registry, which on a
993+ machine with no GPU is empty.
994+
995+ **The registry entry, exactly.** From `loader/loader_windows.c` and
996+ `vk_loader_platform.h` of the loader `compat:vulkan` builds:
997+
998+ - key `HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\Drivers`
999+ - value NAME: the absolute path of the ICD manifest, `...\lvp_icd.x86_64.json`
1000+ - value DATA: `REG_DWORD` `0` -- the loader accepts an entry only when
1001+ `value_size == sizeof(value) && value == 0`
1002+ - a driver whose file name is not in the loader's `known_drivers` table skips
1003+ the DXGI adapter check and is "assumed to be active", which is what lets a
1004+ software rasteriser be listed at all
1005+
1006+ **Three ways to get there, and they are not equivalent.**
1007+
1008+ 1. `xim:mesa-lavapipe`'s `config()` writes the entry on Windows. It has the
1009+ information and the install already runs with the necessary rights on a
1010+ runner. It is also a MACHINE-WIDE mutation performed by a package install:
1011+ every Vulkan application on that machine would then see lavapipe. That is a
1012+ different promise from the one the package makes today, whose own comment
1013+ says it "places the payload and stops; naming the ICD is the consumer's".
1014+ 2. The consumer writes it -- this repository's CI step, or a project's own
1015+ `build.mcpp`. Scoped to whoever wants it, and it keeps the package's promise
1016+ intact; the cost is that every consumer repeats it, which is the shape the
1017+ rule-package work exists to remove.
1018+ 3. Run the program unelevated. Correct on a developer machine and awkward on a
1019+ runner that is elevated by construction.
1020+
1021+ **Recommendation: 2 for CI now, and 1 only behind an explicit opt-in.** A
1022+ package that registers a driver system-wide as a side effect of being installed
1023+ is a surprise, and the surprise lands on software that has nothing to do with
1024+ mcpp. The decision is `xim:mesa-lavapipe`'s to make, not this repository's.
1025+
1026+ Whichever is chosen, the CI step withdrawn from `ci-windows.yml` carries the
1027+ four eliminations and the loader's own output beside it, so the next attempt
1028+ starts from here.
1029+
1030+ ### 14.4 A rule feature that is on by default does not imply `host-module`
1031+
1032+ `host-module` is inferred from the features a consumer REQUESTS. A rule package
1033+ whose rule sits in its own `[features] default` is activated without being
1034+ named, so the rule modules are collected -- that reads the resolved set -- while
1035+ the inference reads the requested one. Measured with a probe package declaring
1036+ `default = ["rules-probe"]`: mcpp synthesises the build program and then refuses
1037+ with
1038+
1039+ ```
1040+ error: build.mcpp imports 'probe.rules.probe', and no dependency provides it
1041+ as a host module.
1042+ declared without ` host-module = true ` : probe.rules
1043+ ```
1044+
1045+ **The fix that was written and withdrawn.** Extending the inference to the
1046+ dependency's own defaults made the outcome worse: the refusal gained
1047+ `importable here: rules`, which says the dependency is wired up while the
1048+ feature's own module still is not.
1049+
1050+ **The mechanism is not what the first reading said, and that is worth
1051+ recording.** The withdrawal note guessed that a default feature's sources are
1052+ folded into the set `units()` enumerates too late. That is false: the fold
1053+ happens at `prepare.cppm` line ~7138 and `units()` runs at ~7616. So the cause
1054+ is elsewhere and is NOT established. Anyone picking this up should start by
1055+ finding it rather than by trusting the guess.
1056+
1057+ `mcpp:plugins` declares `default = []`, so nothing published reaches this, and
1058+ the current behaviour is a refusal that names both the module and the key to
1059+ add. It is a correctness-of-diagnosis item, not a correctness-of-artifact one.
1060+
1061+ ### 14.5 A hermetic `vulkan-1.dll` on Windows
1062+
1063+ Measured feasible and not currently needed. The Khronos loader in
1064+ `compat:vulkan` cross-builds into a working DLL from the source the index
1065+ already carries: 265 exports matching upstream's `vulkan-1.def` name for name
1066+ and all `vk*`-prefixed, `DllMain` present, importing only ADVAPI32, CFGMGR32,
1067+ KERNEL32 and msvcrt. The descriptor's note argues a Windows loader must be a
1068+ DLL, not that it cannot be built.
1069+
1070+ **One constraint, found while sizing this and true of every descriptor.** A
1071+ platform table's `targets` APPENDS rather than replaces. `mcpp xpkg parse` on
1072+ today's `pkgs/c/compat.vulkan.lua` reports
1073+
1074+ ```
1075+ targets: [ "vulkan", "vulkan"]
1076+ ```
1077+
1078+ -- the base `{ ["vulkan"] = { kind = "lib" } }` and the linux
1079+ `{ ["vulkan"] = { kind = "shared", soname = ... } }` are two targets sharing a
1080+ name, silently. So a Windows entry naming a target `vulkan-1` would produce a
1081+ THIRD target rather than a replacement. The change has to either move the base
1082+ `targets` into each platform table, so each platform declares exactly one, or
1083+ keep the name `vulkan` and reach `vulkan-1.dll` through `soname`.
1084+
1085+ That a duplicate target name is accepted without a word is worth a look on its
1086+ own, independently of this entry.
1087+
1088+ ### 14.6 Not open: the Slang version pin
1089+
1090+ Recorded because it was on the plan and is resolved by fact rather than by work.
1091+ `xim:slang` publishes `2026.14.1` and nothing newer, `latest` points at it, and
1092+ `mcpp:plugins` declares `>=2026.14.1`. The floor is already at the index's
1093+ ceiling. Raising it means publishing a newer slang payload to `xim-pkgindex`
1094+ first -- four platforms and a GitCode mirror -- which is a packaging task, not a
1095+ pin edit.
0 commit comments