@@ -793,8 +793,7 @@ step9_target_side_include_broadcast(PrepareState& state, TargetSideGather& gathe
793793}
794794
795795static std::expected<void , std::string>
796- step9_kernel_abi_interfaces_and_requirements (PrepareState& state, TargetSideGather& gather) {
797- namespace tsd = mcpp::targetside;
796+ step9_kernel_abi_interface_enumeration (PrepareState& state) {
798797 // INTERFACE ENUMERATION — THE RESOLUTION-TIME HALF OF THE CAPABILITY
799798 // MODEL (design 2026-09-20 §5.5; openkal SPEC 0.14 §3.3, §6.2).
800799 //
@@ -816,113 +815,116 @@ step9_kernel_abi_interfaces_and_requirements(PrepareState& state, TargetSideGath
816815 // writes `[kernel-abi]` reaches neither loop below, so this addition
817816 // changes no command line and no diagnostic for every project built
818817 // before it.
819- {
820- // THE LIST COMES FROM THE PACKAGE THAT RESOLVED AS THE LAYER, NOT
821- // FROM THE FIRST ONE IN THE GRAPH THAT STATED ONE. A graph may
822- // carry more than one candidate for a layer — a workspace member
823- // beside a dependency, a second implementation reached through a
824- // feature that did not activate — and only one of them is the
825- // provider this build resolved. Reading whichever came first in
826- // `packages` would compare a consumer's requirements against an
827- // implementation the build is not using, which is a wrong answer
828- // rather than a missing one.
829- std::vector<std::string> providedInterfaces;
830- std::string providerId;
831- for (auto & pkg : state.packages ) {
832- if (pkg.manifest .kernelAbiProvidesInterfaces .empty ()) continue ;
833- // `impl` is `name@version`; the name is what precedes the
834- // separator. A substring test would match `openkal` against
835- // `openkal-linux@0.15.0` and read one implementation's list
836- // as another's.
837- if (!state.resolvedTargetSide .kernelAbi .impl .empty ()) {
838- auto const & impl = state.resolvedTargetSide .kernelAbi .impl ;
839- const auto at = impl.find (' @' );
840- const auto implName = at == std::string::npos
841- ? impl : impl.substr (0 , at);
842- if (implName != pkg.manifest .package .name ) continue ;
843- }
844- providedInterfaces = pkg.manifest .kernelAbiProvidesInterfaces ;
845- providerId = pkg.manifest .package .name ;
846- break ;
818+ // THE LIST COMES FROM THE PACKAGE THAT RESOLVED AS THE LAYER, NOT
819+ // FROM THE FIRST ONE IN THE GRAPH THAT STATED ONE. A graph may
820+ // carry more than one candidate for a layer — a workspace member
821+ // beside a dependency, a second implementation reached through a
822+ // feature that did not activate — and only one of them is the
823+ // provider this build resolved. Reading whichever came first in
824+ // `packages` would compare a consumer's requirements against an
825+ // implementation the build is not using, which is a wrong answer
826+ // rather than a missing one.
827+ std::vector<std::string> providedInterfaces;
828+ std::string providerId;
829+ for (auto & pkg : state.packages ) {
830+ if (pkg.manifest .kernelAbiProvidesInterfaces .empty ()) continue ;
831+ // `impl` is `name@version`; the name is what precedes the
832+ // separator. A substring test would match `openkal` against
833+ // `openkal-linux@0.15.0` and read one implementation's list
834+ // as another's.
835+ if (!state.resolvedTargetSide .kernelAbi .impl .empty ()) {
836+ auto const & impl = state.resolvedTargetSide .kernelAbi .impl ;
837+ const auto at = impl.find (' @' );
838+ const auto implName = at == std::string::npos
839+ ? impl : impl.substr (0 , at);
840+ if (implName != pkg.manifest .package .name ) continue ;
847841 }
848- // A REQUIREMENT NOBODY ANSWERED IS SAID SO, because otherwise
849- // "yes" and "never asked" are the same reading.
850- //
851- // Three situations exist and two of them build: the provider
852- // states a list and it contains the requirement (build); it
853- // states a list and does not (refuse, below); it states nothing
854- // at all (build, and until this note, in silence). The third is
855- // deliberate --- `provides-interfaces` is younger than the
856- // implementations that exist, and a graph that has not adopted it
857- // must keep building --- but a consumer reading a green build
858- // cannot tell it from the first. One line closes that, and it
859- // costs nothing to a graph where the provider does declare.
860- std::size_t uncheckedRequirements = 0 ;
861- for (auto & pkg : state.packages ) {
862- const auto & need = pkg.manifest .kernelAbiRequiresInterfaces ;
863- if (need.empty ()) continue ;
864- if (providerId.empty ()) {
865- uncheckedRequirements += need.size ();
866- continue ;
867- }
868- auto missing = mcpp::targetside::interfaces_not_provided (
869- need, providedInterfaces);
870- if (missing.empty ()) continue ;
871- refusal::record (refusal::Code::InterfaceNotProvided);
872- std::string names;
873- for (auto const & mI : missing) {
874- names += " \n " ;
875- names += mI ;
876- }
877- // THE CODE IS PRINTED, THE WAY E0006 IS, BECAUSE SOMETHING
878- // READS THIS. A refusal that only a person can recognise
879- // forces every machine consumer to match prose --- and prose
880- // that a package's own compile error could coincidentally
881- // contain. The mcpp-index compatibility measurement
882- // distinguishes "this graph does not supply what the member
883- // asked for" from "the member did not build" on exactly this
884- // token, and that distinction decides whether a member counts
885- // against a compatibility figure.
886- // THE LABEL SAYS WHICH IMPLEMENTATION WAS RESOLVED, NOT
887- // "provided by". The missing names are listed immediately
888- // above it, and `provided by fakekernel` under `openkal.space`
889- // reads as the statement that fakekernel provides it --- the
890- // exact opposite of what this refusal is about. Read once,
891- // rendered, which is the only way that kind of defect is
892- // visible: every assertion on this message matches an
893- // identifier inside it, and an identifier is in the right
894- // place under either wording.
895- return std::unexpected (std::format (
896- " '{}' requires interfaces the resolved implementation does "
897- " not provide. [interface-not-provided]{}\n "
898- " the resolved implementation is {} ({} interface{}), "
899- " and none of those listed above is among them.\n "
900- " This is refused before anything is compiled "
901- " because dependency resolution is the earliest time the "
902- " question can be answered. Select an implementation that "
903- " provides them, or remove them from [kernel-abi] "
904- " requires-interfaces in '{}'." ,
905- pkg.manifest .package .name , names, providerId,
906- providedInterfaces.size (),
907- providedInterfaces.size () == 1 ? " " : " s" ,
908- pkg.manifest .package .name ));
842+ providedInterfaces = pkg.manifest .kernelAbiProvidesInterfaces ;
843+ providerId = pkg.manifest .package .name ;
844+ break ;
845+ }
846+ // A REQUIREMENT NOBODY ANSWERED IS SAID SO, because otherwise
847+ // "yes" and "never asked" are the same reading.
848+ //
849+ // Three situations exist and two of them build: the provider
850+ // states a list and it contains the requirement (build); it
851+ // states a list and does not (refuse, below); it states nothing
852+ // at all (build, and until this note, in silence). The third is
853+ // deliberate --- `provides-interfaces` is younger than the
854+ // implementations that exist, and a graph that has not adopted it
855+ // must keep building --- but a consumer reading a green build
856+ // cannot tell it from the first. One line closes that, and it
857+ // costs nothing to a graph where the provider does declare.
858+ std::size_t uncheckedRequirements = 0 ;
859+ for (auto & pkg : state.packages ) {
860+ const auto & need = pkg.manifest .kernelAbiRequiresInterfaces ;
861+ if (need.empty ()) continue ;
862+ if (providerId.empty ()) {
863+ uncheckedRequirements += need.size ();
864+ continue ;
909865 }
910-
911- if (uncheckedRequirements > 0 ) {
912- // THE IMPLEMENTATION IS NAMED FROM THE RESOLVED LAYER, not
913- // from whichever package happened to be first: the note has
914- // to say WHOSE silence this is, or a reader cannot act on it.
915- const auto & impl = state.resolvedTargetSide .kernelAbi .impl ;
916- mcpp::ui::info (" note" , std::format (
917- " kernel-abi interfaces: {} states none, {} requirement{} "
918- " unchecked" ,
919- impl.empty () ? std::string (" the resolved implementation" )
920- : impl,
921- uncheckedRequirements,
922- uncheckedRequirements == 1 ? " " : " s" ));
866+ auto missing = mcpp::targetside::interfaces_not_provided (
867+ need, providedInterfaces);
868+ if (missing.empty ()) continue ;
869+ refusal::record (refusal::Code::InterfaceNotProvided);
870+ std::string names;
871+ for (auto const & mI : missing) {
872+ names += " \n " ;
873+ names += mI ;
923874 }
875+ // THE CODE IS PRINTED, THE WAY E0006 IS, BECAUSE SOMETHING
876+ // READS THIS. A refusal that only a person can recognise
877+ // forces every machine consumer to match prose --- and prose
878+ // that a package's own compile error could coincidentally
879+ // contain. The mcpp-index compatibility measurement
880+ // distinguishes "this graph does not supply what the member
881+ // asked for" from "the member did not build" on exactly this
882+ // token, and that distinction decides whether a member counts
883+ // against a compatibility figure.
884+ // THE LABEL SAYS WHICH IMPLEMENTATION WAS RESOLVED, NOT
885+ // "provided by". The missing names are listed immediately
886+ // above it, and `provided by fakekernel` under `openkal.space`
887+ // reads as the statement that fakekernel provides it --- the
888+ // exact opposite of what this refusal is about. Read once,
889+ // rendered, which is the only way that kind of defect is
890+ // visible: every assertion on this message matches an
891+ // identifier inside it, and an identifier is in the right
892+ // place under either wording.
893+ return std::unexpected (std::format (
894+ " '{}' requires interfaces the resolved implementation does "
895+ " not provide. [interface-not-provided]{}\n "
896+ " the resolved implementation is {} ({} interface{}), "
897+ " and none of those listed above is among them.\n "
898+ " This is refused before anything is compiled "
899+ " because dependency resolution is the earliest time the "
900+ " question can be answered. Select an implementation that "
901+ " provides them, or remove them from [kernel-abi] "
902+ " requires-interfaces in '{}'." ,
903+ pkg.manifest .package .name , names, providerId,
904+ providedInterfaces.size (),
905+ providedInterfaces.size () == 1 ? " " : " s" ,
906+ pkg.manifest .package .name ));
924907 }
925908
909+ if (uncheckedRequirements > 0 ) {
910+ // THE IMPLEMENTATION IS NAMED FROM THE RESOLVED LAYER, not
911+ // from whichever package happened to be first: the note has
912+ // to say WHOSE silence this is, or a reader cannot act on it.
913+ const auto & impl = state.resolvedTargetSide .kernelAbi .impl ;
914+ mcpp::ui::info (" note" , std::format (
915+ " kernel-abi interfaces: {} states none, {} requirement{} "
916+ " unchecked" ,
917+ impl.empty () ? std::string (" the resolved implementation" )
918+ : impl,
919+ uncheckedRequirements,
920+ uncheckedRequirements == 1 ? " " : " s" ));
921+ }
922+ return {};
923+ }
924+
925+ static std::expected<void , std::string>
926+ step9_layering_and_requirement_checks (PrepareState& state, TargetSideGather& gather) {
927+ namespace tsd = mcpp::targetside;
926928 if (auto why = tsd::check_layering (state.resolvedTargetSide )) {
927929 refusal::record (refusal::Code::LayerOrdering);
928930 return std::unexpected (*why);
@@ -959,7 +961,11 @@ step9_kernel_abi_interfaces_and_requirements(PrepareState& state, TargetSideGath
959961 refusal::record (refusal::Code::HostCannotServe);
960962 return std::unexpected (state.unservedTargetDiagnosis );
961963 }
964+ return {};
965+ }
962966
967+ static void
968+ step9_pin_and_linkage_diagnostics (PrepareState& state) {
963969 // THE TARGET AND THE COMPILER ARE NOT BOUND TOGETHER, AND THE
964970 // TARGET ROW'S CONVENTION IS A FALLBACK RATHER THAN A RULE.
965971 //
@@ -1051,7 +1057,11 @@ step9_kernel_abi_interfaces_and_requirements(PrepareState& state, TargetSideGath
10511057 " target's system comes from the dependency graph: those "
10521058 " packages are compiled into this build as objects, and there "
10531059 " is no shared object to link against. The artifact is static." );
1060+ }
10541061
1062+ static std::expected<void , std::string>
1063+ step9_same_os_check_and_report (PrepareState& state) {
1064+ namespace tsd = mcpp::targetside;
10551065 // Reported, and reported HERE rather than recorded in a manifest field.
10561066 //
10571067 // A line a project writes states an intention, and it goes stale the
@@ -1138,7 +1148,11 @@ step9_kernel_abi_interfaces_and_requirements(PrepareState& state, TargetSideGath
11381148 }
11391149 mcpp::ui::info (" Target" , tsd::format_report (
11401150 state.resolvedTargetSide , reportedTargetName, mcpp::log::is_verbose ()));
1151+ return {};
1152+ }
11411153
1154+ static std::expected<void , std::string>
1155+ step9_platform_sdk_closure_visibility (PrepareState& state) {
11421156 // CLOSURE VISIBILITY — design §6. Distinct from the five-layer
11431157 // report above: a platform dependency is not a LAYER (no engine
11441158 // vocabulary names it, and `mcpp.targetside` — the pure, layer-only
@@ -1196,6 +1210,22 @@ step9_kernel_abi_interfaces_and_requirements(PrepareState& state, TargetSideGath
11961210 return {};
11971211}
11981212
1213+ static std::expected<void , std::string>
1214+ step9_kernel_abi_interfaces_and_requirements (PrepareState& state, TargetSideGather& gather) {
1215+ if (auto r = step9_kernel_abi_interface_enumeration (state); !r)
1216+ return std::unexpected (r.error ());
1217+
1218+ if (auto r = step9_layering_and_requirement_checks (state, gather); !r)
1219+ return std::unexpected (r.error ());
1220+
1221+ step9_pin_and_linkage_diagnostics (state);
1222+
1223+ if (auto r = step9_same_os_check_and_report (state); !r)
1224+ return std::unexpected (r.error ());
1225+
1226+ return step9_platform_sdk_closure_visibility (state);
1227+ }
1228+
11991229static std::expected<void , std::string> step9_layer_conditional_config (PrepareState& state) {
12001230 // ── L1b: conditional sections whose predicate names a target-side layer ──
12011231 //
0 commit comments