Phase 0 - Foundation Decisions
Status: Done
Overview
Section titled “Overview”- Track: Both (MVP & Showcase)
- Effort: Minimal, foundational setup
- Dependencies: None
- Deliverables: Core architecture decisions, repository setup, development environment
Entry Criteria
Section titled “Entry Criteria”- Project requirements defined
- Hosting platform chosen
- Team assembled (if applicable)
- Development machines ready
Implementation Steps
Section titled “Implementation Steps”| Step | Task | MVP | Showcase | Notes |
|---|---|---|---|---|
| 0.01 | Initialize repository | ✅ | ✅ | Include .gitignore, README |
| 0.02 | Choose package manager | ✅ | ✅ | pnpm recommended for speed |
| 0.03 | Set up Node.js version | ✅ | ✅ | Use .nvmrc pinned to the version in the starter’s .nvmrc (Node 24 line) |
| 0.04 | Select framework version | ✅ | ✅ | Latest stable Astro major (see the starter’s versions.json) |
| 0.05 | Configure TypeScript | ✅ | ✅ | Strict mode from start |
| 0.05a | Configure Biome (lint/format) | ✅ | ✅ | Init @biomejs/biome & VSCode extension |
| 0.06 | Initialize Astro project | ✅ | ✅ | Use create-astro CLI |
| 0.07 | Set up Git hooks | ✅ | ✅ | Husky + lint-staged |
| 0.08 | Create branch strategy | ✅ | ✅ | Document in README |
| 0.09 | Configure environment vars | ✅ | ✅ | .env.example template |
| 0.10 | Create ADR structure | ✅ | ✅ | docs/adr/template.md |
| 0.11 | Document key decisions | ✅ | ✅ | First ADR entry |
Code Examples
Section titled “Code Examples”Initialize Project
Section titled “Initialize Project”# Create project with pnpmpnpm create astro@latest my-portfolio -- \ --template minimal \ --typescript strict \ --git \ --no-install
cd my-portfoliopnpm installPackage Manager Configuration
Section titled “Package Manager Configuration”{ "packageManager": "pnpm@10.13.1", "engines": { "node": ">=24.0.0", "pnpm": ">=10.0.0" }, "scripts": { "dev": "astro dev", "build": "astro build", "preview": "astro preview", "check": "SITE_URL=${SITE_URL:-http://localhost:4321} astro check", "check:types": "tsc --noEmit", "format": "biome format . --write", "lint": "biome check ." }}Astro and TypeScript checking are split into separate scripts (check and check:types), and lint is check-only — autofixing happens via lint-staged in the pre-commit hook, not in the lint script itself.
Biome Configuration
Section titled “Biome Configuration”{ "$schema": "https://biomejs.dev/schemas/2.4.9/schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "files": { "includes": [ "src/**/*.{astro,ts,tsx,js,jsx,md,mdx}", "docs/**/*.{md,mdx}", "scripts/**/*.{ts,js,mjs}", "*.{md,mdx,ts,js,mjs}" ] }, "formatter": { "enabled": true, "formatWithErrors": true, "indentStyle": "space", "indentWidth": 2, "lineEnding": "lf", "lineWidth": 100, "attributePosition": "auto" }, "javascript": { "formatter": { "jsxQuoteStyle": "double", "quoteProperties": "asNeeded", "trailingCommas": "all", "semicolons": "always", "arrowParentheses": "always", "bracketSpacing": true, "bracketSameLine": false, "quoteStyle": "double" } }, "css": { "formatter": { "enabled": true } }, "linter": { "enabled": true, "rules": { "recommended": true, "complexity": { "noBannedTypes": "error", "noUselessTypeConstraint": "error" }, "correctness": { "noUnusedVariables": "error", "useExhaustiveDependencies": "error" }, "suspicious": { "noExplicitAny": "error", "noImplicitAnyLet": "error" }, "a11y": { "recommended": true }, "style": { "noNonNullAssertion": "warn", "useAsConstAssertion": "warn", "useBlockStatements": "warn", "noParameterAssign": "warn", "useDefaultParameterLast": "warn", "useEnumInitializers": "warn", "useSelfClosingElements": "warn", "useSingleVarDeclarator": "warn", "useNumberNamespace": "warn", "noInferrableTypes": "warn", "noUselessElse": "warn" } } }}The project config also defines a
useNamingConventionrule and anoverridesarray (per-glob rules for Markdown, scripts, Astro, CSS, tests, and config files). See the repo'sbiome.jsonfor the authoritative, complete configuration.
Editor Configuration
Section titled “Editor Configuration”root = true
[*]charset = utf-8indent_style = spaceindent_size = 2end_of_line = lfinsert_final_newline = truetrim_trailing_whitespace = trueNode Version File
Section titled “Node Version File”# .nvmrc — pin the Node major the project targets (copy the exact# value from the starter's .nvmrc; the starter requires Node >=24)24TypeScript Configuration
Section titled “TypeScript Configuration”{ "extends": "astro/tsconfigs/strict", "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@components/*": ["src/components/*"], "@layouts/*": ["src/layouts/*"], "@utils/*": ["src/utils/*"], "@styles/*": ["src/styles/*"], "@types/*": ["src/types/*"], "@content/*": ["src/content/*"], "@assets/*": ["src/assets/*"], "@scripts/*": ["scripts/*"] } }}Git Hooks Setup
Section titled “Git Hooks Setup”# Install Huskypnpm add -D husky lint-stagedpnpm exec husky init
# Create pre-commit hookecho 'pnpm exec lint-staged' > .husky/pre-commit.husky/pre-commit:
pnpm exec lint-staged.husky/commit-msg:
pnpm exec commitlint --edit "$1".husky/pre-push:
#!/usr/bin/env sh# Run the unit test suite before pushing so failures surface locally# instead of in CI. Skip with --no-verify if you have a deliberate reason# (e.g. pushing WIP for backup).pnpm run test:unitHusky 9 hook files are plain shell commands — the
husky.shsourcing boilerplate from Husky 8 is gone.
// package.json - lint-staged configuration{ "lint-staged": { "*.{astro,ts,tsx,js,jsx,json,yml,yaml}": "biome check --write --no-errors-on-unmatched", "*.{md,mdx}": "markdownlint-cli2 --fix" }}Environment Variables Template
Section titled “Environment Variables Template”# Environment Variables Template# Copy this file to .env and fill in your values# DO NOT commit .env to version control
# Site Configuration## SITE_URL (required for production builds)# The canonical origin URL where your site will be hosted. No trailing slash.# The build will fail if this is not set or contains placeholder values.# For GitHub Pages: https://<your-github-username>.github.io# For custom domains: https://your-domain.com# SITE_URL=https://your-username.github.io## Alternatively, use PUBLIC_SITE_URL (exposed to client-side code via Astro):PUBLIC_SITE_URL=http://localhost:4321
# Deployment Target# Set to "gh-pages" when deploying to GitHub Pages.# This derives the base path from the package.json "name" field automatically.# Leave unset for root deployments (Cloudflare Pages, Netlify, Vercel, etc.)# DEPLOY_TARGET=gh-pages
# Analytics (optional) - See docs/implementation-guides/06-optional-features/01-analytics.md# Not yet implemented — uncomment and set when adding analytics support.# PUBLIC_PLAUSIBLE_DOMAIN="your-domain.com"# PUBLIC_FATHOM_SITE_ID="YOUR_FATHOM_SITE_ID"
# Contact InformationPUBLIC_CONTACT_EMAIL=hello@example.comPUBLIC_CONTACT_PHONE=+1234567890PUBLIC_CONTACT_PHONE_DISPLAY="+1 (234) 567-890"PUBLIC_CONTACT_LOCATION="San Francisco, CA"PUBLIC_CONTACT_TIMEZONE="Mon-Fri, 9AM-6PM PST"
# Social Media LinksPUBLIC_SOCIAL_GITHUB=https://github.com/examplePUBLIC_SOCIAL_LINKEDIN=https://linkedin.com/company/examplePUBLIC_SOCIAL_TWITTER=https://twitter.com/exampleNote: The starter's typed environment schema lives in
astro.config.mjs(env.schemaviaastro:env, ADR-050) and defines thePUBLIC_CONTACT_*andPUBLIC_SOCIAL_*variables;SITE_URL/PUBLIC_SITE_URLis read at config-load time.
First Architecture Decision Record
Section titled “First Architecture Decision Record”---title: ADR TemplatelastUpdated: 2025-06-10T00:00:00.000Zdescription: >- Canonical template for Architectural Decision Records (ADRs) in this project. All new ADRs must follow this structure.tableOfContents: truepagefind: false---
<!-- USAGE: Copy this template to create a new ADR.
File naming: NNN-kebab-case-title.md (e.g., 009-caching-strategy.md)
Frontmatter requirements: title: 'ADR-NNN: Title Here' (hyphenated ADR-NNN prefix) description: >- (multi-line YAML string, no markdown) Plain text summary of the decision lastUpdated: YYYY-MM-DDT00:00:00.000Z (ISO date, not boolean) tableOfContents: true pagefind: true
Body rules: - First heading must be h2 (## Status), never h1 - No emoji in section headers - Use plain text for Status values (Proposed, Accepted, Superseded by ADR-NNN, Withdrawn). A parenthetical qualifier after the canonical word is allowed, e.g. "Proposed (aspirational)", "Proposed (deferred — revisit trigger)", "Accepted (amended YYYY-MM-DD: summary)". Only the canonical word carries binding force (see docs/adr/README.md). - Use pnpm (never npm) in all script references
Architecture diagrams (optional): Use Mermaid for flowcharts, sequence diagrams, etc. See: https://mermaid.js.org/syntax/ for syntax reference.-->
## Status
[Proposed | Accepted | Superseded by ADR-NNN | Withdrawn]
## Context
What is the issue that we're seeing that is motivating this decision or change? Provide enough context so that someone reading this in the future understands the "why" behind the decision.
## Decision Drivers
- **Driver 1**: [e.g., Performance requirements]- **Driver 2**: [e.g., Developer experience]- **Driver 3**: [e.g., Maintenance burden]- **Driver 4**: [e.g., Cost considerations]
## Considered Options
### Option 1: [Name]
**Description**: Brief description of this approach
**Pros**:
- Advantage 1- Advantage 2
**Cons**:
- Disadvantage 1- Disadvantage 2
### Option 2: [Name]
**Description**: Brief description of this approach
**Pros**:
- Advantage 1- Advantage 2
**Cons**:
- Disadvantage 1- Disadvantage 2
### Option 3: [Name]
**Description**: Brief description of this approach
**Pros**:
- Advantage 1- Advantage 2
**Cons**:
- Disadvantage 1- Disadvantage 2
## Decision
We will go with **Option X** because [justification].
### Implementation Details
```typescript// Include code examples if relevantconst example = 'implementation details';```
## Consequences
### Positive
- What becomes easier or better as a result of this change- Performance improvements expected- Developer experience improvements
### Negative
- What becomes more difficult- Technical debt we're accepting- Additional complexity introduced
### Neutral
- Things that change but aren't necessarily better or worse- Migration requirements- Training needs
## Validation
How will we know if this decision was correct?
- **Metric 1**: [e.g., Bundle size remains under 160KB]- **Metric 2**: [e.g., Build time under 2 minutes]- **Metric 3**: [e.g., Developer satisfaction survey]
## References
- [Link to relevant documentation]- [Link to proof of concept]- [Link to benchmark results]- [Link to team discussion]
## Notes
Additional implementation notes, migration strategies, or other relevant information that doesn't fit in the sections above.
---**Date**: YYYY-MM-DD\**Participants**: [List of people involved in the decision]\**Outcome**: [Accepted | Rejected | Deferred]Branch Strategy Documentation
Section titled “Branch Strategy Documentation”## Branch Strategy
### Main Branches- `master` - Production-ready code (the starter's default branch; use `main` in your own fork if you prefer)- `develop` - Integration branch (Showcase only)
### Feature Branches- `feature/*` - New features- `fix/*` - Bug fixes- `docs/*` - Documentation updates
### Workflow1. Create feature branch from the default branch2. Make changes with conventional commits3. Open PR with description4. Merge after review (Showcase) or self-merge (MVP)
### Commit Convention- `feat:` - New feature- `fix:` - Bug fix- `docs:` - Documentation- `style:` - Formatting- `refactor:` - Code restructuring- `perf:` - Performance improvement- `test:` - Testing- `chore:` - MaintenanceCommon Pitfalls
Section titled “Common Pitfalls”-
Wrong Package Manager: Mixing npm/yarn/pnpm causes lockfile conflicts
- Solution: Commit
.npmrcwithengine-strict=true
- Solution: Commit
-
Loose TypeScript: Starting without strict mode makes it hard to enable later
- Solution: Always start with strict mode, add
// @ts-expect-errorsparingly
- Solution: Always start with strict mode, add
-
Missing Git Hooks: Code quality degrades without automation
- Solution: Set up hooks before writing code
-
Environment Variable Confusion: Hardcoded values in code
- Solution: Use
.env.exampleand validate on startup
- Solution: Use
Exit Criteria
Section titled “Exit Criteria”- Repository initialized with correct .gitignore
- Package manager locked (pnpm-lock.yaml committed)
- Node.js version specified (.nvmrc file)
- TypeScript in strict mode
- Git hooks functioning (test with commit)
- Branch strategy documented
- Environment variables structured
- ADR template and first decision recorded
- README has basic project information
Rollback Strategy
Section titled “Rollback Strategy”If critical issues found after Phase 0:
-
Package Manager Issues:
Terminal window rm -rf node_modules pnpm-lock.yamlpnpm install -
Git Configuration Issues:
Terminal window rm -rf .gitgit init# Re-apply configuration -
Framework Version Issues:
- Update package.json to stable version
- Clear cache:
pnpm store prune - Reinstall dependencies
AI Assistant Notes
Section titled “AI Assistant Notes”Key Files to Reference
Section titled “Key Files to Reference”package.json- Verify scripts and dependenciestsconfig.json- TypeScript configuration.husky/pre-commit- Git hooksdocs/adr/000-starter-decisions.md- Key decisions
Common Prompts for This Phase
Section titled “Common Prompts for This Phase”- “Set up an Astro project on the current stable major with TypeScript strict mode”
- “Configure Biome for Astro project”
- “Create Git hooks for code quality”
- “Write ADR for foundation decisions”
Context Requirements
Section titled “Context Requirements”- Project type (portfolio, blog, marketing)
- Team size (solo vs team)
- Deployment target (Cloudflare Pages, Vercel, etc.)