Skip to content

feat(pebble): polish export markdown + pin format with tests - #202

Open
jpb33333 wants to merge 1 commit into
devfrom
feat/pebble-export-polish
Open

feat(pebble): polish export markdown + pin format with tests#202
jpb33333 wants to merge 1 commit into
devfrom
feat/pebble-export-polish

Conversation

@jpb33333

Copy link
Copy Markdown
Collaborator

Summary

Tightens pebble/export.py:render_profile_markdown (the prospect-research export endpoint) so partial runs, long claims, and heterogeneous confidence values render cleanly, and pins the document shape with structural tests so it can't regress silently.

Five concrete improvements:

  1. Empty-state messaging. When claims and summary are both blank, emit a ## No information gathered section explaining the run was partial (with partial_reason when available). Previously: header followed by nothing — looked broken.
  2. Confidence normalization. Accepts float / string / None / bool and renders as High (0.84) / Medium / Unknown. Previously a raw float like 0.73 was emitted next to literal strings like "unknown".
  3. Claim text truncation. Claims > 240 chars truncate in the table cell with a footnote anchor pointing to a ### Full claim text detail section below the table. Previously: ugly multi-line table cells that could break Markdown table rendering.
  4. Source ordering by first appearance. ## Sources now orders URLs by first claim appearance with a (N claims) suffix when an URL backs multiple claims. Previously sorted alphabetically by URL string.
  5. Source titles when present. When a claim provides source_title, the source list renders - [Title](url) instead of a bare URL.

Also extracts three pure helpers (escape_md_table_cell, truncate_for_cell, normalize_confidence) into a new pebble/formatting.py module so the L2-swarm renderer can adopt them in a follow-up without code coupling.

Why

The endpoint had zero tests pinning its output shape and several rough edges that show up on real research runs. This PR fixes the rough edges and locks the format with tests so it stops being a silent-failure surface.

Scoped intentionally to the legacy markdown path on dev rather than the L2-swarm renderer.py (which lives only on feat/pebble-l2-swarm / PR #191). The new pebble/formatting.py is the seam for coordinated polish — L2 renderer can adopt the same helpers in a follow-up off dev after PR #191 lands or as part of its split.

How tested

  • pytest pebble/tests/test_export.py pebble/tests/test_formatting.py -v45 passed (19 structural format tests + 26 helper unit tests).
  • pytest pebble/tests/ -q335 passed (290 baseline + 45 new). No regressions.
  • pytest financial_forecasting/tests/ -q827 passed, 22 skipped. No regressions (this PR doesn't touch financial_forecasting/).
  • Manual sanity: built a profile dict with one 300-char claim, a 0.62 float confidence, two claims sharing a URL — eyeballed rendered markdown to confirm truncation/footnote, normalized confidence label, and (2 claims) suffix on the shared source.

Golden path scenarios pinned by tests:

  • Full profile (summary + claims + sources)
  • Partial run (empty state with reason)
  • Partial run (empty state without reason — generic fallback)
  • Summary-only run (no claims, no sources)
  • Long claim text (truncation + footnote)
  • Mixed confidence values (float / string / None across claims)
  • Multi-claim source ordering

Risk

Low.

  • Only callers of render_profile_markdown are the /api/v1/research/profiles/{contact_id}/export endpoint (pebble/main.py:655); signature is unchanged.
  • No DB changes, no migrations, no frontend changes.
  • The endpoint is already gated behind use_pebble_chat permission, so blast radius is JP + whichever staff have that permission today.
  • Empty-state and source-ordering changes are user-visible format diffs in the exported markdown — these are improvements, not breaking changes.

Rollback

Revert this single commit (feat(pebble): polish export markdown + pin format with tests). No follow-up cleanup needed; no migrations.

Coordinated polish handoff

After this lands, pebble/orchestrator/renderer.py (currently on feat/pebble-l2-swarm) can adopt:

  • normalize_confidence for its degraded/confidence band display.
  • escape_md_table_cell if any tool renderer ever produces tabular output.

That follow-up should be its own focused PR off dev (or part of splitting PR #191).

🤖 Generated with Claude Code

Tightens the prospect-research export so partial runs, long claims, and
heterogeneous confidence values render cleanly, and pins the document
shape with structural tests so it can't regress silently.

What changed:
- pebble/formatting.py (new): escape_md_table_cell, truncate_for_cell,
  normalize_confidence — pure helpers, no I/O.
- pebble/export.py: empty-state section when claims+summary are blank;
  confidence rendered as "Medium (0.62)" / "High" / "Unknown" instead of
  raw values; claim text > 240 chars truncates with a footnote pointing
  to a "Full claim text" detail section; sources ordered by first claim
  appearance with "(N claims)" suffix and source_title links when present.
- pebble/tests/test_export.py (new): 19 structural assertions on header,
  empty state, summary, claims table, truncation, confidence, sources.
- pebble/tests/test_formatting.py (new): 26 helper unit tests.

Signature of render_profile_markdown is unchanged. PDF path is untouched
beyond a smoke check.

Test counts: pebble 290 -> 335 (+45 new), no regressions.
financial_forecasting 827 passed/22 skipped, unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jpb33333
jpb33333 requested a review from jacrev-pursuit as a code owner May 19, 2026 15:26

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, push a new commit or reopen this pull request to trigger a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant