✓
Passing This code compiles and runs correctly.
Code
// TEST: an event that RE-ISSUES the obligation it consumes is not a discharger.
//
// The registry's discharge lookup returned the FIRST event whose declaration
// consumed the obligation. With two consumers — `use` (`<!open> -> <open!>`) and
// `shut` (`<!open>`, void) — first meant `use`, so a session's hang-up called
// the one that hands the obligation straight back and the resource survived its
// own release. The counter still went to zero, because a re-issue discharges the
// old handle before minting the new one.
//
// The compiled path has excluded this shape for as long as auto-discharge has
// existed (`eventReIssuesObligation`); only the interpreted path picked it up.
//
// Found by kopium-headless in koru-examples — an agent harness whose only tool
// is a Koru interpreter over a bridge. Its `append` verb is exactly `use`, and
// the session's hang-up wrote to the file it was closing.
//
// The bridge holds one resource; nothing in this flow discharges it, so the
// compiler inserts the hang-up. `shut` must be what runs.
//
// Both turns are flat tors reading the bridge from a store (probe_bridge_store's
// shape): a run chained inside an arm body is refused by KORU022's pipeline
// check, and its arms would attach to the wrong switch.
import std/bridge
import std/io
import std/store
import app/session
std/store:new(session, capacity: 2) { *std/bridge:Bridge<session!> }
[with]std/bridge:create(id: "s1", scope: "res"): br
|> std/store:insert(session) { br }
| row _ |> _
| full _ |> _
tor open-turn {}
open-turn = std/store:query(session)
! query br |> std/bridge:run(br, source: "open(path: \"x\")")
| result _ |> _
| unhandled-branch _ |> std/io:print.ln("FAIL: unhandled_branch")
| defined d1 |> std/io:print.ln("FAIL turn1 defined {{ d1:s }}")
| exhausted _ |> std/io:print.ln("FAIL exhausted")
| residue _ |> std/io:print.ln("FAIL: residue outcome")
| parse-error _ |> std/io:print.ln("FAIL parse")
| validation-error _ |> std/io:print.ln("FAIL validation")
| shape-error _ |> std/io:print.ln("FAIL shape")
| event-denied _ |> std/io:print.ln("FAIL denied")
| dispatch-error e2 |> std/io:print.ln("FAIL {{ e2.message:s }}")
| scope-not-found _ |> std/io:print.ln("FAIL scope")
tor use-turn {}
use-turn = std/store:query(session)
! query br |> std/bridge:run(br, source: "use(handle: \"res_1\")")
| result r2 |> std/io:print.ln("after use, bridge holds {{ r2.handles:d }} handle(s) — the re-issue released nothing")
| unhandled-branch _ |> std/io:print.ln("FAIL: unhandled_branch")
| defined d2 |> std/io:print.ln("FAIL turn2 defined {{ d2:s }}")
| exhausted _ |> std/io:print.ln("FAIL exhausted")
| residue _ |> std/io:print.ln("FAIL: residue outcome")
| parse-error _ |> std/io:print.ln("FAIL parse")
| validation-error _ |> std/io:print.ln("FAIL validation")
| shape-error _ |> std/io:print.ln("FAIL shape")
| event-denied _ |> std/io:print.ln("FAIL denied")
| dispatch-error e |> std/io:print.ln("FAIL {{ e.message:s }}")
| scope-not-found _ |> std/io:print.ln("FAIL scope")
open-turn()
use-turn()
Supporting Files
// Two events consume `<!open>`. Only one of them RELEASES anything.
//
// `use` is the transaction shape (`tx.exec`): it consumes the obligation and
// hands back another, so calling it lets go of nothing. `shut` consumes and
// creates nothing, so it is the only real discharger.
~import std/runtime
const std = @import("std");
~pub tor open { path: string } -> string<open!>
~proc open|zig {
_ = path;
return "res_1";
}
~pub tor use { handle: string<!open> } -> string<open!>
~proc use|zig {
std.debug.print("use() ran — obligation re-issued, nothing released\n", .{});
return handle;
}
~pub tor shut { handle: string<!open> }
~proc shut|zig {
std.debug.print("shut() ran — released '{s}'\n", .{handle});
}
~std/runtime:register(scope: "res") {
open(10)
use(1)
shut(1)
}
Actual
use() ran — obligation re-issued, nothing released
after use, bridge holds 1 handle(s) — the re-issue released nothing
shut() ran — released 'res_1'
[BRIDGE] Invoked 'shut' for handle 'res_1' [app.session:open]
Expected output
use() ran — obligation re-issued, nothing released
after use, bridge holds 1 handle(s) — the re-issue released nothing
shut() ran — released 'res_1'
[BRIDGE] Invoked 'shut' for handle 'res_1' [app.session:open]
Flows
flow ~new click a branch to expand · @labels scroll to their anchor
new (session, capacity: 2, source: *std/bridge:Bridge<session!>)
flow ~create click a branch to expand · @labels scroll to their anchor
create (id: "s1", scope: "res")
subflow ~open-turn click a branch to expand · @labels scroll to their anchor
query (session)
subflow ~use-turn click a branch to expand · @labels scroll to their anchor
query (session)
flow ~open-turn click a branch to expand · @labels scroll to their anchor
open-turn
flow ~use-turn click a branch to expand · @labels scroll to their anchor
use-turn
Test Configuration
MUST_RUN