mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-07 15:32:21 +02:00
fix(runtime): restrict running effects to launch results and index history
Only the native detached launch (bg_job_id) or a bridge's explicit detachment marks an operation's own work as RUNNING. A listing that reports some other download/model/job as running settled normally; treating it as running left the claim pending forever and could block required artifacts. EffectHistory now indexes outcomes per effect once, removing a cubic scan in assessment over long run lineages. Adds adversarial coverage: browser page operations stay fail-closed through the real dispatcher with effects enabled (no claim, never dispatched), scheduler task triggers stay unverified admission, and assessment scales.
This commit is contained in:
@@ -191,9 +191,12 @@ def _execution(result: Any, facts: ProducerFacts) -> ExecutionOutcome:
|
||||
return ExecutionOutcome.INTERRUPTED
|
||||
if facts.timed_out:
|
||||
return ExecutionOutcome.TIMED_OUT
|
||||
# Only launch-shaped results mean this operation's own work continues:
|
||||
# the native detached launch, or a bridge's explicit detachment. A listing
|
||||
# that merely reports some other thing as "running" is not.
|
||||
if isinstance(result.get("bg_job_id"), str) and facts.exit_code == 0:
|
||||
return ExecutionOutcome.RUNNING
|
||||
if result.get("detached") is True or result.get("status") == "running" or result.get("running") is True:
|
||||
if result.get("detached") is True:
|
||||
return ExecutionOutcome.RUNNING
|
||||
denied = bool(result.get("blocked") or result.get("approval_required")
|
||||
or facts.failure_kind.endswith("_denied"))
|
||||
|
||||
@@ -642,14 +642,21 @@ class EffectHistory:
|
||||
raise ValueError("A settled effect outcome cannot be replaced")
|
||||
if outcome.execution is not ExecutionOutcome.RUNNING:
|
||||
settled.add(outcome.effect_id)
|
||||
# Derived indexes (not fields): outcomes per effect in sequence order.
|
||||
by_effect: dict[str, list[EffectOutcome]] = {}
|
||||
for outcome in sorted(self.outcomes, key=lambda o: o.sequence):
|
||||
by_effect.setdefault(outcome.effect_id, []).append(outcome)
|
||||
object.__setattr__(self, "_outcomes_by_effect", by_effect)
|
||||
object.__setattr__(self, "_claims_by_id", {c.effect_id: c for c in self.claims})
|
||||
|
||||
def claim(self, effect_id: str) -> EffectClaim | None:
|
||||
return next((c for c in self.claims if c.effect_id == effect_id), None)
|
||||
return self._claims_by_id.get(effect_id)
|
||||
|
||||
def latest_outcome(self, effect_id: str, before: int | None = None) -> EffectOutcome | None:
|
||||
matching = [o for o in self.outcomes if o.effect_id == effect_id
|
||||
and (before is None or o.sequence < before)]
|
||||
return max(matching, key=lambda o: o.sequence) if matching else None
|
||||
for outcome in reversed(self._outcomes_by_effect.get(effect_id, ())):
|
||||
if before is None or outcome.sequence < before:
|
||||
return outcome
|
||||
return None
|
||||
|
||||
def execution(self, effect_id: str, before: int | None = None) -> ExecutionOutcome:
|
||||
outcome = self.latest_outcome(effect_id, before)
|
||||
|
||||
Reference in New Issue
Block a user