0005 — The template is vendored, not a dependency
Three kinds of code, three upgrade policies. The interesting one is the middle: ours to improve, yours to edit.
Context
A starter kit has to answer one question honestly: when we improve the action client, does your copy improve too?
The two usual answers are both bad. Publish it as a library and customers cannot
edit code they own and paid for. Ship it as a snapshot and "lifetime updates"
means, as upgrades.mdx puts it, "a changelog you may read and a diff you may
apply by hand."
Decision
Three categories, three policies:
Published packages (@launchquickly/* — CLI, ESLint config and plugin,
codemods). Upgraded with pnpm up, like any dependency. Nobody edits these.
Vendored and managed — the action client, tenancy primitives, the error
taxonomy, the application components. Vendored because the APIs are not stable
enough to publish and buyers genuinely do edit them; managed because they are
ours to improve. lq upgrade tracks which copies are still the ones we shipped.
Product surface — never touched. src/components/ui is deliberately here:
those are shadcn's primitives, re-installable with npx shadcn add, and the
first thing anyone restyles.
Consequences
This is what makes the upgrade path possible at all, and it is the commercial moat: the ESLint plugin is MIT on purpose because it is the copyable part. What is not copyable is the manifest, the generators and a tested upgrade path into code a customer has already edited.
The cost is that the boundary has to be exactly right, and getting it wrong is expensive in a way that only shows up at a customer's machine. Two examples, both found by running the upgrade against a real project rather than in CI:
A managed file may not depend on something an upgrade cannot deliver. Managed files are free to import unmanaged ones, and an upgrade delivers only the managed half — so files would land importing modules that were never coming, and the project stopped building with no warning, because nothing looked.
CI was structurally blind to it. The upgrade job synthesises a release by copying the current template and upgrades a project materialised from that same template, so every unmanaged peer already exists. A managed file needing a new unmanaged peer cannot fail there.
That second point generalises past this record: a test whose fixture is derived from the thing under test can only confirm the thing agrees with itself.
0004 — The action builder is a type-state machine
.mutation() does not exist until you have validated and chosen an auth posture. Not a lint rule — the code does not compile.
0006 — Managed files carry a marker, the hash lives elsewhere
A comment survives every edit that matters. A version inside the file goes stale and becomes a confident lie.