Skip to content

Commit e2ae1d3

Browse files
RyanAlbertsclaude
andauthored
feat(phase-2): finish memo + screenshot + JSON-escape bug fix (#17)
Closes the Phase 2 memo polish. Memo (src/ycai/reports/docx.py) is now 12 sections: 1. title + dateline 2. executive summary citing Acemoglu's framing 3. three-POV introduction (Andreessen / Dalio / Acemoglu) 4. coverage and methodology 5. agentic batch (heatmap) 6. industry distribution 7. inside B2B SaaS — sub-industry table 8. tech stack + OSS posture (unknown footnoted, not charted) 9. traction signals — verbatim with citable URLs 10. spotlights with traction bullets 11. unanswered questions framed against the three POVs 12. reproduce NAMED_FIGURES dict centralizes the three figures so the deck and memo agree. Tests assert every section appears, the three names are present, optional sections appear only when they have data. ADR 0003 + docs/MEMO_STRUCTURE.md codify and vet. Schema: TractionSignal + TractionSignalKind. yc_subindustry passthrough on CompanyAnalysis (set from source data, not LLM). Source-URL guard rejects fabricated traction citations. Analytics: b2b_subindustry_distribution, tech_stack_known_only, traction_index, traction_count. Deck: 'Three views of this batch' slide (17 slides total). Bug fix: dashboard chart-options JSON was HTML-escaped inside <script type='application/json'>, breaking JSON.parse silently. The v0.2.0 example HTML had blank canvases in browsers; this fixes them. Surfaced when I tried to screenshot the dashboard for the README — Playwright reported the same JSON parse error every non-test browser would have hit. W26 re-run: 105 high-confidence rows, 73 with traction, 212 signals across 8 kinds. Real customer names and funding amounts; all verbatim with citable source URLs. Full-page dashboard screenshot compressed to JPG (under repo's 500KB pre-commit cap). 161 tests, mypy --strict clean, publish-check clean. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9261d8a commit e2ae1d3

21 files changed

Lines changed: 5517 additions & 74 deletions

.secrets.baseline

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@
136136
"line_number": 102
137137
}
138138
],
139+
"examples/output/dashboard-w26-pr17-2026-05-03.html": [
140+
{
141+
"type": "Base64 High Entropy String",
142+
"filename": "examples/output/dashboard-w26-pr17-2026-05-03.html",
143+
"hashed_secret": "3c09e03744a49c6020501c9b7ef6218ad440976e",
144+
"is_verified": false,
145+
"line_number": 102
146+
}
147+
],
139148
"src/ycai/dashboard.py": [
140149
{
141150
"type": "Base64 High Entropy String",
@@ -146,5 +155,5 @@
146155
}
147156
]
148157
},
149-
"generated_at": "2026-05-02T02:15:53Z"
158+
"generated_at": "2026-05-03T19:16:51Z"
150159
}

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
_(no changes since 0.2.0)_
10+
### Added — PR #17 (memo polish)
11+
- **Executive summary** at the top of the memo, citing the Nobel laureate's framing of what the headline finding implies for capital allocation.
12+
- **Three-POV introduction** — single paragraph that pits Marc Andreessen, Ray Dalio, and Daron Acemoglu (2024 Nobel laureate in Economics) against each other on what the batch findings imply. The memo deliberately does not pick a winner. Codified in [`docs/MEMO_STRUCTURE.md`](docs/MEMO_STRUCTURE.md) and [ADR 0003](docs/decisions/0003-three-povs-memo-frame.md).
13+
- **Inside B2B SaaS** sub-industry table — one-layer-deeper breakdown using YC's `subindustry` passthrough (not LLM-derived, so it can't drift). Renders only when B2B SaaS rows exist.
14+
- **Tech stack chart now excludes the `unknown` bucket**; the unknown count is rendered as a footnote / asterisk under the chart instead of as the largest bar.
15+
- **Traction signals section** — companies that advertise verifiable traction (GitHub stars, named customers, funding rounds, revenue, user counts, press, partnerships). New `TractionSignal` schema, model populates them with verbatim spans, source-URL guard rejects fabricated citations. W26 dogfood: **73 of 105 high-confidence companies surfaced 212 traction signals across 8 kinds**.
16+
- **3-POV slide** in the deck for parity with the memo; named figures live in a single dict so memo and deck can never disagree.
17+
- **README hero screenshot** of the dashboard (auto-generated via Playwright at PR time).
18+
- **Bug fix**: dashboard chart-options JSON was being HTML-escaped before injection into a `<script type="application/json">` block, which broke `JSON.parse` on the client. Real charts in the v0.2.0 example HTML now render in browsers as well as in Playwright.
1119

1220
## [0.2.0] — 2026-05-01
1321

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
77
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
88

9+
![yc-ai-pulse W26 dashboard hero](docs/screenshots/dashboard-hero.png)
10+
11+
_The dashboard auto-refuses to ship if any cited URL is dead. Full-page screenshot at [`docs/screenshots/dashboard-w26.jpg`](docs/screenshots/dashboard-w26.jpg)._
12+
913
## What it does
1014

1115
`yc-ai-pulse` answers: *what does the most recent YC batch tell us about the state of AI?*

docs/MEMO_STRUCTURE.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Memo structure (the codified instruction)
2+
3+
This file is load-bearing. Anyone editing `src/ycai/reports/docx.py` should
4+
keep the structure below intact unless the README, this file, ADR 0003, and
5+
the memo all change together.
6+
7+
The memo (`report.docx`) is a narrative document, 3-6 pages, with embedded
8+
chart PNGs and verbatim citations. Per [USER.md document-format
9+
discipline](#) it is the canonical *strategic* surface. The deck mirrors it
10+
visually but the memo is the load-bearing prose.
11+
12+
## Required sections, in order
13+
14+
1. **Title + dateline** — batch label, generation timestamp, repo URL.
15+
2. **Executive summary** — 3-4 sentences. Headline coverage %, headline
16+
capability finding (e.g. "X of Y companies build agents"), one Nobel
17+
laureate's framing of what that means for capital allocation.
18+
3. **Introduction (three POVs)** — single paragraph that pits three named
19+
public voices against each other on what to do with this batch:
20+
- **Marc Andreessen** (a16z) — techno-optimist concentrate-and-bet
21+
- **Ray Dalio** (Bridgewater) — diversify, weight macro cycles
22+
- **Daron Acemoglu** (2024 Nobel laureate, MIT) — productivity claims
23+
are inflated; weight labor-displacement and redistribution risk
24+
The paragraph should not pick a winner. It frames the batch findings as
25+
an empirical input that all three would interpret differently.
26+
4. **Coverage and methodology** — Tier A/B/C breakdown, Layer 1+2 disclosure.
27+
5. **The agentic batch** — capability heatmap + analysis paragraph.
28+
6. **Industry distribution** — top-level industry chart.
29+
7. **Inside B2B SaaS** — one-layer-deeper breakdown of the largest industry
30+
bucket using YC's `subindustry` field. Pure passthrough math (not
31+
LLM-derived) so the breakdown can't drift.
32+
8. **Tech stack and OSS posture** — chart of *known* tech-stack mentions
33+
only; the unknown count is rendered as a footnote/asterisk under the
34+
chart, not as a chart bar.
35+
9. **Traction signals** — companies that advertise verifiable traction
36+
(GitHub stars, named customers, funding rounds, revenue, user counts,
37+
press, partnerships). One section per signal kind, capped at 5
38+
companies per kind for legibility, with verbatim detail and a citable
39+
source URL. Companies without any traction signal are not listed.
40+
10. **Six company spotlights** — diverse-capability + non-B2B-SaaS picks.
41+
Each spotlight includes its traction signals as bullets when present.
42+
11. **What we still cannot answer** — three open questions framed against
43+
the introduction's three POVs.
44+
12. **Reproduce this memo** — install + run instructions.
45+
46+
## Why this structure
47+
48+
- **Executive summary first** because most readers don't read past page one.
49+
The Nobel POV in section 2 is what makes the memo *useful as input to a
50+
capital allocation decision* — without it, this is just classification.
51+
- **Three-POV introduction** because no single voice on AI is dispositive
52+
in 2026. Pitting Andreessen, Dalio, and Acemoglu against each other forces
53+
the reader to make a judgment rather than absorb a pre-cooked answer.
54+
- **Sub-industry breakdown** because "B2B SaaS" is the laziest taxonomy
55+
bucket in venture and tells you nothing useful. One layer deeper
56+
("DevTools", "GTM/Sales", "Compliance") differentiates a bet.
57+
- **Tech-stack-known-only chart** because rendering "unknown" as the largest
58+
bar is misleading even when honest. The footnote keeps the honesty.
59+
- **Traction section before spotlights** because traction is a stronger
60+
signal than capability. A B2B SaaS with 5,000 GitHub stars is more
61+
interesting than 50 nameless agents companies.
62+
63+
## Citation rules (Layer 2 enforced)
64+
65+
Every number in aggregate prose must trace back to `analytics.headline_numbers`,
66+
a chart counter, or `extra_allowed` (derived sums and infrastructure facts).
67+
Per-company verbatim quotes (taglines, rationales, traction details) are
68+
exempt from drift check but still scanned for forbidden phrases.
69+
70+
The named-figures allowlist (Andreessen, Dalio, Acemoglu) is **explicitly
71+
not** sanitized — these are real public figures whose published views are
72+
being summarized, not anonymous "industry insiders". Per Layer 2 invariants,
73+
the prose around their names must paraphrase rather than fabricate quotes.
74+
75+
## Vetting
76+
77+
The repo's `tests/test_docx.py` exercises:
78+
- The structure renders end-to-end on a synthetic 8-company cohort.
79+
- Layer 2 audit aborts the build on a forbidden-phrase injection.
80+
- Layer 2 audit aborts on a fabricated number injection.
81+
- Sub-industry table appears when B2B SaaS rows exist.
82+
- Tech-stack footnote appears when unknown count > 0.
83+
- Traction section appears when at least one company has signals.
84+
- Three-POV introduction includes the three named figures.
85+
86+
Run via `make validate-p0`.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# ADR 0003 — Three-POV introduction frame in the memo
2+
3+
**Date:** 2026-05-03
4+
**Status:** Accepted
5+
6+
## Context
7+
8+
The memo's purpose is to inform capital-allocation decisions, not to certify
9+
that a YC batch is "good" or "bad". An LLM-generated narrative without a
10+
named editorial frame defaults to bland techno-optimism — agents are real,
11+
software is eating the world, etc. — which is true but useless to a reader
12+
making allocation calls in 2026 against debt cycles, AI-productivity
13+
skepticism, and sectoral concentration.
14+
15+
The reader needs a frame to argue *with*, not absorb. The cheapest,
16+
sharpest frame is to name three public voices whose views materially
17+
disagree and let the data set sit between them.
18+
19+
## Decision
20+
21+
Every memo opens with a single-paragraph "introduction" that pits three
22+
named public voices against each other on what the batch findings imply
23+
for capital allocation:
24+
25+
- **Marc Andreessen** (a16z) — techno-optimist; AI is the dominant
26+
industrial transition of our generation; concentrate capital in the
27+
winners; regulation is the existential threat. Source: ["The
28+
Techno-Optimist Manifesto"](https://a16z.com/the-techno-optimist-manifesto/), 2023.
29+
- **Ray Dalio** (Bridgewater) — paradigm-shift macro; AI is real but is
30+
one factor among many; debt cycles, monetary regimes, and geopolitical
31+
realignment dominate; diversify. Source: Dalio's "Principles for Dealing
32+
with the Changing World Order" + ongoing LinkedIn essays.
33+
- **Daron Acemoglu** (2024 Nobel laureate in Economics, MIT) — AI's
34+
productivity claims are likely overstated. His [2024 NBER working paper
35+
10.3386/w32487](https://www.nber.org/papers/w32487) estimates total-factor productivity gains from AI
36+
over the next decade at <0.66%, with the largest distributional risk
37+
being labor-displacement-without-reabsorption. Investment frame: weight
38+
redistributive risk and policy response, not just productivity TAM.
39+
40+
The memo does **not** pick a winner. The job of the introduction is to
41+
force the reader to make a judgment, not to absorb a pre-cooked answer.
42+
43+
## Consequences
44+
45+
**Positive**
46+
- The memo becomes useful *as input to a real capital decision* rather
47+
than as a tour of YC's classification taxonomy.
48+
- The three names provide an editorial spine that survives a YC batch
49+
whose findings could be cherry-picked to support any one of them.
50+
- Anti-hallucination Layer 2's forbidden-phrase scan is unaffected — the
51+
three figures are named real public people whose published views are
52+
being summarized, not anonymous "industry insiders".
53+
54+
**Negative**
55+
- We become responsible for representing each figure's view fairly.
56+
Mitigated by linking to their actual published statements, paraphrasing
57+
rather than fabricating quotes, and updating the doc when the figures'
58+
positions evolve materially.
59+
- The frame is opinionated. A maintainer who wants a "neutral" memo
60+
should fork or override.
61+
62+
## Alternatives rejected
63+
64+
- **No frame** — leaves the memo prose to defaults, i.e. techno-optimist
65+
haze. Tested in PR #15; the result reads like a press release.
66+
- **One frame** (e.g. just Acemoglu) — replaces one bias with another.
67+
Three voices disagreeing forces the reader's own synthesis.
68+
- **Five frames** — three is the minimum number of distinct positions
69+
that span the optimist / hedger / skeptic axis. Five over-flattens.
70+
71+
## Verification
72+
73+
- `tests/test_docx.py::test_memo_introduction_includes_three_named_figures`
74+
asserts the three names appear in the introduction paragraph.
75+
- The named-figures list is **not** in `FORBIDDEN_PHRASES`; appearance
76+
passes Layer 2.
77+
- `docs/MEMO_STRUCTURE.md` is the maintainer-facing instruction; this ADR
78+
is the rationale.
79+
80+
## Updating the figures
81+
82+
If one of the three names becomes inappropriate (e.g. retracted views,
83+
defamation risk, factual error), the change requires:
84+
1. A successor ADR (this one moves to "Superseded").
85+
2. A test update to the new names.
86+
3. A README + memo regeneration.
87+
88+
The list is intentionally short to make this churn cheap.

docs/decisions/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ The point: future-me reads three paragraphs and understands *why*, not just *wha
1212

1313
- [0001 — Use yc-oss/api as the primary YC data source](0001-yc-data-source.md)
1414
- [0002 — Chrome extension talks to a local FastAPI, not Native Messaging](0002-localhost-vs-native-messaging.md)
15+
- [0003 — Three-POV introduction frame in the memo](0003-three-povs-memo-frame.md)
226 KB
Loading

docs/screenshots/dashboard-w26.jpg

369 KB
Loading

examples/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ Sanitized sample artifacts. Every commit goes through `make publish-check` so PI
44

55
| File | What |
66
|---|---|
7-
| [`output/deck-w26-pr14-2026-05-01.pptx`](output/deck-w26-pr14-2026-05-01.pptx) | **PR #14 VC-style deck.** 16 slides, a16z-feel palette, matplotlib chart PNGs anchored to the same data the dashboard uses. Anti-hallucination Layer 2 ran before write. |
8-
| [`output/report-w26-pr15-2026-05-01.docx`](output/report-w26-pr15-2026-05-01.docx) | **PR #15 narrative memo.** 9 sections, ~47 paragraphs, 4 embedded chart PNGs. Headline finding, coverage methodology, capability heatmap with analysis, industry distribution, tech-stack/OSS-posture caveat, six company spotlights, unanswered questions. Layer 2 audit clean. |
7+
| [`output/dashboard-w26-pr17-2026-05-03.html`](output/dashboard-w26-pr17-2026-05-03.html) | **PR #17 dashboard — current best.** ECharts canvases now render correctly (the v0.2.0 release had a JSON-escape bug that left them blank in browsers). |
8+
| [`output/deck-w26-pr17-2026-05-03.pptx`](output/deck-w26-pr17-2026-05-03.pptx) | **PR #17 deck.** 17 slides; adds the three-POV slide (Andreessen / Dalio / Acemoglu) right after the TL;DR. |
9+
| [`output/report-w26-pr17-2026-05-03.docx`](output/report-w26-pr17-2026-05-03.docx) | **PR #17 narrative memo — current best.** Adds executive summary with Acemoglu's framing, three-POV introduction, "Inside B2B SaaS" sub-industry table, tech-stack-known-only chart with unknown footnote, full traction-signals section (73 of 105 companies surface verifiable traction). |
10+
| [`output/analyses-w26-pr17-2026-05-03.json`](output/analyses-w26-pr17-2026-05-03.json) | **PR #17 enrichment.** 105 high-confidence rows out of 124. 212 traction signals total across 8 kinds. |
11+
| [`output/deck-w26-pr14-2026-05-01.pptx`](output/deck-w26-pr14-2026-05-01.pptx) | PR #14 deck (16 slides). Kept as before-3-POV reference. |
12+
| [`output/report-w26-pr15-2026-05-01.docx`](output/report-w26-pr15-2026-05-01.docx) | PR #15 narrative memo. Kept as before-PR-#17 reference. |
913
| [`output/dashboard-w26-pr12-2026-05-01.html`](output/dashboard-w26-pr12-2026-05-01.html) | **PR #12 dashboard — current best HTML.** Same W26 data, ECharts canvases (real heatmap, pies, bars). |
1014
| [`output/dashboard-w26-pr11-2026-05-01.html`](output/dashboard-w26-pr11-2026-05-01.html) | PR #11 dashboard with the depth=1 crawl but static CSS bars. Useful for comparing visual fidelity vs. PR #12. |
1115
| [`output/analyses-w26-pr11-2026-05-01.json`](output/analyses-w26-pr11-2026-05-01.json) | Source data for both PR #11 and PR #12 dashboards. 113/124 high-confidence. |

0 commit comments

Comments
 (0)