90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
"""Cognitive worker registry: independent toggles and isolation."""
|
|
|
|
from rune.orchestration.no_change import NoChangeGate, content_fingerprint
|
|
from rune.orchestration.proposals import ProposalSink
|
|
from rune.orchestration.records import ProposalRecord
|
|
from rune.orchestration.workers import CognitiveWorker, WorkerRegistry, WorkerResult
|
|
|
|
|
|
class _SpyWorker(CognitiveWorker):
|
|
def __init__(self, name: str, cadence: str, fail: bool = False):
|
|
self.name = name
|
|
self.cadence = cadence
|
|
self.fail = fail
|
|
self.runs = 0
|
|
|
|
def run(self, evidence, sink, gate):
|
|
self.runs += 1
|
|
if self.fail:
|
|
raise RuntimeError(f"{self.name} exploded")
|
|
fp = content_fingerprint({"evidence": evidence})
|
|
if not gate.should_run(fp):
|
|
return WorkerResult(self.name, self.cadence, 0, 1, True)
|
|
gate.record(fp)
|
|
sink.submit(ProposalRecord(worker=self.name, action="surface", confidence=0.8))
|
|
return WorkerResult(self.name, self.cadence, 1, 0, True)
|
|
|
|
|
|
def _ctx():
|
|
n1 = _SpyWorker("n1", "subconscious")
|
|
refl = _SpyWorker("reflection", "reflection")
|
|
reg = WorkerRegistry()
|
|
reg.register(n1)
|
|
reg.register(refl)
|
|
return reg, n1, refl
|
|
|
|
|
|
def test_run_all_runs_every_enabled_worker():
|
|
reg, _n1, _refl = _ctx()
|
|
results = reg.run_all(
|
|
[{"event_type": "x"}],
|
|
{"subconscious": ProposalSink(), "reflection": ProposalSink()},
|
|
{"subconscious": NoChangeGate(), "reflection": NoChangeGate()},
|
|
)
|
|
assert {r.worker for r in results} == {"n1", "reflection"}
|
|
|
|
|
|
def test_disabling_one_worker_does_not_affect_others():
|
|
reg, n1, refl = _ctx()
|
|
reg.disable("n1")
|
|
reg.run_all(
|
|
[{"event_type": "x"}],
|
|
{"subconscious": ProposalSink(), "reflection": ProposalSink()},
|
|
{"subconscious": NoChangeGate(), "reflection": NoChangeGate()},
|
|
)
|
|
assert n1.runs == 0
|
|
assert refl.runs == 1
|
|
|
|
|
|
def test_failing_worker_does_not_stop_others():
|
|
reg = WorkerRegistry()
|
|
bad = _SpyWorker("bad", "subconscious", fail=True)
|
|
ok = _SpyWorker("ok", "reflection")
|
|
reg.register(bad)
|
|
reg.register(ok)
|
|
results = reg.run_all(
|
|
[{"event_type": "x"}],
|
|
{"subconscious": ProposalSink(), "reflection": ProposalSink()},
|
|
{"subconscious": NoChangeGate(), "reflection": NoChangeGate()},
|
|
)
|
|
by_name = {r.worker: r for r in results}
|
|
assert by_name["bad"].ok is False
|
|
assert "bad exploded" in by_name["bad"].error
|
|
assert by_name["ok"].ok is True
|
|
assert ok.runs == 1
|
|
|
|
|
|
def test_run_all_is_deterministic_in_order():
|
|
reg, _n1, _refl = _ctx()
|
|
r1 = reg.run_all(
|
|
[{"event_type": "x"}],
|
|
{"subconscious": ProposalSink(), "reflection": ProposalSink()},
|
|
{"subconscious": NoChangeGate(), "reflection": NoChangeGate()},
|
|
)
|
|
r2 = reg.run_all(
|
|
[{"event_type": "x"}],
|
|
{"subconscious": ProposalSink(), "reflection": ProposalSink()},
|
|
{"subconscious": NoChangeGate(), "reflection": NoChangeGate()},
|
|
)
|
|
assert [x.worker for x in r1] == [x.worker for x in r2]
|