Skip to content

Correct the retry mechanism: trigger repetition, not restart-on-failure - #22

Merged
mspinola merged 1 commit into
mainfrom
claude/correct-restart-on-failure-advice
Aug 21, 2026
Merged

Correct the retry mechanism: trigger repetition, not restart-on-failure#22
mspinola merged 1 commit into
mainfrom
claude/correct-restart-on-failure-advice

Conversation

@mspinola

Copy link
Copy Markdown
Owner

The advice was wrong, and it was wrong in production

This README told you to give the bars task a restart on failure to turn "fire at 9pm" into "run the moment the Finals land". That setting fires when the scheduler cannot launch the action. It does not fire on a non-zero exit from the action — which is exactly what a --require-final defer is. Such a run is recorded as event 102, "Task Scheduler successfully finished", and no restart is scheduled.

Measured on the reference box, which had RestartCount 20 / RestartInterval PT15M set on that task:

2026-08-12 to 08-15 — the action returned exit 1 each night and the task was launched exactly once each night. Four consecutive nights, no retry, no bars captured. Events 111 and 322–324, the restart and queue events, never appeared at all.

It stayed invisible because the gate self-heals: a missed night is captured by the next run that finds Norgate ahead of the store, so the data was never permanently wrong, only a day late, and --check a week later looked fine.

The fix is a repetition on the trigger, which fires on schedule regardless of what the previous run returned:

schtasks /Create /TN "marketdata bars" /TR "<DIR>\run-prices.cmd" /SC DAILY /ST 20:55 /RI 15 /DU 0005:00

cotdata's guide has carried this correction since mspinola/cotdata#111. This page and docs/design.md were still giving the old advice — which is worse than no advice, because a reader who follows it believes the gate has a retry loop behind it when it has nothing.

docs/design.md mattered as much as the README here: the finals-gate design section explained the exit-code contract and then named restart-on-failure as the thing that consumes it. The gate's whole design assumes something re-runs it, so naming the wrong mechanism undercuts the design it is documenting.

Also: the equities producer had no documentation here

The Windows box runs three scheduled producers across two packages; this README described two. Adds Scheduling the equities half, covering:

  • Why it is a separate task, not a step in run-prices.cmd — the futures wrapper exits at its first command once the futures half has captured, so anything chained behind it is unreachable on the repeats; a single flaky Yahoo symbol fails the whole equities run and would otherwise abort the futures replica syncs; and Yahoo needs no finals gate, so it can run at 17:30 and stay clear of the 20:55 task's replica mirroring.
  • Why it takes no --require-final — futures-only, and refused rather than ignored (update.py exits 2). The protection comes from cadence: period="max" + full-parquet replace means every run restates the whole history, so a provisional bar captured today is overwritten tomorrow. That is why the task must be daily — the store keeps no per-bar provisional flag, so on a monthly cadence a bad capture sits unmarked for a month.
  • Why its retry goes inside the wrapper, not on the task, and why a repetition trigger is the wrong shape for this half specifically: with no gate to defer cheaply, every repeat after a success would re-fetch every symbol and re-run both replica syncs.

Tests

205 passed, 12 deselected (-m "not network"). Docs-only change; no source touched.

🤖 Generated with Claude Code

This README told you to give the bars task a **restart on failure** to turn "fire
at 9pm" into "run the moment the Finals land". That does not work, and it did not
work in production.

Task Scheduler's "if the task fails, restart every N minutes" fires when the
scheduler cannot LAUNCH the action. It does not fire on a non-zero exit code from
the action -- which is exactly what a `--require-final` defer is. A run whose
action returns 1 is recorded as event 102, "Task Scheduler successfully finished",
and no restart is scheduled.

Measured on the reference box, which had RestartCount 20 / RestartInterval PT15M
set on that task: 2026-08-12 to 08-15, the action returned exit 1 each night and
the task was launched exactly once each night. Four consecutive nights, no retry,
no bars captured. Events 111 and 322-324 never appeared at all.

The failure stayed invisible because the gate self-heals -- a missed night is
captured by the next run that finds Norgate ahead of the store -- so the data was
never permanently wrong, only a day late, and `--check` a week later looked fine.

The fix is a repetition on the trigger, which fires on schedule regardless of what
the previous run returned. cotdata's guide has carried the correction since #111;
this page and docs/design.md were still giving the old advice, which is worse than
no advice: a reader who follows it believes the gate has a retry loop behind it.

Also documents the equities producer, which had no coverage here at all. It is a
separate task rather than a step in run-prices.cmd for three reasons, all of which
survive the change above -- the futures wrapper exits early by design, the two
halves fail differently, and Yahoo needs no finals gate. Includes why it takes
neither --require-final (refused, not ignored) nor --metadata, why it must be
daily rather than weekly, and why its retry belongs inside the wrapper rather than
on the task.

"Two producers" on the Windows box is now three scheduled producers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mspinola
mspinola force-pushed the claude/correct-restart-on-failure-advice branch from 63cb9e1 to a8a42c8 Compare August 21, 2026 12:47
@mspinola
mspinola merged commit 62fca61 into main Aug 21, 2026
5 checks passed
@mspinola
mspinola deleted the claude/correct-restart-on-failure-advice branch August 24, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant