{"atom":"regression/whenever-supply-transform-chain","family":"regression","slug":"whenever-supply-transform-chain","header":{"atom":"regression/whenever-supply-transform-chain","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/whenever-supply-transform-chain.raku","counts":{"a":0,"f":0,"d":0,"c":0,"r":1,"n":0},"files":["atoms/regression/whenever-supply-transform-chain.grid"],"records":[["0001","","# Regression: `whenever $supplier.Supply.head(n)` (or .grep/.map/…) must carry the\n# Supply's TRANSFORM CHAIN onto the tap it registers. The whenever path built a tap\n# record with only the emit block — no chain — so:\n#   a) the transform was ignored (head(2) delivered every value, not 2), and\n#   b) worse, the chain never reported completion, so the tap's react source was\n#      never released and the enclosing `react` blocked forever.\n# (b) was masked whenever no worker thread existed: runReactLoop early-breaks on\n# `!gilHeld_`, so the hang only appeared once a `start {…}` had engaged the GIL —\n# which is exactly what S17-supply/head.t does before its final react test.\n# Contract: exit 0 + last line PASS. A hang (no output) is a failure.\nmy @fail;\n\n# a worker must exist first — that engages the GIL and disables runReactLoop's\n# \"no async emitter can exist\" early break, which is what exposed the deadlock.\nstart { 1 };\nsleep 0.2;\n\n# head(n) limits AND completes (this react used to hang forever)\n{\n    my $s = Supplier.new;\n    my @got;\n    react {\n        whenever $s.Supply.head(2) -> $v { @got.push($v) }\n        for 1..5 { $s.emit($_) }\n        done;\n    }\n    @fail.push(\"head-2 ({@got.join(',')})\") unless @got eqv [1, 2];\n}\n\n# head(1) — the exact shape from S17-supply/head.t (rakudo issue 3877)\n{\n    my $s = Supplier.new;\n    my int $seen;\n    react {\n        whenever $s.Supply.head(1) { ++$seen }\n        $s.emit(42);\n    }\n    @fail.push(\"head-1 seen=$seen\") unless $seen == 1;\n}\n\n# other chain ops travel too, and each tap gets its OWN state\n{\n    my $t = Supplier.new;\n    my @evens;\n    react {\n        whenever $t.Supply.grep(* %% 2) -> $v { @evens.push($v) }\n        for 1..6 { $t.emit($_) }\n        done;\n    }\n    @fail.push(\"grep ({@evens.join(',')})\") unless @evens eqv [2, 4, 6];\n}\n{\n    my $m = Supplier.new;\n    my @doubled;\n    react {\n        whenever $m.Supply.map(* * 2) -> $v { @doubled.push($v) }\n        for 1..3 { $m.emit($_) }\n        done;\n    }\n    @fail.push(\"map ({@doubled.join(',')})\") unless @doubled eqv [2, 4, 6];\n}\n\n# an unchained whenever still works (no regression to the plain path)\n{\n    my $p = Supplier.new;\n    my @all;\n    react {\n        whenever $p.Supply -> $v { @all.push($v) }\n        for 1..3 { $p.emit($_) }\n        done;\n    }\n    @fail.push(\"plain ({@all.join(',')})\") unless @all eqv [1, 2, 3];\n}\n\nif @fail { note \"FAILED: @fail[]\"; say 'FAIL' } else { say 'PASS' }","output PASS","r",["FAIL","PASS"],{"v":"disputed","w":"the reference implementation does not satisfy the program's own contract here (exit 0 with a last line of PASS) — it needs modules that are not installed, or exercises something only one engine implements; there is no reference observation to rule against","d":"2026-08-14 against rakudo-2026.07"},[["budget","20s"]]]]}