Sub-issue of #1133. The P7 answer, implemented: placement is decided by a resolver — a pure function of (what the artifact holds × what the profile says × what the platform can do) — mirroring WeightFormResolver, the pattern that already works. Consumers carry and obey; they never decide.
Scope
- New
sk.ainet.lang.memory.plan.AllocationResolver (@ExperimentalMemoryApi):
resolve(form: WeightForm?, format: Format, elementCount: Long, bytes: Long, profile: PlannerProfile, platform: StorageCapabilities): AllocationSpec
with a small injectable StorageCapabilities(supportsMappedFiles, …) whose current() reads PlatformStorage. Rules:
WeightResidency.MAPPED ∧ platform can map ∧ encoding kept-as-stored → MMAP_FILE / MODEL / immutable
- MAPPED but unmappable platform, or re-encoded form (dequant output bytes are not file bytes) →
profile.domainFor(residentBytes) + MODEL
- non-weight / HEAP →
profile.domainFor + caller's scope
MemoryPlan.kt: replace the hardcoded PlanTensor.allocation property (unconditional MMAP_FILE/MODEL, ignores form.residency, profile, and platform) with fun allocation(profile, platform) delegating to the resolver. The property has zero main-source consumers, so the signature change is safe.
- This gives
PlannerProfile.domainFor(bytes) its first real consumer.
Acceptance
- New
AllocationResolverTest (commonTest) + extended MemoryPlanTest/PlannerProfileTest: MOBILE_2GB mapped Q4_K → MMAP_FILE; same form on a no-mmap platform → falls back via domainFor; small bias → HOST_HEAP; a dequantized form never yields MMAP_FILE; domainFor threshold edges.
- Full pr-gate green (pure common code, all legs).
Sub-issue of #1133. The P7 answer, implemented: placement is decided by a resolver — a pure function of (what the artifact holds × what the profile says × what the platform can do) — mirroring
WeightFormResolver, the pattern that already works. Consumers carry and obey; they never decide.Scope
sk.ainet.lang.memory.plan.AllocationResolver(@ExperimentalMemoryApi):resolve(form: WeightForm?, format: Format, elementCount: Long, bytes: Long, profile: PlannerProfile, platform: StorageCapabilities): AllocationSpecwith a small injectable
StorageCapabilities(supportsMappedFiles, …)whosecurrent()readsPlatformStorage. Rules:WeightResidency.MAPPED∧ platform can map ∧ encoding kept-as-stored →MMAP_FILE/MODEL/ immutableprofile.domainFor(residentBytes)+MODELprofile.domainFor+ caller's scopeMemoryPlan.kt: replace the hardcodedPlanTensor.allocationproperty (unconditionalMMAP_FILE/MODEL, ignoresform.residency, profile, and platform) withfun allocation(profile, platform)delegating to the resolver. The property has zero main-source consumers, so the signature change is safe.PlannerProfile.domainFor(bytes)its first real consumer.Acceptance
AllocationResolverTest(commonTest) + extendedMemoryPlanTest/PlannerProfileTest: MOBILE_2GB mapped Q4_K →MMAP_FILE; same form on a no-mmap platform → falls back viadomainFor; small bias →HOST_HEAP; a dequantized form never yieldsMMAP_FILE;domainForthreshold edges.