Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
772bd3d
feat(course-rollout): 2.1 mom-test-ask-about-past-not-future SVG -> O…
pftg Aug 1, 2026
bdc07ce
feat(course-rollout): 2.2 ai-persona-pre-validation-mom-test-prep SVG…
pftg Aug 1, 2026
b8ca003
feat(course-rollout): 2.3 find-10-people-where-to-look SVG -> O2 flat…
pftg Aug 1, 2026
0f29a24
feat(course-rollout): 2.4 find-10-people-with-problem-outreach-2026 S…
pftg Aug 1, 2026
c4084f9
feat(course-rollout): 2.6 clickable-prototype-validation-2-hour-lovab…
pftg Aug 1, 2026
8ccc222
feat(course-rollout): 3.1 one-page-product-brief-vibe-prd SVG -> O2 f…
pftg Aug 1, 2026
7c7fd20
feat(course-rollout): 3.2 stop-specifying-features-start-outcomes SVG…
pftg Aug 1, 2026
3823ab1
feat(course-rollout): 4.2 github-aws-database-ownership-checklist SVG…
pftg Aug 1, 2026
c476ad0
feat(course-rollout): 4.3 self-serve-mvp-stack-lovable-supabase-strip…
pftg Aug 1, 2026
f3d0834
feat(course-rollout): 4.4 self-serve-mvp-stack-build-phases SVG -> O2…
pftg Aug 1, 2026
10e63e1
feat(course-rollout): 5.1 must-have-segment-pmf-test SVG → O2 flat-ve…
pftg Aug 1, 2026
cd855a8
feat(course-rollout): 5.2 channel-selection-before-outbound SVG → O2 …
pftg Aug 1, 2026
05a662a
feat(course-rollout): 5.3 first-ten-customers-network-list SVG → O2 f…
pftg Aug 1, 2026
c509bd5
feat(course-rollout): 5.4 first-ten-customers-outreach-message SVG → …
pftg Aug 1, 2026
8ba294b
feat(course-rollout): 5.5 first-ten-customers-send-track SVG → O2 fla…
pftg Aug 1, 2026
0b59d12
feat(course-rollout): 5.6 paid-pilot-charge-before-ship SVG → O2 flat…
pftg Aug 1, 2026
611ff6b
feat(course-rollout): 5.7 outbound-without-sales-team SVG → O2 flat-v…
pftg Aug 1, 2026
d432883
feat(devx): O7b - bin/check-svg-floor legibility gate (M2-M5 rollout …
pftg Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions bin/check-svg-floor
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# devx O7: catch course exhibit SVGs whose smallest text renders below the
# 9px mobile legibility floor at a 390px content width. The visual suite masks
# <img>, so under-floor SVGs ship invisibly (the defect the M1-M5 O2 rollout
# fixed). This is the build-time net that stops the next one.
#
# floor: rendered_px = font_viewBox * 390 / viewBox_width >= 9
# => font_viewBox >= 9 * viewBox_width / 390
#
# Usage: bin/check-svg-floor [dir] (default: content/course)
# Report-only today (exit 0 with a burn-down list) because ~55 not-yet-converted
# reference/continuation SVGs still fail. Flip REPORT_ONLY=false to make it a
# blocking gate once the whole course is converted.

REPORT_ONLY = ENV.fetch("SVG_FLOOR_BLOCK", "") != "1"
FLOOR_PX = 9.0
MOBILE_W = 390.0
root = ARGV[0] || "content/course"

fails = []
Dir.glob(File.join(root, "**", "*.svg")).sort.each do |path|
svg = File.read(path)
vb = svg[/viewBox="[\d.\s]*?([\d.]+)\s+[\d.]+"/, 1] and w = vb.to_f
next unless w && w > 0
sizes = svg.scan(/font-size:?\s*["']?([\d.]+)/).flatten.map(&:to_f).reject(&:zero?)
next if sizes.empty?
min = sizes.min
rendered = min * MOBILE_W / w
fails << [path, min, w, rendered.round(2)] if rendered < FLOOR_PX
end

if fails.empty?
puts "svg-floor: all #{Dir.glob(File.join(root, '**', '*.svg')).size} SVGs clear the #{FLOOR_PX}px@#{MOBILE_W.to_i} floor"
exit 0
end

puts "svg-floor: #{fails.size} SVG(s) under the #{FLOOR_PX}px@#{MOBILE_W.to_i} floor:"
fails.each { |p, min, w, r| puts format(" %6.2fpx (min-font %g @ viewBox %g) %s", r, min, w, p) }
exit(REPORT_ONLY ? 0 : 1)
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ After this lesson you will be able to: **spot and repair pitch-shaped questions

The pattern: rehearsal tells you whether the question is broken; real interviews tell you whether the hypothesis is right. There's a second reason to rehearse - interviewing is a skill you build by doing it and getting feedback, not by reading about it. Teresa Torres' teams practice with mock interviews and a feedback loop before the questions ever reach a real customer; the clumsy run is where the learning happens ([Product Talk](https://www.producttalk.org/learning-to-interview-continuously/)). A Claude persona gives you that same loop without spending a real interviewee on a question you could have caught first.

![The rehearsal flow at a glance: bring in your draft question list plus the Lesson 1.1 customer blank, run Prompts 1-2 to build 3 ICP personas and test each question in-character, Prompt 3 for Claude's out-of-character diagnosis, Prompts 4-5 to surface 3 objections and sharpen weak questions past-anchored, and leave with 5-7 sharpened questions plus an Objection Tracker for real interviews](rehearsal-loop.svg)
![A left-to-right flow: input is your draft questions plus the Lesson 1.1 customer blank; step 1 (Prompts 1-2) has 3 personas answer each question in-character; step 2 (Prompt 3, marked ruby) is Claude's out-of-character diagnosis - where the learning happens; step 3 (Prompts 4-5) sharpens weak questions past-anchored; output is 5-7 sharpened questions plus an Objection Tracker for real interviews](rehearsal-loop.svg)

## Build 3 ICP personas in Claude

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading