Files
zeekayandHanzo Dev d90600177b The deck says why now, and both halves of it are drawn rather than claimed
Two slides between the title and the problem. The pitch opened by answering why
US before it had answered why NOW, so the cost argument on the old slide two was
competing with the thesis instead of following from it. It now follows: if
operations is the bottleneck you will run a great many agents, and everyone
else's bill for an idle one is the thing that stops you.

Slide 2 is the shape of the product, not a sentence about the market. Thirteen
rooms ship under app/(app) and exactly one of them is the code; the other twelve
are the inbox, the board, the calendar, the contacts, the meeting, the files,
the agents doing the work. Every name and every line on those tiles is that
room's own `metadata`, resolved by deck/sync.mjs out of lib/data/rooms.json —
the answer scripts/sync-rooms.mjs already keeps — and a room that describes
itself with nothing stops the sync rather than getting copy invented for it.
/dev is drawn hollow, the way the field's published figures are drawn hollow
elsewhere: it is the part that already got easy, and it is one tile of thirteen.

Slide 3 is our own receipt for the other half. deck/census.mjs walks every git
log under ~/work/{hanzo,lux,zoo}: 188 repositories took a commit in thirty days
and 13,259 commits landed.

It counts a commit ONCE and a repository once, which walking directories does
not: `git worktree` gives one repository several working directories sharing one
object store, so hanzo/universe and its testnet worktree each hand back the same
1,651 commits. Counting paths reported 202 repositories and 16,961 commits where
there are 188 and 13,259 — a 28% overstatement that looks exactly like growth.

And it claims a FLOOR rather than a figure. A commit carries an author name and
an author mailbox and here they disagree: 2,041 commits are `zeekay
<[email protected]>`, a person's name at a service address, and nothing in a git log
says whether a human had the agent's config set or the reverse. Guessing moves
the headline sixteen points. So both classifiers run, only their agreement is
counted, and the doubt is drawn on the slide as its own segment: at least 36.7%
of everything committed was written by an agent, and 52.6% if the disputed
middle is read as agents. A floor cannot be argued down, which is the only kind
of number worth putting in front of an investor. The footnote also declines to
call an identity a person — one of us commits under three names.

No 10x appears anywhere on the deck. It is a good thesis and it is not a number
we measured, and every other figure here re-derives from a URL or a log.

The pitch is renumbered s1–s14 so the series still means position, and the two
layout gates and hyperframes all pass at 31 slides: no overflow, nothing
squeezed, 210 of 210 contrast checks, and the room whose own description ran
long is trimmed in the data with an ellipsis rather than clipped by its box.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-07 19:34:54 -07:00

136 lines
6.5 KiB
JavaScript

// THE ESTATE, COUNTED. How much of what this company shipped last month was
// written by an agent rather than by a person — measured off the git logs of
// the checkouts on the machine, not asserted.
//
// node deck/census.mjs # writes deck/assets/estate.json
//
// WHY THE ANSWER IS A BAND AND NOT A NUMBER. A commit carries two identities,
// an author name and an author email, and in this estate they disagree. There
// are 2,329 commits authored as `zeekay <[email protected]>`: a person's name at a
// service mailbox. Nothing in the log says whether a human had the agent's git
// config set or an agent had the human's name set, and guessing would move the
// headline by fourteen points in whichever direction the guess went.
//
// So both classifiers are run and only their AGREEMENT is claimed. Where the
// name says agent and the mailbox says agent, the commit is an agent's. Where
// both say person, it is a person's. Where they disagree it is neither: it is
// counted, named and left disputed, and the figure the deck states is the
// settled floor — AT LEAST this much was written by an agent. A floor cannot be
// argued down, which is the only kind of number worth putting in front of an
// investor.
//
// The window is the last 30 days of author dates, and the population is every
// repository under ~/work/{hanzo,lux,zoo} that has one. That is a census of the
// machine this deck was cut on and it is stated as such on the slide: a
// checkout that is behind under-reports, and no checkout can over-report.
//
// A COMMIT IS COUNTED ONCE, AND SO IS A REPOSITORY. Walking directories counts
// neither correctly: `git worktree` gives a repository several working
// directories that share ONE object store, so hanzo/universe and its testnet
// worktree hand back the same 1,651 commits each. Counting paths reported 202
// repositories and 16,961 commits where there are 188 and 13,257 — a 28%
// overstatement that looks exactly like growth. So repositories are identified
// by their common git directory and commits by their hash, and each is counted
// on first sight.
import { execFileSync } from 'node:child_process'
import { existsSync, readdirSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const here = dirname(fileURLToPath(import.meta.url))
const WORK = join(process.env.HOME, 'work')
const ORGS = ['hanzo', 'lux', 'zoo']
const DAYS = 30
// The two rosters. Both are named rather than pattern-matched, because a
// pattern that quietly starts matching a person is a classifier nobody can
// audit — and this one decides the headline.
const NAMED = new Set([
'hanzo-dev', 'Hanzo Dev', 'Hanzo AI', 'Hanzo', 'hanzo-ci', 'Hanzo CI',
'Blue', 'blue', 'Claude', 'CTO', 'cto', 'Carmack Agent', 'scientist',
'github-actions[bot]', 'Lux', 'Zoo',
])
const MAILBOX = new Set([
'[email protected]', '[email protected]', '[email protected]', '[email protected]',
'[email protected]', '[email protected]', 'blue@lux', '[email protected]',
])
const service = (email) => MAILBOX.has(email) || email.includes('[bot]@')
const git = (dir, args) => {
try {
return execFileSync('git', ['-C', dir, ...args],
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 1 << 28 })
} catch {
return ''
}
}
const count = { agent: 0, person: 0, disputed: 0 }
const disputed = new Map()
const identity = new Map()
const face = new Map()
const seen = new Set()
const store = new Set()
const org = {}
let repos = 0, commits = 0
for (const name of ORGS) {
org[name] = { repos: 0, commits: 0 }
const root = join(WORK, name)
if (!existsSync(root)) throw new Error(`no ${root} to count`)
for (const repo of readdirSync(root)) {
const dir = join(root, repo)
if (!existsSync(join(dir, '.git'))) continue
const common = git(dir, ['rev-parse', '--path-format=absolute', '--git-common-dir']).trim()
const lines = git(dir, ['log', `--since=${DAYS} days ago`, '--format=%H\t%an\t%ae']).split('\n').filter(Boolean)
if (!lines.length) continue
let fresh = 0
for (const line of lines) {
const [sha, who, mail] = line.split('\t')
if (seen.has(sha)) continue
seen.add(sha); fresh++
const key = `${who} <${mail}>`
identity.set(key, (identity.get(key) ?? 0) + 1)
const byName = NAMED.has(who), byMail = service(mail)
const verdict = byName && byMail ? 'agent' : !byName && !byMail ? 'person' : 'disputed'
count[verdict]++
face.set(key, verdict)
if (verdict === 'disputed') disputed.set(key, (disputed.get(key) ?? 0) + 1)
}
commits += fresh; org[name].commits += fresh
if (common && store.has(common)) continue
if (common) store.add(common)
repos++; org[name].repos++
}
}
if (!commits) throw new Error('no commits in the window — the checkouts are empty or the walk is wrong')
const pct = (n) => Number(((n / commits) * 100).toFixed(1))
const estate = {
measured: new Date().toISOString().slice(0, 10),
window: `${DAYS} days`,
source: `git log --since='${DAYS} days ago' over every repository under ~/work/{${ORGS.join(',')}}`,
repos, commits,
// Identities, split the way the commits are. An IDENTITY is not a person:
// one human here commits as zeekay, z and Zach Kelling from four mailboxes,
// so "nineteen people committed" is a count of name-and-mailbox pairs and the
// deck must not print it as a headcount.
orgs: ORGS.map((name) => ({ name, ...org[name] })),
identities: {
all: identity.size,
agent: [...face.values()].filter((v) => v === 'agent').length,
person: [...face.values()].filter((v) => v === 'person').length,
disputed: [...face.values()].filter((v) => v === 'disputed').length,
},
agent: count.agent, person: count.person, disputed: count.disputed,
share: { agent: pct(count.agent), person: pct(count.person), disputed: pct(count.disputed) },
// Named, because a disputed commit is the whole reason the headline is a
// floor, and a reader is entitled to see which identity moved it.
contested: [...disputed].sort((a, b) => b[1] - a[1]).slice(0, 6).map(([who, n]) => ({ who, commits: n })),
}
writeFileSync(join(here, 'assets', 'estate.json'), JSON.stringify(estate, null, 2) + '\n')
console.log(`deck/assets/estate.json <- ${repos} repos, ${commits} commits in ${DAYS} days`)
console.log(` agent ${count.agent} (${estate.share.agent}%) · person ${count.person} (${estate.share.person}%) · disputed ${count.disputed} (${estate.share.disputed}%)`)
for (const c of estate.contested) console.log(` disputed: ${c.who} — ${c.commits}`)