fix(ltl): keep the authored window on a step-bounded obligation

reduce decremented StepBound into the residual, so the trace reported the
remaining window rather than the authored one: a within(1915, "steps") showed
up as 1875 after 40 steps, and the replay UI renders that string verbatim. The
duration case was fixed when bounded windows were made to serialize their
resolved deadline; the step case was not, and withinFor's comment claimed
otherwise.

The window is now immutable and the closing observation is resolved once, which
mirrors Deadline exactly. A step counts observations the evaluator reduced,
not steps the runner executed, because a skipped step gave the property no
chance to discharge and transitional-step rate is itself policy-dependent.

Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
This commit is contained in:
pj committed 2026-08-14 17:50:19 +05:30
1 parent 0cc20539bc
commit a1e6bd7853
4 files changed
+329 -67

No files matched your search

+16 -12
View File
@@ -65,21 +65,25 @@ func pushNot(formula Formula) Formula {
return NextFormula{Inner: pushNot(concrete.Inner)}
case AlwaysFormula:
return EventuallyFormula{
Inner: pushNot(concrete.Inner),
StepBound: concrete.StepBound,
HasStepBound: concrete.HasStepBound,
Duration: concrete.Duration,
Deadline: concrete.Deadline,
HasDeadline: concrete.HasDeadline,
Inner: pushNot(concrete.Inner),
StepBound: concrete.StepBound,
HasStepBound: concrete.HasStepBound,
ExpiryObservation: concrete.ExpiryObservation,
HasExpiryObservation: concrete.HasExpiryObservation,
Duration: concrete.Duration,
Deadline: concrete.Deadline,
HasDeadline: concrete.HasDeadline,
}
case EventuallyFormula:
return AlwaysFormula{
Inner: pushNot(concrete.Inner),
StepBound: concrete.StepBound,
HasStepBound: concrete.HasStepBound,
Duration: concrete.Duration,
Deadline: concrete.Deadline,
HasDeadline: concrete.HasDeadline,
Inner: pushNot(concrete.Inner),
StepBound: concrete.StepBound,
HasStepBound: concrete.HasStepBound,
ExpiryObservation: concrete.ExpiryObservation,
HasExpiryObservation: concrete.HasExpiryObservation,
Duration: concrete.Duration,
Deadline: concrete.Deadline,
HasDeadline: concrete.HasDeadline,
}
default:
return NotFormula{Inner: formula}