From b6517f837a03d8d43be00e5dedd0f4478b2d73cd Mon Sep 17 00:00:00 2001 From: Olive Vaughn Date: Sun, 27 Sep 2026 23:38:26 -0400 Subject: [PATCH] Pin every dense track to the feature whose presence it follows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The absence mapping was asserted for two of the ten dense tracks. Each block hands `pack` an :absent predicate that derives a feature from a track INDEX, so the predicate and the vector literal beside it agree only by hand, in four places — and three of the four blocks had nothing checking them. Give each feature a DIFFERENT gap window, so a track wired to the wrong feature shows up as absence inside somebody else's window. A single shared window passes under any permutation, which is the failure port-plan mechanical fact #2 warns about: swap left for right and every part is still roughly where it belongs, so it survives inspection. Verified by mutation, since a test that cannot fail is worth nothing. Swapping the brow block's two tracks, and swapping eye-block tracks 1 and 3 while leaving 0 and 2 correct, both now fail loudly; neither was caught before. Also pin the teeth. They are their own feature so they can carry their own :area :teeth parameters, which means an occluded mouth sets no bit on them; they are dropped regardless because they stencil on :mouth-in and scene/finish drops a node whose stencil drew nothing. Both frames come from an unannotated reference clip, because the interior only draws on an open mouth and a frame the mouth was shut on would have passed for the wrong reason. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01B87NVmiU36qQmN9gmFYnJ9 --- frontend/test/arthur/flow/freeze_test.cljs | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/frontend/test/arthur/flow/freeze_test.cljs b/frontend/test/arthur/flow/freeze_test.cljs index e21aecb..7ab1ab2 100644 --- a/frontend/test/arthur/flow/freeze_test.cljs +++ b/frontend/test/arthur/flow/freeze_test.cljs @@ -402,6 +402,101 @@ (is (contains? drawn-nodes :mouth))) (is (empty? (scene/problems sc))))) +(deftest each-dense-track-follows-its-own-features-presence + ;; Every feature is given a DIFFERENT gap, so a track wired to the wrong one + ;; shows up as absence inside somebody else's window. One shared gap would pass + ;; under any permutation of the four tracks in the eye block, and that is the + ;; failure docs/port-plan.md warns about twice: swap left for right and every + ;; part is still roughly where it belongs, so it survives inspection. + ;; + ;; This is the mapping `pack` cannot check for itself. Each block hands it an + ;; :absent predicate that derives a feature from a track INDEX, so the predicate + ;; and the vector literal beside it have to agree by hand, in four places. + (let [windows {:eye-r (set (range 10 15)) + :eye-l (set (range 20 25)) + :brow-r (set (range 30 35)) + :brow-l (set (range 40 45))} + presence (into {} (map (fn [[id gap]] + [id (mapv #(not (contains? gap %)) + (range take/frames))])) + windows) + c (freeze/clip (assoc take/params :name "per-track-gaps") + (assoc @take/measured :presence presence)) + sc (:scene c) + at (fn [id path f] + (ch/value-at (get-in sc [:nodes id :channels path]) f (:store c))) + ;; Node, channel, and the feature whose gap it must follow. Every dense + ;; track of the eye, iris, brow and brow-position blocks appears once. + tracks [[:eye-r [:geom :pts] :eye-r] + [:eye-r-in [:geom :pts] :eye-r] + [:eye-l [:geom :pts] :eye-l] + [:eye-l-in [:geom :pts] :eye-l] + [:iris-r [:xform :pos] :eye-r] + [:iris-l [:xform :pos] :eye-l] + [:brow-r [:geom :pts] :brow-r] + [:brow-l [:geom :pts] :brow-l] + [:brow-r [:xform :pos] :brow-r] + [:brow-l [:xform :pos] :brow-l]]] + (is (empty? (scene/problems sc))) + (doseq [[id path owner] tracks + [feature gap] windows + f gap] + (if (= feature owner) + (is (ch/nothing? (at id path f)) + (str id " " path " is not absent at " f " with " feature " occluded")) + (is (not (ch/nothing? (at id path f))) + (str id " " path " follows " feature "'s gap at frame " f)))))) + +(deftest teeth-follow-the-mouth-through-their-stencil-and-not-through-a-mask + ;; Teeth are their own feature, so that they can carry their own :area :teeth + ;; parameters — which means an occluded MOUTH sets no absence bit on them. They + ;; do not need one: they are stencilled by :mouth-in, and scene/finish drops a + ;; node whose stencil drew nothing. So the coupling is real, and it is the + ;; stencil rule rather than the mask that enforces it. The rule itself is + ;; asserted in scene-test; what is pinned here is the wiring that relies on it. + (let [gap (set (range 40 60)) + teeth-in {;; The mouth's own inner ring standing in for a pixel-derived + ;; contour: real geometry and no nils, so every frame has a + ;; value that could be dropped. + :contours (:inner @take/measured) + :shown (vec (repeat take/frames true))} + clip (fn [nm extra] + (freeze/clip (assoc take/params :name nm) + (merge (assoc @take/measured :teeth teeth-in) extra))) + ref (clip "teeth-reference" nil) + occ (clip "teeth-mouth-gap" + {:presence {:mouth (mapv #(not (contains? gap %)) + (range take/frames))}}) + ;; Hoisted: the resolver caches its order and reuses its buffers, so the + ;; node ids come out before the next frame is asked for. + nodes-at (fn [c] + (let [r (scene/resolver (:scene c) (:store c) pal/index-of)] + (fn [f] (into #{} (map :node) (r f))))) + ref-at (nodes-at ref) + occ-at (nodes-at occ) + ;; The interior only draws on an OPEN mouth, so both frames are picked + ;; from the UNANNOTATED clip. Same articulation either side of the gap, + ;; which is what stops this passing on a frame the mouth was shut anyway. + open (filter #(contains? (ref-at %) :teeth) (range take/frames)) + inside (first (filter gap open)) + outside (first (remove gap open))] + (is (= :mouth-in (get-in (:scene occ) [:nodes :teeth :stencil])) + "teeth stop inheriting the mouth's absence if this stops being their stencil") + (is (some? inside) "no open-mouth frame inside the gap to test with") + (is (some? outside) "no open-mouth frame outside the gap to test with") + (when (and inside outside) + ;; Annotating the MOUTH sets no bit on the teeth: they are a feature of + ;; their own and nothing named them. + (is (not (ch/nothing? + (ch/value-at (get-in (:scene occ) [:nodes :teeth :channels [:geom :pts]]) + inside (:store occ))))) + ;; They are dropped anyway — :mouth-in drew nothing to clip them against. + (is (not (contains? (occ-at inside) :mouth-in))) + (is (not (contains? (occ-at inside) :teeth))) + ;; And the same articulation outside the gap still draws them. + (is (contains? (occ-at outside) :teeth))) + (is (empty? (scene/problems (:scene occ)))))) + (deftest an-undetected-frame-has-no-pose-at-all ;; A subject that was not on the frame has NO VALUE, which is different from a ;; part being switched off. The mask lands on every block of the freeze, so an