Kat delikleri, sürükleme kapısı ve asma katın kendi çarpışması - #34
Merged
Conversation
Both kinds are multi-level machines that ran through sealed slabs. The host cuts floors for its own elevator and stair, and since the editor's `verticalOpening` capability it will cut them for any kind that declares one — these two declare it. `vertical-opening.ts` holds the part that is easy to get quietly wrong. Level elevations are rebased onto the machine's own level, the same trap `palletlift/levels.ts` documents: read absolutely, a lift on the first floor starts cutting the slabs below it, four metres off and plausible enough that nobody looks. Containment mirrors the host elevator — the floor a machine stands on is excluded, the floor it arrives at included. Anything else either cuts the ground out from under the machine or opens it into a closed ceiling. The pallet lift's span comes from its resolved service stops, so `fromLevelId`/`toLevelId` really clamp what gets pierced, and the synthetic fallback stops (placed outside a building, or clamped to one level) cut nothing rather than inventing a shaft. Its opening is the enclosure envelope, not `footprintM`: that one also wraps the control panel, which stands outside the cage on a single landing and never passes through a floor. The spiral's opening is round like its cage, and its span is its own rise — which floors it reaches follows from `travelHeight` rather than from a second field that would have to read elevations out of the scene. The capability key is spread rather than written into `capabilities` directly. The published `@pascal-app/core` predates it, so naming the key is a TS2353 against the peer CI installs — the `ItemCatalog` trap again, where a type-level break stopped 925 tests from loading at all. A spread bypasses the excess-property check, and the host reads the capability dynamically anyway, so an older host ignores it. A compat probe covers the failure that leaves: on such a host the machines quietly do not cut, and a hole that was never cut leaves no trace, so the probe counts lifts that should have pierced a floor against slabs that carry a `verticalOpening` hole and reports the mismatch. Tests assert the wrong answers: own floor cut, arrival floor left sealed, a clamped lift piercing the floor above its top stop, a sub-storey spiral cutting anything, and a rotated opening keeping its unrotated extents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T5bdAFduH4BkPCjvtJgFzn
Two gaps in the collision work, both of the kind that produces no error. **A placed object could be dragged into steel.** Every tool here gates placement through `clash.ts` in three dimensions. Nothing gated the drag afterwards: the host's move gate reads `floorPlaced.collides`, and every kind in this package must leave that off — the host's test is a plan rectangle with no Y, so it cannot tell a conveyor threading the walkway under a tunnelled bay from one driven through its uprights. Leaving it off left the drag unguarded entirely, so a conveyor placed correctly could be pulled into a rack a second later. `clashGuardedMove()` answers with the same `isClearAt` the placement gate uses, through the editor's new `movable.canMoveTo`. Twenty kinds declare it; route does not, because floor paint has no volume and `occupiedVolumes` gives it none. That is the rule the guard test encodes — what placement checks, the drag checks — and it reads the manifest rather than a list, so the twenty-first kind cannot be forgotten. **A mezzanine obstructed everything but itself.** `occupiedVolumes` has described one since it was written, so a rack could not be put into a mezzanine; the mezzanine tool only ever ran the host's plan-rectangle test. A deck could be drawn straight down through a six-metre run of racking. It now runs both, because they answer different questions: the plan rectangle is the drawn area, and the 3D test is the columns and the deck — not the air between them, which is the whole reason a mezzanine gets built. The test covers both halves; a gate that refuses a deck over short racking has broken the feature it was added to protect, and asking `gridColumnPositions` for a mid-bay point rather than guessing one keeps that half honest about what it is testing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T5bdAFduH4BkPCjvtJgFzn
ovurrsl
marked this pull request as ready for review
August 25, 2026 07:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Üç iş, hepsi aynı sınıftan: hata vermeyen eksiklikler.
1. Palet asansörü ve spiral konveyör kat döşemelerini deliyor
İki kind de çok katlı makine ama kapalı döşemelerin içinden geçiyorlardı. Host kendi asansörü ve merdiveni için döşeme kesiyor; editördeki
verticalOpeningyeteneğinden beri bunu bildiren her kind için kesiyor.fromLevelId/toLevelIdgerçekten kelepçeliyorentryHeight + travelHeight + paySessiz yanlışların durduğu yer
Kotlar makinenin kendi katına göre. Host, düğümü kendi katının kotuna yerleştiriyor. Kat kotları MUTLAK okunursa 1. kattaki bir asansör altındaki döşemeleri delmeye başlar — 4 metre kayık, ama makul görünen deliklerle.
palletlift/levels.tsaynı tuzağı zaten belgeliyor;vertical-opening.tsaynı yeniden tabanlamayı yapıyor.Kapsayıcılık host asansörünü yansıtıyor: oturduğu döşeme HARİÇ, vardığı döşeme DAHİL (
bottom < düzlem ≤ top). Alt uç kapsayıcı olsaydı her makine kendi ayağının altındaki zemini keserdi; üst uç dışlayıcı olsaydı en üst durakta kapalı bir tavana açılırdı.Yedek duraklar delik açmaz.
resolveLiftLevelsiki kattan azı çözülünce sentetik bir yedek kuyu döndürüyor — o çizim içindir. Onu delik aralığı saymak, bina dışına konmuş ya da tek kata kelepçelenmiş bir asansörün altından geçmediği döşemeleri kesmesi demekti.liftOpeningSpanbu durumdanulldönüyor.Delik
footprintMdeğil,enclosureXZ.footprintMkontrol panosunun taşmasını da sarıyor; pano kafesin dışında, tek katta duruyor ve kuyudan geçmiyor.Sarmalın deliği yuvarlak.
footprintMkareyi iki tanjant güdüğünü kapsayacak kadar büyütüyor, ama o güdükler giriş/çıkış kotunda — döşemeden geçen şey kafes silindiri."Hangi katlarda çıkışı olacak"
Sarmalda bu ayrı bir ALAN değil,
travelHeight'ten çıkıyor: makine oturduğu döşemeden toplam boyu kadar yükselir, arasında kalan her döşeme delinir. Kat kimliğiyle seçilen bir "varış katı" alanı kotu sahneden okumayı gerektirirdi —ParametricDescriptor'ınderivekancasınodesgörmüyor, yani ifade edilemiyor.travelHeightzaten ölçünün kendisi:pitchM,turnCountve geometri anahtarı onun üstünde duruyor.Palet asansöründe seçim zaten vardı (
fromLevelId/toLevelId, host asansör deseni) — eksik olan tek şey deliğin açılmasıydı.2. Sürükleme kapısı
Yerleştirme neyi denetliyorsa sürükleme de onu denetler.
Bu paketin araçları yerleştirmeyi
clash.tsüzerinden üç boyutta denetliyordu. Yerleşen nesne sonradan sürüklenebiliyordu ve orada hiçbir denetim yoktu: host'un sürükleme kapısıfloorPlaced.collides'a bakar, bu paketin her kind'ı onu kapalı tutmak zorunda — host'un testi plan dikdörtgenidir ve Y görmez, tünelli bir gözün altındaki yürüme yolunu dikmelerin içinden geçmekten ayıramaz. Kapalı tutmak sürükleme kapısını da kapatıyordu: doğru yerleştirilen bir konveyör, ertesi saniye rafın içine çekilebiliyordu.clashGuardedMove()yerleştirme kapısının kullandığıisClearAt'in aynısıyla cevap veriyor, editördeki yenimovable.canMoveToüzerinden (ovurrsl/editor#41). Yirmi kind bildiriyor; rota bildirmiyor — zemine çizilen boyadır,occupiedVolumesona hacim vermez, hacmi olmayan şeyin çarpışması da yoktur.Bekçi testi bunu manifest üzerinden yapıyor, elle tutulan bir listeden değil: eksiklik hata vermez (
canMoveTobildirmeyen bir kind derlenir, çalışır, yerleştirmesi denetlenir — yalnız sürüklemesi serbesttir), ve yirmi kind'a elle eklenen bir alan yirmi birincisinde unutulur.3. Asma kat kendi kendini de engelliyor
occupiedVolumesasma katı yazıldığı günden beri betimliyordu — yani bir raf asma katın içine konamıyordu. Ama asma kat aracı yalnız host'un plan dikdörtgeni testini koşuyordu: bir güverte altı metrelik bir raf sırasının içinden geçirilerek çizilebiliyordu.Artık ikisi birden koşuyor, çünkü iki ayrı soru:
canPlaceOnFloor'u çizilen alanı sorar. Bir asma katın sınırlayıcı dikdörtgeni kapladığı alanın tamamıdır.İkisi de gerekli: yalnız birincisi kalsa üstünde durduğu her şeyi reddederdi, yalnız ikincisi kalsa kolonlar bir rafın dikmesinin içinden geçebilirdi.
Test iki yarıyı da tutuyor. Kısa rafın üstünden geçen güverteyi reddeden bir kapı, korumak için eklendiği özelliği kırmıştır — ve o testte rafın konumu tahmin edilmiyor,
gridColumnPositions'a soruluyor: bir kolon zemine kadar gerçekten katıdır, üstüne oturan raf gerçek bir çarpışmadır, yani tahmin edilmiş bir konum testi ya yanlış sebeple geçirir ya da adım değişince kırar.Yayınlanmamış tipe karşı: neden yayılım (spread)
Hem
verticalOpeninghemcanMoveToeditörünintegrationdalında; yayınlanmış@pascal-app/coreikisini de tanımıyor. Anahtarı doğrudan yazmak, CI'ın kurduğu peer'e karşı TS2353 verir — bu paketItemCatalogyüzünden bir kez böyle kırılmış ve modül yüklenemediği için 925 test hiç koşmamıştı.Yayılım fazla-özellik denetimini atlıyor, host da yetenekleri çalışma zamanında dinamik okuyor, yani eski host'ta hiçbir şey olmuyor, yenisinde çalışıyor.
compat.tssondası bunun bıraktığı boşluğu kapatıyor: eski bir host'ta makineler sessizce delmiyor ve açılmamış bir delik hiçbir iz bırakmıyor, o yüzden sonda bir kat aralığı olan asansör sayısınıverticalOpeningkaynaklı deliği olan döşeme sayısıyla karşılaştırıp konsola tek satır yazıyor.Kapılar
Yeni testlerin hepsi makul görünen yanlış cevabı hedefliyor: oturduğu döşemeyi delmek, vardığı döşemeyi kapalı bırakmak, kelepçelenmiş bir asansörün üstündeki katı delmesi, kat altında kalan bir sarmalın bir şey delmesi, 90° dönmüş bir deliğin dönmemiş uzanımlarını koruması, ve kısa rafın üstünden geçen güvertenin reddedilmesi.