mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
feat(throughput): script natively computes to-date + run-rate multiples
Enhanced scripts/garry-output-comparison.ts so both calculations come out of a single run instead of being reassembled ad-hoc in bash: PerYearResult now includes: - days_elapsed — 365 for past years, day-of-year for current - is_partial — flags the current (in-progress) year - per_day_rate — logical/raw/commits normalized by calendar day - annualized_projection — per_day_rate × 365 Output JSON's `multiples` now has two sibling blocks: - multiples.to_date — raw volume ratios (2026-YTD / 2013-full-year) - multiples.run_rate — per-day pace ratios (apples-to-apples) Back-compat: multiples.logical_lines_added still aliases to_date for older consumers reading the JSON. Updated README hero to cite both (picking up brain/* repo that was missed in the earlier aggregation pass): 2026 run rate: ~880× my 2013 pace (12,382 vs 14 logical lines/day) 2026 YTD: 260× the entire 2013 year Stderr summary now prints both multiples at the end of each run. Full analysis at ~/throughput-analysis-2026-04-18.md (local-only). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ When I heard Karpathy say this, I wanted to find out how. How does one person sh
|
||||
|
||||
I'm [Garry Tan](https://x.com/garrytan), President & CEO of [Y Combinator](https://www.ycombinator.com/). I've worked with thousands of startups — Coinbase, Instacart, Rippling — when they were one or two people in a garage. Before YC, I was one of the first eng/PM/designers at Palantir, cofounded Posterous (sold to Twitter), and built Bookface, YC's internal social network.
|
||||
|
||||
**gstack is my answer.** I've been building products for twenty years, and right now I'm shipping more products than I ever have. In the last 60 days: 3 production services, 40+ shipped features, part-time, while running YC full-time. On logical code change — not raw LOC, which AI inflates — my 2026 run rate is **~700× my 2013 pace** (9,859 vs 14 logical lines/day). Year-to-date (through April 18), 2026 has already produced **207× the entire 2013 year**. Measured across 41 public + private `garrytan/*` repos including Bookface. AI wrote most of it. The point isn't who typed it, it's what shipped.
|
||||
**gstack is my answer.** I've been building products for twenty years, and right now I'm shipping more products than I ever have. In the last 60 days: 3 production services, 40+ shipped features, part-time, while running YC full-time. On logical code change — not raw LOC, which AI inflates — my 2026 run rate is **~880× my 2013 pace** (12,382 vs 14 logical lines/day). Year-to-date (through April 18), 2026 has already produced **260× the entire 2013 year**. Measured across 41 public + private `garrytan/*` repos including Bookface. AI wrote most of it. The point isn't who typed it, it's what shipped.
|
||||
|
||||
**2026 — 1,237 contributions and counting:**
|
||||
|
||||
|
||||
@@ -46,6 +46,18 @@ type PerYearResult = {
|
||||
raw_lines_added: number;
|
||||
logical_lines_added: number;
|
||||
active_weeks: number;
|
||||
days_elapsed: number; // 365 for past years; day-of-year for current year
|
||||
is_partial: boolean; // true for current year (2026 today), false for past
|
||||
per_day_rate: { // per calendar day (incl. non-active days)
|
||||
logical: number;
|
||||
raw: number;
|
||||
commits: number;
|
||||
};
|
||||
annualized_projection: { // per_day_rate × 365 — what the year looks like if pace holds
|
||||
logical: number;
|
||||
raw: number;
|
||||
commits: number;
|
||||
};
|
||||
per_language: Record<string, { commits: number; logical_added: number }>;
|
||||
caveats: string[];
|
||||
};
|
||||
@@ -55,9 +67,24 @@ type Output = {
|
||||
scc_available: boolean;
|
||||
years: PerYearResult[];
|
||||
multiples: {
|
||||
logical_lines_added: number | null; // 2026 / 2013
|
||||
commits_per_week: number | null;
|
||||
raw_lines_added: number | null;
|
||||
// TO-DATE: raw totals. Compares full 2013 year vs (possibly partial) 2026.
|
||||
// Answers: "How much has been produced so far?"
|
||||
to_date: {
|
||||
logical_lines_added: number | null;
|
||||
raw_lines_added: number | null;
|
||||
commits: number | null;
|
||||
files_touched: number | null;
|
||||
};
|
||||
// RUN RATE: per-day pace, apples-to-apples regardless of calendar coverage.
|
||||
// Answers: "What's the pace at, normalized for time elapsed?"
|
||||
run_rate: {
|
||||
logical_per_day: number | null;
|
||||
raw_per_day: number | null;
|
||||
commits_per_day: number | null;
|
||||
};
|
||||
// Deprecated: kept for backwards-compat with older consumers reading the JSON.
|
||||
// Aliases `to_date.logical_lines_added` — will be removed in a future version.
|
||||
logical_lines_added: number | null;
|
||||
};
|
||||
caveats_global: string[];
|
||||
version: number;
|
||||
@@ -164,7 +191,25 @@ function analyzeCommit(commit: string, repoPath: string, sccAvailable: boolean):
|
||||
void sccAvailable;
|
||||
}
|
||||
|
||||
function analyzeRepo(repoPath: string, year: number, sccAvailable: boolean): PerYearResult {
|
||||
/**
|
||||
* Days elapsed in the given year as of `now`. For past years returns 365
|
||||
* (366 for leap years). For the current year returns the day-of-year
|
||||
* through `now`. For future years returns 0.
|
||||
*/
|
||||
function daysElapsed(year: number, now: Date = new Date()): number {
|
||||
const currentYear = now.getUTCFullYear();
|
||||
if (year < currentYear) {
|
||||
const isLeap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||
return isLeap ? 366 : 365;
|
||||
}
|
||||
if (year > currentYear) return 0;
|
||||
// Current year: days since Jan 1 inclusive
|
||||
const jan1 = new Date(Date.UTC(year, 0, 1));
|
||||
const diffMs = now.getTime() - jan1.getTime();
|
||||
return Math.max(1, Math.floor(diffMs / (24 * 60 * 60 * 1000)) + 1);
|
||||
}
|
||||
|
||||
function analyzeRepo(repoPath: string, year: number, sccAvailable: boolean, now: Date = new Date()): PerYearResult {
|
||||
const commits = enumerateCommits(year, repoPath);
|
||||
const perLang: Record<string, { commits: number; logical_added: number }> = {};
|
||||
let rawTotal = 0;
|
||||
@@ -199,6 +244,12 @@ function analyzeRepo(repoPath: string, year: number, sccAvailable: boolean): Per
|
||||
}
|
||||
}
|
||||
|
||||
const days = daysElapsed(year, now);
|
||||
const isPartial = year === now.getUTCFullYear();
|
||||
const perDayLogical = days > 0 ? logicalTotal / days : 0;
|
||||
const perDayRaw = days > 0 ? rawTotal / days : 0;
|
||||
const perDayCommits = days > 0 ? commits.length / days : 0;
|
||||
|
||||
return {
|
||||
year,
|
||||
active: commits.length > 0,
|
||||
@@ -207,10 +258,22 @@ function analyzeRepo(repoPath: string, year: number, sccAvailable: boolean): Per
|
||||
raw_lines_added: rawTotal,
|
||||
logical_lines_added: logicalTotal,
|
||||
active_weeks: weeks.size,
|
||||
days_elapsed: days,
|
||||
is_partial: isPartial,
|
||||
per_day_rate: {
|
||||
logical: +perDayLogical.toFixed(2),
|
||||
raw: +perDayRaw.toFixed(2),
|
||||
commits: +perDayCommits.toFixed(3),
|
||||
},
|
||||
annualized_projection: {
|
||||
logical: Math.round(perDayLogical * 365),
|
||||
raw: Math.round(perDayRaw * 365),
|
||||
commits: Math.round(perDayCommits * 365),
|
||||
},
|
||||
per_language: perLang,
|
||||
caveats: commits.length === 0
|
||||
? [`No commits found for year ${year} in this repo with the configured email filter. If private work existed in this era, it is excluded.`]
|
||||
: [],
|
||||
: (isPartial ? [`Year ${year} is partial (day ${days} of 365). Run-rate multiple extrapolates current pace.`] : []),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -229,20 +292,50 @@ function main() {
|
||||
|
||||
// For V1, we analyze the single repo at repoRoot. Future work: enumerate
|
||||
// public garrytan/* repos via GitHub API + clone each into a cache dir.
|
||||
const years = TARGET_YEARS.map(y => analyzeRepo(repoRoot, y, sccAvailable));
|
||||
const now = new Date();
|
||||
const years = TARGET_YEARS.map(y => analyzeRepo(repoRoot, y, sccAvailable, now));
|
||||
|
||||
const y2013 = years.find(y => y.year === 2013);
|
||||
const y2026 = years.find(y => y.year === 2026);
|
||||
const multiples = {
|
||||
|
||||
// Both multiples live in the same output — they measure different things:
|
||||
//
|
||||
// to_date = raw totals. "How much did 2026 produce so far?"
|
||||
// (mixes full-year 2013 vs partial 2026; honest about volume)
|
||||
// run_rate = per-day pace. "What's the throughput rate, normalized?"
|
||||
// (apples-to-apples regardless of how much of 2026 has elapsed)
|
||||
const toDate = {
|
||||
logical_lines_added: (y2013?.active && y2013.logical_lines_added > 0 && y2026?.active)
|
||||
? +(y2026.logical_lines_added / y2013.logical_lines_added).toFixed(1)
|
||||
: null,
|
||||
commits_per_week: (y2013?.active && y2013.active_weeks > 0 && y2026?.active && y2026.active_weeks > 0)
|
||||
? +((y2026.commits / y2026.active_weeks) / (y2013.commits / y2013.active_weeks)).toFixed(1)
|
||||
: null,
|
||||
raw_lines_added: (y2013?.active && y2013.raw_lines_added > 0 && y2026?.active)
|
||||
? +(y2026.raw_lines_added / y2013.raw_lines_added).toFixed(1)
|
||||
: null,
|
||||
commits: (y2013?.active && y2013.commits > 0 && y2026?.active)
|
||||
? +(y2026.commits / y2013.commits).toFixed(1)
|
||||
: null,
|
||||
files_touched: (y2013?.active && y2013.files_touched > 0 && y2026?.active)
|
||||
? +(y2026.files_touched / y2013.files_touched).toFixed(1)
|
||||
: null,
|
||||
};
|
||||
|
||||
const runRate = {
|
||||
logical_per_day: (y2013?.per_day_rate.logical && y2013.per_day_rate.logical > 0 && y2026?.active)
|
||||
? +(y2026.per_day_rate.logical / y2013.per_day_rate.logical).toFixed(1)
|
||||
: null,
|
||||
raw_per_day: (y2013?.per_day_rate.raw && y2013.per_day_rate.raw > 0 && y2026?.active)
|
||||
? +(y2026.per_day_rate.raw / y2013.per_day_rate.raw).toFixed(1)
|
||||
: null,
|
||||
commits_per_day: (y2013?.per_day_rate.commits && y2013.per_day_rate.commits > 0 && y2026?.active)
|
||||
? +(y2026.per_day_rate.commits / y2013.per_day_rate.commits).toFixed(1)
|
||||
: null,
|
||||
};
|
||||
|
||||
const multiples = {
|
||||
to_date: toDate,
|
||||
run_rate: runRate,
|
||||
// Back-compat alias — older consumers read `multiples.logical_lines_added`.
|
||||
logical_lines_added: toDate.logical_lines_added,
|
||||
};
|
||||
|
||||
const output: Output = {
|
||||
@@ -268,11 +361,23 @@ function main() {
|
||||
fs.writeFileSync(outPath, JSON.stringify(output, null, 2) + '\n');
|
||||
|
||||
process.stderr.write(`Wrote ${outPath}\n`);
|
||||
process.stderr.write(`2013 logical added: ${y2013?.logical_lines_added ?? 'n/a'} | 2026 logical added: ${y2026?.logical_lines_added ?? 'n/a'}\n`);
|
||||
if (multiples.logical_lines_added !== null) {
|
||||
process.stderr.write(`Logical-lines multiple: ${multiples.logical_lines_added}× (2026 / 2013)\n`);
|
||||
} else {
|
||||
process.stderr.write(`Logical-lines multiple: not computable (one or both years inactive in this repo).\n`);
|
||||
process.stderr.write(
|
||||
`2013: ${y2013?.logical_lines_added ?? 'n/a'} logical added (${y2013?.days_elapsed ?? '?'}d) | ` +
|
||||
`2026: ${y2026?.logical_lines_added ?? 'n/a'} logical added (${y2026?.days_elapsed ?? '?'}d, ${y2026?.is_partial ? 'partial' : 'full'})\n`
|
||||
);
|
||||
if (toDate.logical_lines_added !== null) {
|
||||
process.stderr.write(`TO-DATE multiple (raw volume): ${toDate.logical_lines_added}× logical, ${toDate.raw_lines_added}× raw\n`);
|
||||
}
|
||||
if (runRate.logical_per_day !== null) {
|
||||
process.stderr.write(
|
||||
`RUN-RATE multiple (per-day pace): ${runRate.logical_per_day}× logical/day, ${runRate.commits_per_day}× commits/day\n` +
|
||||
` 2013 pace: ${y2013?.per_day_rate.logical.toFixed(1) ?? '?'} logical/day | ` +
|
||||
`2026 pace: ${y2026?.per_day_rate.logical.toFixed(1) ?? '?'} logical/day | ` +
|
||||
`2026 annualized: ${y2026?.annualized_projection.logical.toLocaleString() ?? '?'} logical/year projected\n`
|
||||
);
|
||||
}
|
||||
if (toDate.logical_lines_added === null && runRate.logical_per_day === null) {
|
||||
process.stderr.write(`No multiple computable (one or both years inactive in this repo).\n`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user