The codebase had no machine-readable signal for “does this node type need a Krill server runtime?” Recipe / FTUE work in #237 had nothing to gate on, so everything was treated as server-required by default.
Per-node-type metadata lives in 37 JSON resources under shared/src/commonMain/resources/KrillApp.*.json and is deserialised into KrillFeature (in krill-sdk, the OSS SDK). The class had every other LLM-oriented capability flag (llmActsOnExternalWorld, llmCanCreateChildren, …) but no flag for the server-vs-client split.
"requiresServer": true|false to all 37 KrillApp.*.json files. Insertion was textual right after "state": to keep diffs single-line and key order stable.KrillApp (root sealed class), KrillApp.Client / KrillApp.Client.About (placeholders for app-vs-server identification, not user-facing per @bsautner), and the four user-facing KrillApp.Project.* types except Project.Camera. The other 30 are server-required.shared/src/jvmTest/.../RequiresServerFlagTest.kt) reads each JSON via JsonObject and asserts (a) the key exists and is boolean, (b) the partition matches the expected file-name set, (c) the file count is exactly 37 (forces an update when types are added/removed).krill-sdk patch release before krill can consume it; the JSON-side guard ships independently.assertEquals(37, krillAppFiles.size)) forces anyone adding a new KrillApp data object to update the expected-server-free set in the test, which forces a deliberate “is this server-free?” decision rather than letting a default leak in.JsonObject instead of KrillFeature is the load-bearing trick: fastJson.ignoreUnknownKeys = true would silently drop a missing field on a typed bind, so a typed test would have given a false-pass when a JSON regressed. Raw-JSON tests for “every file declares X” assertions should always parse as JsonObject rather than the typed DTO, for the same reason.@Serializable data object Foo : KrillApp() and a KrillApp.Foo.json resource will fail this test until they declare requiresServer and decide which side of the split it belongs on.