Skip to content

[defect·P2] consider's own live-resolved prices are reported as mode=agent_feed, not engine_fetch #654

Description

@atomchung

Status

Implementation

Milestone

Current: M1

Owning outcome

price_feed.provenance.mode on the consider lane tells the truth about who priced the book — engine_fetch when the engine's own live resolution did, agent_feed only when an agent actually handed back an envelope through --prices.

Current scope

_consider_price_feed_status (skills/fomo-kernel/engine/review.py:3679, inside cmd_consider) computes:

mode=("agent_feed" if supplied else ("engine_fetch" if priced else "unavailable")),

supplied is cmd_consider's local feed variable, which is set from either of two mutually exclusive sources (review.py ~6661-6678):

if args.prices:
    feed = price_feed.load(...)                      # a real agent envelope
else:
    feed, market_bundle = _resolve_consider_prices(...)  # #605 section E: the engine's OWN live resolution, reshaped into feed format

_resolve_consider_prices never touches an agent-supplied envelope — it calls market_data.resolve(request, root=root) with no feed= kwarg, which is exactly the engine's own Yahoo/cache path, then wraps the result through market_data.to_price_feed_envelope so it can flow through the same downstream lane a supplied envelope uses. Because the mode selector only checks whether the variable feed is truthy — never which of the two branches produced it — an ordinary consider call with no --prices flag at all, whose own live resolution succeeds, is reported as mode: "agent_feed".

Contrast with the equivalent selector on the prepare/review lane, trade_recap.py's main() (~line 2677):

mode=("unavailable" if px is None else ("agent_feed" if feed else "engine_fetch")),

This checks "was anything actually priced" (px is None) before checking whether an envelope variable is truthy, so the review lane does not have this defect — confirmed by tracing market_data.resolve's own precedence rule ("a supplied envelope answers for everything it declares and no Yahoo request is made at all"), which makes engine-fetch and agent-supply mutually exclusive there too, but the selector's order is what actually protects it.

Reproduced

Using this repository's own existing fake-provider technique (tests/test_consider.py's _FAKE_PROVIDER / _with_fake_provider, unmodified — just driven from a standalone script instead of a new test), against skills/fomo-kernel/mock/sample_momentum.csv, with no --prices argument at all:

$ python3 review.py consider sample_momentum.csv --root <tmp> \
    --premise '{"ticker": "NVDA", "side": "buy", "price": 1000.0, "qty": 5}'

provider.log (the fake Yahoo transport) shows one real batch request for the full 11-symbol universe — proving live resolution actually ran, not a supplied envelope:

AMAT,AMD,ARM,AVGO,MRVL,MU,NVDA,PLTR,TSM

The command's own JSON output:

"price_feed": {
  "provenance": {
    "mode": "agent_feed",
    "source": "yahoo (engine resolver)",
    "sources_by_ticker": {"AMD": "yahoo (engine resolver)", "MRVL": "yahoo (engine resolver)", "NVDA": "yahoo (engine resolver)"},
    "coverage": {"requested_n": 3, "priced_n": 3, "missing": []},
    ...
  }
}

No --prices flag was ever passed. "source": "yahoo (engine resolver)" is market_data.to_price_feed_envelope's own internal label for an engine-resolved bundle — the record is self-contradictory: mode says an agent supplied this, source says the engine resolved it itself.

What this is not

This is not the narrower posture #652 asked about (engine retrieval succeeding and the agent separately supplying an fx-only envelope, blended into one run) — that specific combination is unreachable, because args.prices truthy/absent selects exactly one of the two branches above; they never run in the same call. Two things worth recording about the fx-only case specifically, since I traced it while investigating this:

So the real, user-visible defect is narrower in cause (one selector's check order) but broader in reach (any ordinary unsupplied consider call whose own resolution succeeds) than #652 speculated.

Smallest compatible cut (not yet designed in full — a starting point, not a ruling)

  • Give cmd_consider an explicit signal for which of the two branches produced feed (for example, an origin string threaded from the if args.prices: / else: split down to _consider_price_feed_status), and key mode off that rather than off supplied's truthiness — mirroring trade_recap.main()'s existing, correct "was anything priced" precedence would also close this, but loses the "an envelope was supplied but priced nothing" distinction [implementation·M1] Yahoo unavailable on a consider call means recover the prices, not answer on cost #629's refusal currently depends on (recovery.attempted), so the two signals likely need to stay separate rather than collapsing into one check.
  • No change to trade_recap.py's selector — it is already correct.
  • A test driving the real CLI with no --prices against an injected/fake provider (this repository's own established pattern, tests/test_consider.py::_with_fake_provider) should pin mode == "engine_fetch" for that case; none currently exists — the closest tests (test_n_the_route_really_resolves_and_prices_the_book_it_reasons_about and siblings) assert on evaluation.basis/consequence, never on price_feed.provenance.mode.

Current evidence

Found and reproduced during #652 (closed by the PR referenced there); repro above run against main@22417a7.

Dependencies

#652 (the issue whose "is this reachable" question this answers), #605 (introduced _resolve_consider_prices, the source of the second feed origin), #629 (owns the refusal that currently hides the fx-only sub-case in practice).

Privacy

Fictional fixtures only (sample_momentum.csv, a fake in-process provider). No real trade data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions