Skip to content

Commit 0bf562f

Browse files
fahchenclaude
andcommitted
fix: malformed listener is silently ignored; doc/comment cleanup
Round-6 follow-ups: - `Listener.safe_invoke/3` gains a final catch-all clause that returns `:ok` instead of raising `FunctionClauseError` when the runner state is configured with a value that is neither `nil`, a bare atom, nor a `{module, extras}` tuple. This honours the module's documented promise that "the runner never lets a misbehaving listener stop or destabilise an enactment". A dispatch-test case asserts a string listener is silently ignored without crashing the enactment. - `lib/coloured_flow/dsl/spec.md`: - Narrow the "requires `:storage`" sentence to `setup_flow!/0` and `insert_enactment!/{1,2}`; `start_enactment/{1,2}` works without storage as long as the caller supplies an enactment id. - Soften the compile-error claim — only duplicate-name and missing-colour-set violations are remapped to the originating macro callsite; other validator errors fall back to the `defmodule` callsite. - `lib/coloured_flow/dsl/builder.ex` comment for `compile_transition_action/2` now references the actual generated arity (`__action_for__/5`). - `lib/coloured_flow/dsl/function.ex` moduledoc renamed to `function/1` and `function/2` to match the actual macro arities. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 0cd9cdb commit 0bf562f

5 files changed

Lines changed: 35 additions & 7 deletions

File tree

lib/coloured_flow/dsl/builder.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ defmodule ColouredFlow.DSL.Builder do
391391
defp resolve_step(:unknown, descr, _colour_sets), do: descr
392392

393393
# Compile a single transition's `action do ... end` body into a
394-
# `__action_for__/4` clause. The body has access to:
394+
# `__action_for__/5` clause. The body has access to:
395395
#
396396
# * `ctx` — `ColouredFlow.Runner.Enactment.Listener.ctx()`
397397
# * `workitem` — the just-completed `%Workitem{}`

lib/coloured_flow/dsl/function.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
defmodule ColouredFlow.DSL.Function do
22
@moduledoc """
3-
`function/2` and `function/3` macros. See `ColouredFlow.DSL` for context.
3+
`function/1` and `function/2` macros. See `ColouredFlow.DSL` for context.
44
"""
55

66
alias ColouredFlow.DSL.ExpressionHelper

lib/coloured_flow/dsl/spec.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ configuration, pass `listener: {__MODULE__, extras}` as an option — the
9999
positional argument and exposed inside DSL blocks as the magic binding
100100
`extras`.
101101

102-
These functions only work when the host module passes `:storage` to
103-
`use ColouredFlow.DSL` (e.g. `storage: ColouredFlow.Runner.Storage.InMemory`).
104-
Without it, calling them raises a clear `ArgumentError`.
102+
`setup_flow!/0` and `insert_enactment!/{1,2}` only work when the host
103+
module passes `:storage` to `use ColouredFlow.DSL` (e.g.
104+
`storage: ColouredFlow.Runner.Storage.InMemory`); without it, calling
105+
them raises an `ArgumentError`. `start_enactment/{1,2}` is independent
106+
of `:storage` — it only needs an enactment id (or a handle returned by
107+
`insert_enactment!/{1,2}`) and the supervisor.
105108

106109
## Per-workflow options
107110

@@ -357,5 +360,7 @@ populates `action.outputs` from arc/guard analysis), and runs
357360
- function names are unique and result descrs are fully resolved,
358361
- termination criteria reference only `markings`.
359362

360-
Any failure raises a `CompileError` pointing to the offending macro
361-
call.
363+
Any failure raises a `CompileError`. Duplicate-name and missing-colour-set
364+
violations point back to the originating declaration via
365+
`{file, line}` metadata captured at macro expansion; other validator
366+
errors fall back to the `defmodule` callsite.

lib/coloured_flow/runner/enactment/listener.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ defmodule ColouredFlow.Runner.Enactment.Listener do
132132
do_invoke(module, callback, args ++ [nil])
133133
end
134134

135+
# Malformed listener value (PID, string, mis-shaped tuple, …). The runner
136+
# promises a misbehaving listener never destabilises the enactment, so
137+
# silently drop the call rather than letting `FunctionClauseError` bubble.
138+
def safe_invoke(_other, _callback, _args), do: :ok
139+
135140
defp do_invoke(module, callback, full_args) do
136141
if function_exported?(module, callback, length(full_args)) do
137142
try do

test/coloured_flow/runner/enactment/listener_dispatch_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,24 @@ defmodule ColouredFlow.Runner.Enactment.ListenerDispatchTest do
9595
end
9696
end
9797

98+
describe "malformed listener" do
99+
setup :setup_flow
100+
setup :setup_enactment
101+
102+
@tag initial_markings: []
103+
test "non-atom non-tuple value is silently ignored", %{enactment: enactment} do
104+
pid =
105+
start_supervised!(
106+
{ColouredFlow.Runner.Enactment, enactment_id: enactment.id, listener: "garbage"},
107+
id: enactment.id
108+
)
109+
110+
[wi] = get_enactment_workitems(pid)
111+
assert wi.state == :enabled
112+
refute_received {:on_enactment_start, _, _}
113+
end
114+
end
115+
98116
describe "lifecycle dispatch ({module, extras} listener)" do
99117
setup :setup_flow
100118
setup :setup_enactment

0 commit comments

Comments
 (0)