diff --git a/README.md b/README.md index 5d170ec..ef931d8 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,187 @@ # starter -GitHub template for any TypeScript starter project. Toolchain cloned from -`omp-claude-compat`: pnpm workspaces + turbo + changesets + husky + -commitlint + dprint + oxlint + vitest + stryker + OIDC npm release. - -## Use - -1. **Use this template** on GitHub โ†’ clone your new repo. -2. Replace every `TODO` (`packages/starter/package.json` name/author/description, - `.github/workflows/force-release.yml`, `.changeset/ledger.yaml`). -3. Claim the package: rename `packages/starter` to your package, drop - `"private": true` from its `package.json`, and update `repository.directory`. -4. Register the package as npm trusted publisher (repo + `release.yml` workflow) - before first publish โ€” see `.changeset/README.md`. -5. `pnpm install && pnpm gate:tasks && pnpm gate:dist`. +[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) +[![Effect: 4.x](https://img.shields.io/badge/Effect-4.0_RC-purple.svg)](https://effect.website) +[![CI](https://github.com/systemfsoftware/starter/actions/workflows/ci.yml/badge.svg)](https://github.com/systemfsoftware/starter/actions/workflows/ci.yml) + +> ๐Ÿ›๏ธ **starter** is an opinionated monorepo template for serious TypeScript with Effect and AI coding agents. +> ๐Ÿ”’ One architecture, zero knobs, and mechanical gates that reject the slop agents produce when left unconstrained. +> ๐Ÿš€ Built for engineers accountable for codebases where AI writes the commits. + +--- + +## ๐Ÿ’ก Why + +AI coding agents produce TypeScript that compiles cleanly and passes shallow unit tests while quietly violating foundational architecture: ambient side effects inside decision logic, unchecked type assertions (`as Type`), and mock-heavy test suites that mask runtime breakage. + +`starter` establishes an uncompromising substrate. Invariants are not aspirational guidelines or doc comments; they are enforced mechanically by linter rules, complexity ceilings, mutation test floors, and continuous integration gates. + +| Concern | The Naive AI-Assisted Default | The Endgame Architecture (`starter`) | +| ------------------------ | ------------------------------------------------------------ | -------------------------------------------------------------------- | +| **Domain Logic** | โŒ Interleaved I/O, clocks, random generators, and mutations | โœ… Pure functions returning tagged `Decision` or `Refusal` unions | +| **Branching** | โŒ Sprawling nested `if`/`else` and procedural loops | โœ… Cyclomatic complexity 1 via exhaustive pattern matching (`Match`) | +| **Boundary Data** | โŒ Unchecked casts (`as unknown as Type`, `@ts-ignore`) | โœ… Strict `Schema.decode` transforming raw bytes into branded types | +| **Effect Composition** | โŒ Ambient services and eager promise invocations | โœ… Lazy `Cell` workflows composed linearly via `.pipe()` | +| **Dependency Injection** | โŒ Captured instances and deep `provideService` calls | โœ… Single `Cell.provide` at the root with `R = never` at the entry | +| **State Storage** | โŒ Direct mutation and unvalidated store writes | โœ… Tenant-bound store ports carrying write guard predicates | +| **Test Verification** | โŒ Mock-heavy tests pinning internal implementation | โœ… 100% mutation kill floor (`Stryker`) and property-based tests | +| **Configuration** | โŒ Dozens of toggles that let agents bypass strictness | โœ… Zero knobs โ€” one proven opinionated toolchain end to end | +| **Package Entries** | โŒ Star exports (`export *`) hiding dependency graphs | โœ… Explicit named re-exports enumerated one line per symbol | +| **Refactoring** | โŒ Patching around rotten legacy modules | โœ… Delete-first rebuild with published observable pinning | + +--- + +## ๐Ÿ“ Architecture + +Every external interaction in a `starter` project follows the **I/O Sandwich**: + +``` +read (impure) โ”€โ”€โ–บ decode (pure) โ”€โ”€โ–บ decide (pure) โ”€โ”€โ–บ shape (pure) โ”€โ”€โ–บ write (impure) +``` + +1. ๐Ÿ“ฅ **`read`** โ€” Gathers raw input from ports and external systems. +2. ๐Ÿ” **`decode`** โ€” Validates unvalidated input into branded domain types using Schema. +3. ๐Ÿง  **`decide`** โ€” Executes domain logic with cyclomatic complexity 1 (zero I/O, zero ambient state). +4. ๐Ÿ“ฆ **`shape`** โ€” Builds pure output documents and domain events from the decision. +5. ๐Ÿ“ค **`write`** โ€” Persists changes, emits domain events, or returns responses. + +Phase ordering is guaranteed at compile time: each phase returns branded markers that the succeeding phase demands as input. + +--- + +## ๐Ÿงฐ Toolchain + +`starter` wires a modern, fast, and type-safe toolchain across the workspace: + +| Tool | Role & Configuration | +| ------------------------- | -------------------------------------------------------------------------------------------- | +| โšก **pnpm Workspaces** | Strict workspace dependency management with catalog versioning (`pnpm-workspace.yaml`) | +| ๐ŸŽ๏ธ **Turbo** | High-performance task pipeline with cached builds, tests, and lint runs | +| ๐Ÿ›ก๏ธ **Effect 4** | The standard functional effect system (`^4.0.0-rc.112`) | +| ๐Ÿ” **oxlint** | Rust-based linter enforcing strict TypeScript rules and `@systemfsoftware/all` house presets | +| ๐ŸŽจ **dprint** | Fast, deterministic code and markdown formatting (`dprint.json`) | +| ๐Ÿงช **Vitest** | Fast unit and integration test runner with TypeScript support | +| ๐Ÿ”ฌ **Stryker** | Mutation testing ensuring tests fail when bugs are introduced | +| ๐Ÿ“ฆ **tsdown** | Fast TypeScript bundler building dual ESM and type declarations | +| ๐Ÿ“ **Changesets** | Automated versioning and changelog generation with npm OIDC provenance | +| ๐Ÿช **Husky & Commitlint** | Git hooks enforcing conventional commit standards | +| ๐ŸŒณ **Worktrunk Scripts** | Deno-powered git worktree lifecycle hooks for isolated agent work | + +--- + +## ๐Ÿ“ Workspaces + +The repository is structured into two workspace roots defined in `pnpm-workspace.yaml`: + +```text +. +โ”œโ”€โ”€ packages/ # Reusable libraries, engines, and domain cores +โ”‚ โ””โ”€โ”€ starter/ # Seed library template (rename to your package) +โ”œโ”€โ”€ apps/ # Declared workspace root for apps and CLI tools (added as needed) +โ”œโ”€โ”€ repos/ # Vendored subtrees (constitution, worktrunk-scripts) +โ””โ”€โ”€ docs/ # Solutions, tooling decisions, and plans +``` + +- [`packages/starter`](packages/starter) โ€” The starter package scaffold with pre-configured build, lint, test, and mutation configs. + +--- + +## ๐Ÿš€ Getting Started + +### 1. Create a Repository from Template + +Click the **Use this template** button on GitHub, or create a repository via the GitHub CLI: + +```bash +gh repo create my-effect-project --template systemfsoftware/starter --public +cd my-effect-project +``` + +### 2. Install Dependencies + +```bash +pnpm install +``` + +### 3. Claim the Package + +1. Rename `packages/starter` to your desired package name (e.g., `packages/my-lib`). +2. Update `name`, `description`, and `author` in `packages/starter/package.json`. +3. Remove `"private": true` from `package.json` when you are ready to publish. + +### 4. Build and Verify + +```bash +pnpm build +pnpm check:ci +``` + +--- + +## ๐Ÿšฆ Verification Gates + +All changes must satisfy local and continuous integration verification gates: + +```bash +# Format code and markdown +pnpm format:check + +# Typecheck workspace packages +pnpm typecheck + +# Run linter across packages +pnpm lint + +# Run unit and integration tests +pnpm test + +# Run mutation tests +pnpm mutation + +# Run full CI suite locally +pnpm check:ci +``` + +--- + +## โ“ Frequently Asked Questions + +
+Why does starter pin Effect 4 RC instead of Effect 3? + +Effect 4 introduces first-class primitives for cell composition, branded type ordering, and modern schema transformations that enable the endgame architecture. `starter` targets the future of Effect rather than supporting legacy patterns. + +
+ +
+Why are there no configuration options or preset levels? + +Every configuration toggle provides a route for AI agents to downgrade verification standards and reintroduce slop. Zero knobs guarantees that all packages created from this template adhere to identical architectural standards. + +
+ +
+How does mutation testing work in this template? + +Stryker introduces deliberate syntax and logic mutations into your code and runs your test suite against each mutant. If your tests still pass when code behavior changes, the mutant survives and the gate fails. Domain decisions require a 100% kill score. + +
+ +
+How do I migrate an existing codebase to this architecture? + +Follow the strangler pattern: pin the published observable behavior of a module, delete the legacy file completely, and rebuild it from a blank page using pure I/O sandwiches. Never patch around a flawed core. + +
+ +--- + +## ๐Ÿค Contributing + +Development setup, workflows, and PR guidelines are documented in [CONTRIBUTING.md](CONTRIBUTING.md). + +--- + +## ๐Ÿ“„ License + +Licensed under the [Apache-2.0 License](LICENSE). diff --git a/STRATEGY.md b/STRATEGY.md new file mode 100644 index 0000000..23ea773 --- /dev/null +++ b/STRATEGY.md @@ -0,0 +1,96 @@ +--- +name: starter +last_updated: 2026-09-12 +--- + +# starter Strategy + +## Purpose + +AI writes most of the code now, so precedent decides the shape โ€” and the Effect +idiom the ecosystem teaches, including the canonical exemplars it learns from +(`mikearnaldi/effect-torch`), was authored for human hands. Agents reproduce it +faithfully at volume, producing code that compiles, passes tests, and has thrown +away the invariants that made Effect worth adopting, with nothing in the loop +that rejects it. Maybe fine when AIs weren't programming everything; it isn't +the endgame. + +## Positioning + +One opinionated shape, enforced end to end โ€” zero knobs. The endgame ships as +mechanism (the house lint preset, the complexity-1 gate on decisions, mutation +floors, CI at error severity, the vendored constitution, the agent harness), +never as documentation a reader can ignore: an invariant is carried by a gate +that fails the build, or not carried at all. Effect 4 is the price of entry. The +destination law itself lives in `repos/constitution/` and +`skill://endgame-strangler`, not in this document. + +## Users + +**Primary:** The engineer accountable for a TypeScript/Effect tree that agents +write into โ€” solo, or leading a team that does. They're hiring starter to make +the endgame shape the default, so correctness is enforced by CI rather than by +their own review attention, and drift can't accumulate behind their back. + +## Boundaries + +- No distribution work: the gates earn the stars, not the pitch. +- No light preset, no opt-out, no `warn` severity โ€” ever. +- No second exemplar: the starter is the exemplar, and a demo app would drift from doctrine. +- No Effect 3 compatibility surface: Effect 4 is the price of entry. + +_Resist a change when:_ it buys adoption โ€” or stars โ€” by making the endgame shape optional. + +_Gate:_ review โ€” the reviewer applies exactly the resist test above; a policy +refusal has no command that can catch it, so the PR decision is the gate. + +## Key metrics + +- **Stars** - the leading signal that serious people have found the kit; measured on GitHub (baseline: 1 star, 0 forks for `systemfsoftware/starter`, 2026-09-12). + +Single metric by decision: a deliberate launch-phase bet on attention, revisited +**2026-12-12** (chosen here, 90 days out), when the adoption metric gets named. +The template's usual 3-5 is knowingly unmet until then. + +## Tracks + +### Template hardening via dogfooding a derived repo + +A derived repo hits the real walls โ€” `are-the-types-wrong-effect` did, and its +fixes came back upstream as PR #8 (merge commit `7207d28`) โ€” so field use is how +the template learns where the shape leaks. + +_Why it serves the approach:_ zero knobs only holds if the shape survives real +work; the derived repo finds the holes before adopters do. + +### The enforcement surface (gates, presets, constitution) + +The house oxlint preset (`@systemfsoftware/all`, `packages/starter/oxlint.config.ts`), +the complexity-1 gate on decisions, mutation floors (stryker), CI at error +severity (`pnpm check:ci`), and the vendored constitution (`repos/constitution/`). + +_Why it serves the approach:_ the approach is mechanism over documentation โ€” an +invariant is carried by a gate, or not carried at all. + +### The agent harness + +`AGENTS.md` and its gated Definition of Done, worktree lifecycle hooks +(`repos/worktrunk-scripts/`), the worktree include whitelist, and the +constitution as load-bearing context. + +_Why it serves the approach:_ the agents are the writers, so the harness is the +interface โ€” it makes the endgame the path of least resistance. + +## Milestones + +- **On Effect 4 stable** โ€” the kit's pin (`effect: ^4.0.0-rc.112`, from the + `pnpm-workspace.yaml` catalog) moves to the stable line and the audience + arrives on it; the date is Effect's release schedule, not ours. + +## Brand + +**One-liner:** We embrace the ENDGAME. + +**Key message:** The starter kit for anyone serious about writing TypeScript with +Effect and AI. The shape is enforced, not documented โ€” one architecture, zero +knobs, gates that reject the slop precedent would otherwise produce.