Thales
← research journal

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

DISMISSED — DSP-2 — PR #193's named cause for the VRP dispatch stall is falsified by its own table

status: dismissed as an OPEN ROW by panel run 31 (2026-09-30) · kept as a correction of record, because the note it corrects self-merges unreviewed · sibling to dsp-1-dispatch-liveness-attested-on-send.md, which it EXTENDS with new evidence rather than relitigating


Why this is a dismissal and not a row

The defects below are real, reproduced, and were each verified twice this run (change-review lens, then the chair). They are nonetheless not queue work:

  • Wrong latency class. research/**.md is on the auto-merge-records.yml fast path, so PR #193 self-merges with no human review the moment Actions returns (~2026-10-01, when the allowance resets). A row in open/ waits weeks. A file in dismissed/ merges at the same latency as the note itself.
  • Wrong shape. This is a one-file prose edit (~0.4 person-days in total), not an experiment with a mechanism, a data plan and a kill criterion.
  • The queue is over its cap. True post-merge count is 13 items against a cap of 12 (main 12, minus FIN-4 via #190, plus LAB-1 via #188, plus FRZ-1 via #192). The eviction nominee is RWG-1, whose leg 3 is that a drawdown-kill-switch liquidation of the meanrev book would be blocked by the runaway guard as armed. Trading a live-path safety row for a markdown proofread is indefensible.

Recorded here so that the note's own closing instruction — "so the next session does not re-derive either" — does not stop future routines investigating a cause that is dead.


What #193 gets right, and it is the part that matters

The measurement is sound. Every workflow_dispatch timestamp in the note's table reproduces to the second against the Actions API. The realised cost is real: data/state/vrp/order_log.jsonl carries a manage:close on a live SPY put credit spread on 2026-09-18, roughly two hours down the tape from where the schedule intends. The phenomenon is not in question. Only the cause, the counts, and the claimed detector coverage are.

Correction 1 — the "one structural difference" is not a difference

The note (:50-64) says: "VRP is the only one of the three workflows whose workflow_dispatch: carries an inputs: block… paper-trading.yml, paper-trading-meanrev.yml and skew-snapshot.yml all declare a bare workflow_dispatch: with no inputs."

grep -rn "inputs:" .github/workflows/ returns three hits: _alert.yml:29, paper-trading-vrp.yml:29, and paper-trading-meanrev.yml:31. meanrev carries an identical inputs: dry_run: {type: boolean, default: false} block.

The note's own table is the negative control. Its meanrev column shows meanrev dispatched within 2–4 seconds of momentum on 09-16, 09-17, 09-18, 09-23, 09-24, 09-25 and 09-29 — every one a day it bolds VRP as late, including the largest lag in the whole table (09-17, +301 min). The workflow carrying the claimed cause is never late. A stdin-block mechanism would also have to bite meanrev first: it is second in the dispatcher's argv loop, VRP third.

Stated precisely, because the bound matters. The meanrev block landed 2026-09-14 (fbdf0dd; its parent has zero dry_run hits), so the control covers 7 of the 9 bolded days — 09-16 onward — including two of the three material stalls (09-17 and 09-18) but not 09-11 (+99), which predates it. Seven consecutive dispatch days of a workflow with the identical block and no lateness is sufficient to falsify the lead; claiming it covers all nine, or all three stalls, would be the same overclaim this file exists to correct.

The offered fourth data point does not hold either. The note cites skew-snapshot.yml as a no-inputs control, "punctual on all 14 of its dispatch days". On 09-14, 09-15, 09-21 and 09-23 that workflow received no workflow_dispatch at all (only the late schedule run). A no-inputs workflow, dispatched alone, silently got zero dispatch on at least four weekdays in the same window — so "no inputs ⇒ dispatched reliably" is false, and the note's own denominator (14 of 19) already concedes five missing days while the prose reads as a clean control.

A third mechanism the note does not rule out is already in this directory. dsp-1:69-70 records that the 09-16 17:22:59Z re-dispatch was vrp-only, not the agent's three-workflow batch — a targeted human action. That is the exact timestamp #193 tables as VRP's morning dispatch arriving 166 min late. If those late events are vrp-only make-up dispatches, the failure is "the batch dispatched no VRP at all" — identical to the four skew misses and to 09-14 — and the lag column is measuring human repair latency, not a hung call. The note's confound section asserts only two explanations exist.

Consequence if uncorrected. The note asks the owner — the only person who may touch scripts/dispatch_workflows.sh — to add -f dry_run=false at VRP's call site only, an asymmetric change against a non-difference, and it demotes the argv-reorder experiment to "if the log is inconclusive" when that is now the only surviving hypothesis in its own hypothesis space.

Correction 2 — it resurrects a gate number a merged record retired

The note (:48) says the 09-18 close happened "49 trading days into its 126-day forward gate."

The pinned evaluator disagrees. thales --sleeve vrp forward-gate prints min_live_trading_days | 38 | >= 126, "clean clock since 2026-07-27". data/state/vrp/equity_history.jsonl has exactly 49 lines, first date 2026-07-13 — so 49 is the raw row count from activation, ignoring the pre-registered once-only clock reset whose entire purpose is that the pre-amendment days must not count.

That exact wrong number was retired eleven days earlier by a merged record on main: research/2026-09-18_two_forward_gate_numbers_were_wrong.md (PR #178, commit a325c3c) pins marks_since_activation 49 / marks_since_clean 39 / returns_since_clean 38. #193's own PR body says 38/126, so the note contradicts both main and itself.

Consequence if uncorrected. The next reader — the daily audit, which declares itself a standing re-reader of this PR, and the ~Jan-2027 forward-gate read — inherits a maturity figure 11 trading days ahead of the gate, on the one live sleeve whose activation that gate decides. The identical error already misled the 09-17 daily audit into printing "48/126"; #177 is open about it.

Correction 3 — incidence and detector coverage are both overstated

  • The prose says VRP was late on "7 of the 18 dispatch days". The table bolds 9: 09-04 +7, 09-11 +99, 09-16 +166, 09-17 +301, 09-18 +119, 09-23 +115, 09-24 +16, 09-25 +161, 09-29 +116. The PR body compounds it by listing 8 lag values against 9 dates.
  • The prose says open PR #174 "would have raised all seven of these days". #174's scripts/detect_dispatch_gap.py sets LATE_AFTER_MINUTES = 90 and puts failure in NON_DECISIVE_CONCLUSIONS. So 09-04 (+7) and 09-24 (+16) are under the bar, and 09-23 / 09-25 / 09-29 sit inside the billing refusal where every run concluded failure and is non-decisive by design. #174 raises 4 of the 9 — 09-11, 09-16, 09-17, 09-18.
  • The table holds 18 rows; 2026-09-03 → 09-29 holds 19 weekdays. The missing row is 09-14, the one day the dispatcher created no VRP dispatch at all (dsp-1:20). The reliability denominator silently excludes its worst day.

Correction 4 — four of the nine "late" days moved no decision at all

Not caught by the lens; found at review. Every sleeve's state stops at 2026-09-18 — data/state/vrp/equity_history.jsonl, data/processed/equity_history.jsonl and data/state/meanrev/equity_history.jsonl all end there, because Actions has refused every job since 09-21. So the table's rows for 09-23 (+115), 09-24 (+16), 09-25 (+161) and 09-29 (+116) are dispatch events for runs that never executed; they cost nothing. Netting this against corrections 1 and 3, the live-regime truth is 12 dispatch days, 5 lates, 3 material stalls (09-11 +99, 09-17 +301, 09-18 +119) — with 09-16's +166 attributable to a human re-dispatch per dsp-1. Not "7 of 18". The note's numerator and denominator are both wrong, in opposite directions.

What should replace the lead

The argv-position hypothesis is now the only survivor in the note's own hypothesis space, and the cheapest resolution is unchanged and owner-side: read logs/dispatch.log on the Mac for the affected mornings and establish whether the call hung, failed, or was never made. That distinguishes a stalled loop from a batch that dispatched no VRP — which is the fork corrections 1 and 4 open, and no repo-side evidence can close it. Note also that the proposed </dev/null remedy cannot be fixing a blocking stdin read under launchd: neither dispatch plist sets StandardInPath, whose documented launchd default is already /dev/null, and a stdin block has no natural 99 / 119 / 301-minute timeout followed by success.


Reopens if

  • (a) the owner (or any session) reads logs/dispatch.log and the evidence supports a stdin/inputs block after all — in which case correction 1 is wrong and this file must be retracted, not quietly left standing;
  • (b) a paper-trading-meanrev.yml dispatch is ever observed late by

    90 min while VRP's is on time, which would break the negative control this file rests on;

  • (c) #193 merges and the daily audit or any later memo quotes "49 trading days", "7 of 18", or "#174 covers all seven" as established fact — the correction has then failed to land and belongs in open/ as a row;
  • (d) the inputs: block is removed from paper-trading-vrp.yml on the strength of #193's lead and the lateness persists, which is the note's own experiment run backwards and settles it;
  • (e) dsp-1's clause (a) fires — a session trades after the 16:00 ET close with the dispatch layer having failed silently. It has not: the worst live-regime lag, 09-17 at 19:49:11Z, is 15:49 ET, inside the session.

Panel-Item: run 31 change-review lens (corrections 1–3) + adversarial review (correction 4); three candidates folded into one file by the reviewer's ruling that they are one edit to one unmerged document.


2026-10-01 append — THE CORRECTION LANDED, and the symptom got worse

Dated append rather than an edit above the line, so the sequence stays readable.

All four corrections are in #193, by its author's own hand. Commit d31afd0 on audit/vrp-dispatch-stall-2026-09-29 (2026-09-30 ~22:10Z) retracts the named cause and credits this panel run: "The mechanism proposed on 09-29 … is false. paper-trading-meanrev.yml:30-35 carries an identical dry_run inputs block … Found by research panel run 31 (PR #194); re-verified here by reading both workflow files." The PR title now carries the retraction too. Corrections 2 and 3 are folded in verbatim (38 not 49, late-day count 9 not 7, #174 covers 4 of 9 at its 90-minute bar), and the proposed remedy is narrowed to timeout 60 plus </dev/null, dropping the -f dry_run=false ask that rested on the dead hypothesis.

So reopen clause (c) is now moot in the good direction — the correction did not fail to land, and no row is owed. Clause (c) stays on the books only for the case where a later memo re-quotes the retired figures.

One correction of my own, for the record. #193's retraction says meanrev "has been punctual 18 of 18 days". That is one day longer than its inputs: block has existed — the block landed 2026-09-14 in fbdf0dd, whose parent has zero dry_run hits. The control is 7 of the 9 bolded late days, as stated above, not 18 of 18. The conclusion is unaffected and this is not worth a further round; recorded so the 2027 reader is not told the control is wider than it is.

The symptom has escalated, and it landed on the side of the fork this file named. #193's append records that on 2026-09-30 momentum and meanrev dispatched 2 s apart (14:42:13Z / 14:42:15Z) and VRP was NEVER dispatched — no workflow_dispatch run exists for it that day, checked at 22:05Z. That is no longer lateness; it is absence, and it is the third mechanism this file named above: "the failure is 'the batch dispatched no VRP at all' — identical to the four skew misses and to 09-14 — and the lag column is measuring human repair latency, not a hung call." Two instances of total absence (09-14, 09-30) now sit alongside the lateness. The logs/dispatch.log read is correspondingly more valuable, not less: it is the only thing that separates a hung call from a call never made, and loop position (VRP third and last in the plist argv) remains the standing correlate.

Nothing traded on 09-30 regardless — Actions refused every job that day too — so the absence cost nothing this time. It would have cost something on a working day.

Status of this PR, and a NOT-RUN. #194 is still unmerged at 2026-10-01 01:20Z. Its auto-merge check failed on 25c53f3 as a 5-second no-log runner refusal (the repo-wide billing outage; the guard allows this path — see the standing-down comment on the PR). I cannot spend the one re-run the drive-to-green rules allow: POST /actions/runs/36725279536/rerun returns 403 Resource not accessible by integration, so re-running is outside this routine's token scope. Recorded as a NOT-RUN rather than as a re-run spent.

Whether the 2026-10-01 allowance reset has taken effect is still untested: no workflow run has been created since 2026-10-01T00:00Z, and the last evidence either way is a Heartbeat Monitor run at 2026-09-30T23:23:21Z that failed in 9 s. The next natural signal is the ~14:35–14:45Z dispatch batch.

Also still open: #185 and #186 (the Actions-minute trims) were not merged before the new month's allowance began burning — the 09-30 daily audit called them "due tonight". They need a manual merge; auto-merge is itself an Actions workflow and cannot land its own fix. Each day they wait spends roughly one of the ~14 trading days #191 forecasts for October.