Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
199 changes: 185 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

<details>
<summary><strong>Why does starter pin Effect 4 RC instead of Effect 3?</strong></summary>

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.

</details>

<details>
<summary><strong>Why are there no configuration options or preset levels?</strong></summary>

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.

</details>

<details>
<summary><strong>How does mutation testing work in this template?</strong></summary>

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.

</details>

<details>
<summary><strong>How do I migrate an existing codebase to this architecture?</strong></summary>

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.

</details>

---

## 🤝 Contributing

Development setup, workflows, and PR guidelines are documented in [CONTRIBUTING.md](CONTRIBUTING.md).

---

## 📄 License

Licensed under the [Apache-2.0 License](LICENSE).
96 changes: 96 additions & 0 deletions STRATEGY.md
Original file line number Diff line number Diff line change
@@ -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.