- yfinance returned its errors as text, which the router counted as an answer, so the chain stopped and the text reached the analyst
- an empty result is checked against the vendor being reachable, so an outage is not reported as a company with no data
- a chain where every vendor is unavailable says so instead of ending the run
- xAI: Grok 4.6 and 4.5; DeepSeek: deepseek-flash (V4.1); Qwen: the 3.8 series; Mistral: the current dated snapshots
- every provider can name a model the picker does not list
- retired IDs stay valid, so existing configs run without a warning
- a price written as a range or a hedge is dropped like any other unusable value
- a field the model did not give is named as not provided, rather than omitted
- backtest takes --run-id, so an interrupted sweep continues
- an inverted date range or an empty ticker list is an error, not a clean zero-cell run
- a setup failure in a sweep reports one line instead of a traceback
- the remembered endpoint URL is offered back
- a round count set in the environment says it overrode the chosen research depth
- the run directory validates the ticker, and the report saves under results_dir
- the run says whether it resumed a saved run or started fresh
- gap filling keeps indicators on a continuous series, but put the previous session's open, high and low under an unsettled bar's date
- load_ohlcv takes fill_gaps, and the snapshot reads the frame as reported
- the fundamentals brief reached the model as a Python tuple
- analysts no longer emit a trade call that nothing reads
- a report that was not produced says so instead of appearing as a blank section
- holding_period_days sets it; the price request covers the calendar span those trading days occupy
- reflection states the window it judges, so a short one does not read as a failed thesis
- a Sell that fell counted as a miss and a Sell that rose as a hit
- Hold claims no direction, so it reports mean alpha and no hit rate
- the report names the window the outcomes were measured over
- a non-blank message is text, whatever it would mean as a Python literal
- the live layout renders on the alternate screen, so a tall layout does not scroll
- a run with no readable rating says so instead of looking like a normal result
- the state log keeps non-ASCII readable (#1081)
- the labelled rating decides, whatever dash separates it, and a scale the model echoed is not one
- prose naming several ratings is reviewed rather than read as the first word in the text
- an unreadable decision is tagged REVIEW everywhere instead of a tradeable Hold
- unrated decisions are counted apart from the backtest figures
- statements are limited to facts filed by the run's date, at the values filed then
- quarterly figures are picked by span, so a year to date total is not read as a quarter
- a fourth quarter is reported unavailable rather than derived
- opt in with sec_edgar in the fundamental_data chain; a non-filer falls through
- settling a past decision is per entry; a provider error leaves it pending
- an explicitly configured benchmark ticker is normalized like any other symbol (#1075)
- analysts, depth, provider, models and language prefill; prompts still shown
- values no longer offered by the current catalog are dropped
- environment variables keep skipping their step
- analysis stays the bare invocation; backtest runs a ticker and date grid
- selections name the models as the config does
- the pending note prints only when a cell is pending
- run_backtest runs the pipeline per cell into its own decision log and resumes by skipping logged cells
- summarize scores settled cells by rating on realized alpha
- settle_pending settles a ticker whose last decision would otherwise stay open
- PortfolioContext input, rendered once into state and read by the trader, risk and portfolio agents
- --portfolio JSON file on the CLI
- a run without it is never treated as a flat book
- the checkpoint signature keys on the portfolio
- the key prompt created .env with the default umask, typically readable by
other local users
- create it 0600 and tighten an existing file before writing the key; a
read-only file is still updated
- FRED and Alpha Vantage authenticate with a query parameter, and requests
quotes the full URL in HTTP, connection and timeout errors, so the key was
written into any log or traceback that recorded one
- route both vendors through one request helper that re-raises a requests error
as the same class with the key removed, carrying no request, response or
exception chain, each of which still held the URL #1324
- anonymous RSS allows about one request per minute per IP, so fetching each
subreddit separately hit a 429 and a 60s back-off on nearly every run
- search the subreddits as one combined feed at Reddit's full page size and
group posts by the subreddit each entry names; each subreddit keeps its own
limit, and a full page is not taken as evidence of absence
- drop the unused JSON search path, still blocked with a 403, and the
per-subreddit pacing; arguments after subreddits are keyword-only
- the sentiment prompt no longer asks for vote and comment counts, which the
RSS feed does not carry
- Yahoo news and the Reddit and StockTwits feeds serve only recent items, so a
historical window trimmed to nothing was reported as "no news" or "no
posts", and the sentiment analyst scored that silence as a neutral signal
- judge each empty window in one shared rule: it is a real absence only when
the feed's coverage reaches the window's first day and the window ends by
today; otherwise report it unavailable with where coverage starts
- coverage comes from the returned timestamps, which are newest-first on these
feeds, plus Reddit's one-week search lookback; merged global-news searches
prove no continuity and are bounded by the present alone
- state in the sentiment analyst that historical sentiment inputs are not
guaranteed to be point-in-time
- OpenAI: gpt-6-astra
- Google: gemini-3.8-flash and gemini-3.5-flash-lite
- Anthropic: claude-opus-5 and claude-fable-5-1
- models taken out of the picker stay valid through LEGACY_MODELS, so configs
that name them run without an unknown-model warning
- reasoning_effort was forwarded only to IDs matching gpt-5 or the o-series, so
GPT-6 models silently dropped the configured effort; match GPT-5 and later,
with a version boundary so unrelated IDs do not match
- Gemini Pro, 3.8+ and the -latest aliases reject thinking_level "minimal"
with a 400; send it only to numbered Flash models before 3.8 and map it to
"low" elsewhere, since aliases move between generations
- both manager prompts and both rating fields listed "materially conflicting"
as a Hold condition, and every bull/bear and risk debate is conflicting, so
a modest directional edge collapsed to Hold
- state one rule at all four sites: conflict alone is not a reason to Hold,
commit to the stronger side sized by how decisively it wins, and Hold only
when the evidence is still balanced after weighing or too thin for a call
- a modest bull edge now resolves to Overweight; a genuinely balanced debate
still resolves to Hold #1321
- a newest bar with no close made load_ohlcv reject the whole frame, so the
routing layer answered with its no-data sentinel: the caller lost the entire
price history and was told the symbol may be invalid, delisted or not
covered, when only the latest session had not settled
- treat a closeless newest bar as an unsettled session instead. The gap fill
already drops it, here and mid-series alike, so the frame ends at the last
settled bar; only a range with no close anywhere is still no data
- the staleness check keeps deciding whether what remains is recent enough, so
falling back cannot resurrect a long-dead series
- log which bars had no close and which date is being used as the latest close
- the console highlights numbers and URLs, so with colour enabled the rendered
output splits asserted substrings with escape codes and two tests fail
- strip the codes before asserting so the result no longer depends on where the
suite runs
- get_stock requests the full daily series up to today, so trimming to the
requested window is the only thing keeping bars after end_date out of a
historical run
- the trim caught every exception, warned, and returned the untrimmed body, so
a parse failure fed future prices into a backtest with no usable signal that
it had happened
- let a parse failure propagate instead: the routing layer already logs the
vendor failure, falls through to the next vendor, and surfaces the real error
if none can serve
- create_sentiment_analyst pre-fetches news, StockTwits and Reddit before
prompting, and TestSentimentAnalystAgent invoked it unstubbed, so every run
made live requests and a real 429 stalled the suite for minutes
- stub the three sources as the sibling prompt tests already do; the file drops
from ~84s to under a second, and the suite no longer depends on Reddit or
Yahoo being reachable
- a failed fetch and an empty search both returned [], so a 429 rendered as
'no posts found' and the sentiment analyst read throttling as real silence;
when every subreddit was throttled the summary asserted it outright
- a failed fetch now returns None and renders as unavailable, and the summary
only claims silence for subreddits actually searched
- raise the headerless-429 back-off to 60s, which is where a retry starts
succeeding; pay it at most once per run so three throttled subreddits do not
stall the analysis, and match the Retry-After cap to it #1295
- both fundamentals vendors accepted curr_date and ignored it, serving a
present-day company profile into a run dated in the past: yfinance via
Ticker.info, Alpha Vantage via OVERVIEW
- that profile has no historical vintage, not even name/sector/industry (which
move when a company renames or is reclassified), so a past curr_date now
withholds it and says why; live runs are unchanged
- the rule lives once in date_window next to the existing look-ahead helpers,
so switching data_vendors between the two cannot reintroduce the leak, and
the guard runs before the request rather than discarding a paid-for response
- point-in-time fundamentals for a past date already come from the balance
sheet, income statement and cash flow tools, which filter on curr_date #1300
- ElementTree does not resolve external entities, so the reported XXE flag
doesn't apply; the real residual is an unbounded read of untrusted network XML
- cap both the RSS and JSON reads at 5 MiB; overflow degrades to empty / RSS
fallback through the existing failure paths #1206#1276
- a valid Retry-After: 0 means retry at once but was treated as absent
(`or 5.0`) and waited 5s; honour it exactly now
- jitter our own headerless fallback and the inter-subreddit pacing so several
analyses sharing an IP don't retry in lockstep and re-collide on the limit;
keep the single-retry ceiling (more retries can't fix an exhausted IP budget) #1193
- the unconditional realtime pin 400s when curr_date is ahead of FRED's
US-Central date (a live run's local date), which the router then degrades to
a silent DATA_UNAVAILABLE — an Asia/Pacific run loses macro data
- clamp realtime_start/end to min(curr_date, FRED-today) via pytz Chicago;
a past curr_date pins unchanged, so historical look-ahead safety is preserved
- name the vintage in the empty-result message: widening the window can't fix a
series with no vintage coverage #1275
- GPT-5.6 (sol/terra/luna) is GA and OpenAI's recommended default; add it and
make gpt-5.6 (deep) / gpt-5.6-luna (quick) the defaults
- add GLM-5.3 and GLM-5.3-Flash, Zhipu's current flagship line