Spaces:
Sleeping
fix: close four review minors on the attention-ordered brief
Browse files- tone_strip._reading split on the Unicode arrow only while _SERIES_RE
already accepted "->" as well, so an upstream ASCII normalisation would
have kept the rate series working while the plain-language reading
silently vanished. Both halves of the parser are now tolerant.
- test_kpi_footnote_html_is_byte_identical_without_source_url copied the
footnote composition out of _render_quarter_kpis and asserted on the
copy, so changing the separator left it green while it claimed to be a
regression net. Replaced with a spy on verdict.st.markdown driving the
real renderer -- confirmed to fail when the separator is changed, which
the old test did not. test_kpi_source_note_is_exact_empty_string...
is kept as-is.
- attention_view._evasion_deltas duplicated evasion_deltas; it now
delegates, with a test pinning that the two agree across every delta
shape including a non-string kind.
- Roadmap: record explicitly that the consensus-archive work must add a
live reader of display_policy["market_expectations_aligned"]. This
branch deleted market_read.py, the flag's only (dead) consumer, and no
roadmap item was carrying the requirement.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@@ -58,10 +58,10 @@ def _fmt_period(period: str) -> str:
|
|
| 58 |
|
| 59 |
|
| 60 |
def _evasion_deltas(brief: dict) -> list[dict]:
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
|
| 66 |
|
| 67 |
def _heatmap(clusters: list[dict]) -> str:
|
|
|
|
| 58 |
|
| 59 |
|
| 60 |
def _evasion_deltas(brief: dict) -> list[dict]:
|
| 61 |
+
"""Kept as the in-module name the heatmap view reads; the selection itself
|
| 62 |
+
lives in `evasion_deltas` below, which The Brief's summary panel and
|
| 63 |
+
verdict.brief_block_order also call."""
|
| 64 |
+
return evasion_deltas(brief)
|
| 65 |
|
| 66 |
|
| 67 |
def _heatmap(clusters: list[dict]) -> str:
|
|
@@ -31,6 +31,10 @@ from dashboard.theme import (
|
|
| 31 |
# tone_drift.compute_tone_trend's `trajectory = "→".join(...)`.
|
| 32 |
_SERIES_RE = re.compile(r"\d+(?:\.\d+)?(?:\s*(?:→|->)\s*\d+(?:\.\d+)?)+")
|
| 33 |
_NUMBER_RE = re.compile(r"\d+(?:\.\d+)?")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
def parse_rates(computed_metric: str) -> list[float]:
|
|
@@ -56,9 +60,10 @@ def tone_deltas(brief: dict) -> list[dict]:
|
|
| 56 |
def _reading(computed_metric: str) -> str:
|
| 57 |
"""The analyser's plain-language reading, e.g. "more cautious".
|
| 58 |
|
| 59 |
-
tone_drift formats it as the trailing "→ <reading>" segment.
|
|
|
|
| 60 |
"""
|
| 61 |
-
parts = str(computed_metric or "")
|
| 62 |
return parts[-1].strip() if len(parts) > 1 else ""
|
| 63 |
|
| 64 |
|
|
|
|
| 31 |
# tone_drift.compute_tone_trend's `trajectory = "→".join(...)`.
|
| 32 |
_SERIES_RE = re.compile(r"\d+(?:\.\d+)?(?:\s*(?:→|->)\s*\d+(?:\.\d+)?)+")
|
| 33 |
_NUMBER_RE = re.compile(r"\d+(?:\.\d+)?")
|
| 34 |
+
# Same two arrow forms _SERIES_RE accepts. Splitting on the Unicode arrow
|
| 35 |
+
# alone would keep the rate series working under an upstream ASCII
|
| 36 |
+
# normalisation while silently dropping the plain-language reading.
|
| 37 |
+
_ARROW_RE = re.compile(r"→|->")
|
| 38 |
|
| 39 |
|
| 40 |
def parse_rates(computed_metric: str) -> list[float]:
|
|
|
|
| 60 |
def _reading(computed_metric: str) -> str:
|
| 61 |
"""The analyser's plain-language reading, e.g. "more cautious".
|
| 62 |
|
| 63 |
+
tone_drift formats it as the trailing "→ <reading>" segment. Both arrow
|
| 64 |
+
forms _SERIES_RE accepts are honoured here too.
|
| 65 |
"""
|
| 66 |
+
parts = _ARROW_RE.split(str(computed_metric or ""))
|
| 67 |
return parts[-1].strip() if len(parts) > 1 else ""
|
| 68 |
|
| 69 |
|
|
@@ -78,6 +78,15 @@ switched off by the app's own fail-closed alignment gate. No UI change can turn
|
|
| 78 |
it on. Only a point-in-time consensus archive carrying our own verifiable
|
| 79 |
capture timestamp can.
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
**2. No base rate exists.** `analysis/textdiff.py:665-673` diffs the latest
|
| 82 |
10-Q against exactly one prior 10-Q. Significance thresholds are module
|
| 83 |
constants (`textdiff.py:31-33`, `:61-62`). `compute_lexicon_trend`
|
|
|
|
| 78 |
it on. Only a point-in-time consensus archive carrying our own verifiable
|
| 79 |
capture timestamp can.
|
| 80 |
|
| 81 |
+
Follow-on requirement, carried here explicitly: **the archive work is not done
|
| 82 |
+
until a live screen reads `display_policy["market_expectations_aligned"]`.**
|
| 83 |
+
The flag is produced on every run and, as of the attention-ordered-brief branch,
|
| 84 |
+
read by nothing -- that branch deleted `dashboard/market_read.py`, its only
|
| 85 |
+
consumer, and that consumer was dead code (no screen routed to it). A flag
|
| 86 |
+
computed and never consulted is indistinguishable from a flag that is wrong, so
|
| 87 |
+
whichever work item lands the consensus archive must ADD a real consumer, not
|
| 88 |
+
merely flip the hardcoded `False` at `agent/graph.py:665` to a computed value.
|
| 89 |
+
|
| 90 |
**2. No base rate exists.** `analysis/textdiff.py:665-673` diffs the latest
|
| 91 |
10-Q against exactly one prior 10-Q. Significance thresholds are module
|
| 92 |
constants (`textdiff.py:31-33`, `:61-62`). `compute_lexicon_trend`
|
|
@@ -443,3 +443,38 @@ def test_render_keeps_zone_3_inside_the_expander_and_everything_else_above_it(
|
|
| 443 |
# Order is preserved within each side, not merely partitioned.
|
| 444 |
assert above == [k for k in order if k not in set(mod.ZONE_3)]
|
| 445 |
assert inside == [k for k in order if k in set(mod.ZONE_3)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
# Order is preserved within each side, not merely partitioned.
|
| 444 |
assert above == [k for k in order if k not in set(mod.ZONE_3)]
|
| 445 |
assert inside == [k for k in order if k in set(mod.ZONE_3)]
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def test_private_evasion_deltas_delegates_to_the_public_one():
|
| 449 |
+
"""Two near-identical four-line comprehensions answered the same question.
|
| 450 |
+
_evasion_deltas now delegates; this pins that they agree on every shape a
|
| 451 |
+
persisted delta list can take -- missing key, None, empty, non-dict
|
| 452 |
+
entries, and a non-string `kind` (where the two spellings,
|
| 453 |
+
`d.get("kind") == ...` and `str(d.get("kind") or "") == ...`, could
|
| 454 |
+
conceivably have diverged)."""
|
| 455 |
+
from dashboard.attention_view import _evasion_deltas, evasion_deltas
|
| 456 |
+
|
| 457 |
+
cases = [
|
| 458 |
+
{},
|
| 459 |
+
{"quarter_deltas": None},
|
| 460 |
+
{"quarter_deltas": []},
|
| 461 |
+
{"quarter_deltas": [{}]},
|
| 462 |
+
{"quarter_deltas": [{"kind": None}]},
|
| 463 |
+
{"quarter_deltas": [{"kind": ""}]},
|
| 464 |
+
{"quarter_deltas": [{"kind": 5}]},
|
| 465 |
+
{"quarter_deltas": [{"kind": True}]},
|
| 466 |
+
{"quarter_deltas": [{"kind": ["recurring_evasion"]}]},
|
| 467 |
+
{"quarter_deltas": ["not-a-dict", None, 7]},
|
| 468 |
+
{"quarter_deltas": [
|
| 469 |
+
_delta("recurring_evasion", "margin"),
|
| 470 |
+
_delta("risk_added", "supply"),
|
| 471 |
+
_delta("recurring_evasion", "pricing"),
|
| 472 |
+
]},
|
| 473 |
+
]
|
| 474 |
+
|
| 475 |
+
for brief in cases:
|
| 476 |
+
assert _evasion_deltas(brief) == evasion_deltas(brief), brief
|
| 477 |
+
|
| 478 |
+
# Not vacuous: the last case must actually select something, in order.
|
| 479 |
+
selected = _evasion_deltas(cases[-1])
|
| 480 |
+
assert [d["term"] for d in selected] == ["margin", "pricing"]
|
|
@@ -164,3 +164,40 @@ def test_render_tone_strip_never_calls_markdown_when_metric_has_no_numbers(monke
|
|
| 164 |
]})
|
| 165 |
|
| 166 |
assert calls == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
]})
|
| 165 |
|
| 166 |
assert calls == []
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
_ASCII_METRIC = (
|
| 170 |
+
"hedge-word rate 27->13->15->55 per 10k words over FY2025->Q32026 "
|
| 171 |
+
"(rising 3 quarters) -> more cautious"
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def test_reading_accepts_both_arrow_forms():
|
| 176 |
+
"""_SERIES_RE already tolerates "->" as well as "→"; _reading split on the
|
| 177 |
+
Unicode arrow alone. An upstream ASCII normalisation would therefore keep
|
| 178 |
+
the rate series working while the plain-language reading silently
|
| 179 |
+
vanished -- half a tolerant parser is worse than none."""
|
| 180 |
+
from dashboard.tone_strip import _reading
|
| 181 |
+
|
| 182 |
+
assert _reading(_METRIC) == "more cautious"
|
| 183 |
+
assert _reading(_ASCII_METRIC) == "more cautious"
|
| 184 |
+
assert _reading("rate 1->2 per 10k words → less assertive") == "less assertive"
|
| 185 |
+
assert _reading("no arrows at all") == ""
|
| 186 |
+
assert _reading("") == ""
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def test_render_tone_strip_shows_the_reading_for_an_ascii_series(monkeypatch):
|
| 190 |
+
"""End to end through render: the ASCII form must produce the sparkline
|
| 191 |
+
AND the reading, not the sparkline alone."""
|
| 192 |
+
import dashboard.tone_strip as mod
|
| 193 |
+
|
| 194 |
+
captured: list[str] = []
|
| 195 |
+
monkeypatch.setattr(
|
| 196 |
+
mod.st, "markdown", lambda html, **kw: captured.append(str(html))
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
mod.render_tone_strip({"quarter_deltas": [_tone_delta(_ASCII_METRIC)]})
|
| 200 |
+
|
| 201 |
+
joined = "".join(captured)
|
| 202 |
+
assert "<svg" in joined
|
| 203 |
+
assert "more cautious" in joined
|
|
@@ -278,42 +278,58 @@ def test_kpi_source_note_is_exact_empty_string_without_source_url():
|
|
| 278 |
assert _kpi_source_note({"source_url": ""}) == ""
|
| 279 |
|
| 280 |
|
| 281 |
-
def
|
| 282 |
"""Regression net for the footnote composition in _render_quarter_kpis.
|
| 283 |
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
st.markdown
|
| 288 |
-
|
| 289 |
-
source_note = _kpi_source_note(latest)
|
| 290 |
-
source_html = f' · {source_note}' if source_note else ""
|
| 291 |
-
footnote = f'...{brief_quarter_note_text}{source_html}</div>'
|
| 292 |
-
|
| 293 |
-
and asserts that when source_note is "" (no source_url), source_html is
|
| 294 |
-
also exactly "" -- so the surrounding footnote text is byte-identical
|
| 295 |
-
to what it was before this feature existed, with no dangling separator.
|
| 296 |
"""
|
| 297 |
-
|
|
|
|
|
|
|
| 298 |
from dashboard.i18n import t
|
| 299 |
|
| 300 |
base_text = t("brief_quarter_note").format(section=t("nav_financials"))
|
| 301 |
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
|
| 318 |
|
| 319 |
def test_market_band_cells_exclude_commodity_reference_numbers():
|
|
|
|
| 278 |
assert _kpi_source_note({"source_url": ""}) == ""
|
| 279 |
|
| 280 |
|
| 281 |
+
def test_kpi_footnote_composition_goes_through_the_production_renderer(monkeypatch):
|
| 282 |
"""Regression net for the footnote composition in _render_quarter_kpis.
|
| 283 |
|
| 284 |
+
This test used to mirror the composition (`source_html = f' . {note}' if
|
| 285 |
+
note else ""`) into itself and assert on the copy, so changing the
|
| 286 |
+
separator in verdict.py left it green while it claimed to be a regression
|
| 287 |
+
net. It now spies on verdict.st.markdown and drives the real renderer, so
|
| 288 |
+
the assertions are about the HTML the user actually receives.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
"""
|
| 290 |
+
import dashboard.verdict as mod
|
| 291 |
+
import dashboard.financials as fin
|
| 292 |
+
import storage.metrics_db as mdb
|
| 293 |
from dashboard.i18n import t
|
| 294 |
|
| 295 |
base_text = t("brief_quarter_note").format(section=t("nav_financials"))
|
| 296 |
|
| 297 |
+
def _row(source_url):
|
| 298 |
+
return {
|
| 299 |
+
"period": "Q32026", "form_type": "10-Q", "period_basis": "quarterly",
|
| 300 |
+
"revenue": 94_000_000_000.0, "eps": 1.57,
|
| 301 |
+
"gross_margin": 0.469, "operating_margin": 0.301,
|
| 302 |
+
"free_cash_flow": 24_000_000_000.0,
|
| 303 |
+
"accession": "0000320193-26-000081", "source_url": source_url,
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
def _render(source_url) -> str:
|
| 307 |
+
captured: list[str] = []
|
| 308 |
+
monkeypatch.setattr(mdb, "get_all_metrics", lambda ticker: [_row(source_url)])
|
| 309 |
+
monkeypatch.setattr(fin, "_display_rows", lambda rows: [_row(source_url)])
|
| 310 |
+
monkeypatch.setattr(mod.st, "markdown", lambda html, **kw: captured.append(str(html)))
|
| 311 |
+
mod._render_quarter_kpis("AAPL")
|
| 312 |
+
return "".join(captured)
|
| 313 |
+
|
| 314 |
+
url = "https://www.sec.gov/Archives/edgar/data/320193/000032019326000081/x.htm"
|
| 315 |
+
with_url = _render(url)
|
| 316 |
+
without_url = _render(None)
|
| 317 |
+
|
| 318 |
+
# The renderer's try/except swallows failures and returns -- an empty
|
| 319 |
+
# capture would make every assertion below vacuous.
|
| 320 |
+
assert base_text in without_url
|
| 321 |
+
assert base_text in with_url
|
| 322 |
+
|
| 323 |
+
# No dangling separator, no empty span, nothing at all after the note.
|
| 324 |
+
assert f'{base_text}</div>' in without_url
|
| 325 |
+
assert "<a href=" not in without_url
|
| 326 |
+
|
| 327 |
+
# The link path adds exactly the separator plus the anchor, and removing
|
| 328 |
+
# that one segment reproduces the no-URL output byte for byte.
|
| 329 |
+
note = mod._kpi_source_note({"source_url": url, "accession": "0000320193-26-000081"})
|
| 330 |
+
assert note
|
| 331 |
+
assert f'{base_text} . {note}</div>'.replace(" . ", " · ") in with_url
|
| 332 |
+
assert with_url.replace(f' · {note}', "") == without_url
|
| 333 |
|
| 334 |
|
| 335 |
def test_market_band_cells_exclude_commodity_reference_numbers():
|