Skip to content

audit: .md routes strip frontmatter — the LLM-facing format loses every number the HTML shows #28

Description

@tkowalczyk

Evidence (verified on production 2026-06-12)

markdownResponse (src/lib/dual-format/markdown-response.ts) serves entry.body, which Astro's glob loader provides without frontmatter. So curl https://ogsfrompoly.com/statements/2026-05-31-weekly.md returns prose that begins at ## Strategy track record and contains none of: title, summary, period, bankroll, alert_count, hit_rate, hypothetical_pnl_usd, categories, top_wallets, (monthly) pnl.

Worse, the served body literally references the missing data: "Numbers in the frontmatter are mechanically derived" — but there is no frontmatter in the response. An agent subscribing via llms.txt (the site's whole distribution thesis) gets a statement with zero numbers.

TDD plan (failing tests first)

Keep the deep-module shape: one helper, zero per-collection branching.

  1. Add tests to src/lib/dual-format/markdown-response.test.ts FIRST:
    • given { body, data }, the response text starts with a YAML frontmatter block (---\n...\n---\n) round-trippable to deep-equal data (use yaml or devalue-safe serialization; dates already strings post-schema)
    • body follows the frontmatter unchanged, byte-identical (existing byte-match test updated)
    • entries without data (or with empty data) serve body-only, no empty --- block
    • 404 behavior unchanged
  2. Update markdownResponse(entry: { body?: string; data?: Record<string, unknown> }) to prepend serialized frontmatter.
  3. src/pages/[collection]/[...slug].md.ts already passes the full entry — no route change expected.

Decision taken here (cheap to revisit): re-serialize validated data as YAML frontmatter rather than echoing the raw on-disk file — keeps draft/validation semantics and works for any future collection. Determinism matters (feeds snapshot rule): use stable key order = schema declaration order or sorted keys, locked by a snapshot test.

Acceptance criteria

AC Test
.md response contains all statement frontmatter fields incl. top_wallets and monthly pnl new unit tests + snapshot
Byte-determinism across invocations snapshot test (same convention as determinism.test.ts)
Wallet IDs in the emitted frontmatter remain truncated (disclosure invariant holds on the new surface) reuse findFullWalletAddresses against the full serialized response in a test
404 + draft behavior unchanged existing tests stay green

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkAgent-grabbable: implementable without further inputbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions