The site now presents one freelance developer on one page: hero, introduction, four typical cases (developer for a product, make an app reliable, automate a process, a website) with what you get, how it runs and what it costs, a process automation section, technology and project fit, how I work, two live references, questions and contact. - Hero and closing headline are drawn from running binary digits. - Each case explains its four steps with an animated drawing; the automation section shows example workflows with work travelling between systems; a pipeline run shows what a test catches before a release. - Contact is one email address, kept in parts and assembled in the browser so it never appears in the page source (src/domain/email.ts, spec first). - Imprint and privacy pages added. - The old site (blog, case studies, contact form, fixed-price pages, video rendering and related scripts) is removed. Its URLs redirect to the homepage. - The deploy smoke test checks the legal pages and the redirects instead of the removed case study. - Dev scripts use docker compose v2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
4.0 KiB
Markdown
65 lines
4.0 KiB
Markdown
# mintel.me
|
|
|
|
Personal website of a senior freelance engineer. Next.js 16 (App Router), pnpm monorepo.
|
|
App lives in `apps/web`. Concept and scope: `apps/web/plans/freelance-relaunch.md` (read it first).
|
|
|
|
## Commands (run from repo root)
|
|
|
|
- `pnpm --filter @mintel/web typecheck` — must pass with zero errors
|
|
- `pnpm --filter @mintel/web test` — unit specs (Vitest); 0 tests run counts as a failure
|
|
- `pnpm --filter @mintel/web test:coverage` — `src/domain` must stay at 100%
|
|
- `pnpm --filter @mintel/web test:mutation` — Stryker on `src/domain`, ~2 min, must score 100
|
|
- `pnpm --filter @mintel/web lint`
|
|
|
|
## How to work here (spec first)
|
|
|
|
1. Write the behavior as concrete cases (inputs, outputs, edge cases, errors) as a `*.spec.ts`.
|
|
2. Run it and confirm it fails for the right reason (missing behavior, not an import error).
|
|
3. Implement the simplest clean solution, run again.
|
|
4. Run typecheck, coverage and mutation. Survivors mean weak specs or dead code: fix one or the other.
|
|
5. Report the exact commands and results. "Done" means they were run on the current code.
|
|
Never weaken, skip or delete a test to make it pass.
|
|
|
|
## Copy rules (client-facing pages)
|
|
|
|
- Never mention AI as the owner's tool, and no internal method vocabulary (specs, TDD, mutation, coverage,
|
|
pull requests). The automation service may name AI as part of what the client gets. Plain language.
|
|
- Modest and factual: no promises the owner did not confirm, no superlatives. See the concept, section 1.
|
|
- One page: everything lives on the homepage. The header has only "Get in touch", which jumps to the contact
|
|
section (`#contact`). Contact is one email address: no calendar integration, no form. The only other
|
|
pages are `/imprint` and `/privacy`. URLs of the previous site redirect to `/` (see `next.config.mjs`).
|
|
- The email address is kept in parts (`src/content/site.ts`) and assembled in the browser by `EmailLink`.
|
|
Never write the full address into markup, metadata or copy.
|
|
- The hero design (binary-digit headline) is approved: do not change its design.
|
|
- Look at the result before reporting: take screenshots (desktop and mobile), do not ship blind.
|
|
|
|
## Architecture rules
|
|
|
|
- Functional core, imperative shell. Pure logic goes in `apps/web/src/domain/` (no I/O, no clock, no
|
|
randomness, no framework imports; pass `today` etc. in as arguments). Everything there is covered 100%
|
|
and mutation-tested. Pages, routes and adapters stay thin and call into the core.
|
|
- Parse at the boundary into precise types (`Result`, discriminated unions); no nullable state bags.
|
|
- No `any`, `@ts-ignore`, `!` non-null assertions or eslint-disable to silence errors. Fix the cause.
|
|
|
|
## Do not
|
|
|
|
- Read or edit `.env*` (only `.env.example`). Never print or log secrets.
|
|
- Commit scratch files (`tmp*`, `*.log`, `*.tmp`, debug HTML, build output). They are gitignored; keep it so.
|
|
- Use `--no-verify`, force-push, push to `main`, or run `reset --hard`, `clean`, `rm -rf` without asking.
|
|
- Edit `.gitea/workflows/*` or deploy config without asking.
|
|
- Upgrade `vitest` past 4.1.x: `@stryker-mutator/vitest-runner` 10.0.0 runs zero tests per mutant under
|
|
vitest 5 and reports false survivors. Re-verify mutation results after any tooling bump.
|
|
- Add dependencies for trivial things, or touch unrelated code (no drive-by refactors).
|
|
|
|
## Gotchas
|
|
|
|
- `tsconfig` includes `.next/types`: after deleting a route, stale generated files there break `tsc`.
|
|
Run `next typegen` and remove the stale file.
|
|
- Run only one Stryker process at a time; parallel runs share `.stryker-tmp` and hang.
|
|
- Stryker needs `plugins` listed explicitly in `stryker.config.json` (pnpm layout) and ignores the large
|
|
asset folders via `ignorePatterns`; keep them in sync when adding top-level folders.
|
|
- `@mintel/*` packages are `link:` dependencies on a sibling `at-mintel` checkout. The app itself now only
|
|
uses `@mintel/next-config`; the other entries in `package.json` are leftovers from the old site.
|
|
- Pushing `main` deploys to the testing environment; production is deployed from a `v*` tag.
|
|
- Git branches: work on `feat/*`, never directly on `main`.
|