Development Workflow
This guide covers the standard development workflow for the Sustentus monorepo.
Daily Development Workflow
1. Start Development Environment
# Pull latest changes
git pull origin main
# Install any new dependencies
pnpm install
# Start packages in watch mode
pnpm packages:dev2. Start Relevant Applications
# Start the app you're working on
pnpm marketing:dev # Marketing site
pnpm docs:dev # Documentation
pnpm storybook:dev # Component development3. Make Changes
- Edit code in your editor
- Changes hot-reload automatically
- Check browser for updates
4. Verify Changes
# Run linting
pnpm lint
# Format code
pnpm format
# Build to ensure no errors
pnpm build5. Commit Changes
Make small, atomic commits as you go. The branch, PR, and merge are driven by the delivery pipeline — you commit; the Build and Release stages handle the rest.
# Stage changes
git add .
# Commit with a descriptive message (the pre-commit hook auto-formats staged files)
git commit -m "feat: add new feature"Feature delivery pipeline
Sustentus does not ship features through an ad-hoc “branch → commit → PR → merge” flow. Features
move through an ICM delivery pipeline driven by a single /pipeline command, through four
ordered, human-gated stages. Nothing advances automatically —
you review (and edit) a stage’s output, then invoke the next stage. That pause is the gate.
The pipeline lives in the repo at .icm/CONTEXT.md (the workspace map) and
.claude/skills/pipeline/SKILL.md (the router). Each stage’s full contract is
.icm/stages/NN_*/CONTEXT.md.
| Stage | Command | What it produces | Gate before moving on |
|---|---|---|---|
| Scope | /pipeline scope "<story>" | The author’s story committed verbatim, plus the questions it leaves open | ✅ Paul or David agrees the settled story |
| ↳ approve | /pipeline approve <slug> | Their answers settled into scope.md; the intake stubs are cut | — (a substage of Scope; flows into Define) |
| Define | /pipeline new | A spec (canonical, in git) and a draft PR, one per intake stub | ✅ you tick Spec approved on the PR |
| Build | /pipeline build <slug> | Implementation on the run’s branch; PR flips draft → open | ✅ you smoke the preview, tick Ready to merge |
| Release | /pipeline release <slug> | Reviews · docs + changelog in-PR → squash-merge; CI announces + archives | — (the merge ends the run) |
(The former Verify and Ship stages were merged into Release in 2026-08: your
Ready to merge tick now attests your own preview smoke-testing, and only a blocking CI
failure, a security-critical finding, or deploy-breaking config can hold the merge after it —
every other finding is parked as a stub in .icm/intake/triage/ and picked up by a fast lane
later.)
Work arrives as a written user story from Paul or David; the story is the scope’s starting point and is committed to the run folder exactly as written. The two of them are interchangeable business approvers — either may write a story and either may agree one.
Two of those gates are PR checkboxes — Spec approved and Ready to merge — and both are yours to tick. The business’s involvement ends when the story is settled at Scope; from Define onward every approval is yours.
Check where any feature stands with /pipeline status <slug> (or /pipeline status for the board).
Fast lanes
Work that needs no scope and no spec skips the front entirely and keeps only the merge gate. Each
is one PR carrying just the Ready to merge checkbox; the contracts live in
.icm/lanes/<name>/CONTEXT.md.
| Command | For |
|---|---|
/pipeline bug "<report>" | reproduce → fix → verify |
/pipeline tweak "<change>" | a tiny, fully-specified adjustment |
/pipeline chore "<task>" | refactor / dep-bump / migration — no behaviour change |
A lane that turns out to carry product decisions gets routed back to /pipeline scope. Lanes can
also start from a parked finding by name — .icm/intake/triage/ holds one small stub per
off-ticket finding any stage cut instead of widening its PR (/pipeline bug <stub-name> picks it
up).
The PR and the run spec are the source of truth
Define writes the spec to .icm/runs/<slug>/02_define/output/spec.md (the canonical, editable
spec) and opens a draft PR whose body links to it — no GitHub issue is created. The PR’s labels,
including a single stage:* label (stage:define → stage:build → stage:release),
are projected from the spec and from which run outputs exist, so the PR board always shows exactly
where each feature is. The two human gates are checkboxes in the PR body, ticked by a human — never
by the agent.
Branch management
There is no develop branch and no manual branch-naming convention to memorise. The tooling names
the branch (e.g. claude/<slug>-<hash>) and the run’s run.md records it. main is the only
long-lived branch and is always production-ready — Release squash-merges into it and deploys.
Commits and PRs
- Keep commits small and atomic. Conventional-commit prefixes (
feat:,fix:,refactor:,chore:,docs:) are preferred but not enforced — the repo’sCONVENTIONS.mdholds the canonical git rules. - The Husky + lint-staged pre-commit hook auto-formats staged
*.{ts,tsx,md}files, so formatting is handled for you. - You don’t open or merge the PR by hand — Define opens the draft PR, Build flips it to open, and Release merges it, but only after you tick Ready to merge on the PR.
Docs stay in sync automatically
- Release updates the technical and business/product docs in
apps/docsin the same PR as the change (via thedocs-syncskill), so this site never drifts from the code, and publishes the changelog page with the merge — the one release artifact (its frontmatter carries the announcement’s title, summary and audience). - On the merge, the
release.yamlworkflow posts that announcement to#product-updateand archives the run (close-out) — the agent never sends or archives by hand, and there is no deploy-status gate.
Working with Packages
UI Package Development
-
Start UI in watch mode:
pnpm ui:dev -
Start Storybook:
pnpm storybook:dev -
Edit component in
packages/ui/src/components/ -
View in Storybook at
http://localhost:3002 -
Test in app by running
pnpm marketing:dev -
Add story if new component:
cd apps/storybook # Create src/NewComponent.stories.tsx
Services Package Development
-
Start services in watch mode:
pnpm services:dev -
Edit service in
packages/services/src/ -
Use in apps - they’ll pick up changes automatically
-
Test with unit tests (when implemented)
Working with Applications
Adding New Page
In Marketing App
-
Create page file:
# For route /about touch apps/marketing/app/about/page.tsx -
Implement page:
export default function AboutPage() { return <div>About</div>; } -
Add to navigation if needed
-
Test at
http://localhost:3001/about
In Documentation App
-
Create MDX file:
touch apps/docs/app/guides/new-guide/page.mdx -
Write content:
# New Guide Guide content here... -
Appears automatically in navigation
Adding New Component
-
Create component file:
touch apps/marketing/components/new-component.tsx -
Implement component:
type Props = { title: string; }; export const NewComponent = ({ title }: Props) => <div>{title}</div>; -
Import and use:
import { NewComponent } from "@/components/new-component";
Dependency Management
Adding Dependencies
# Add to specific package
cd apps/marketing
pnpm add package-name
# Add to UI package
cd packages/ui
pnpm add package-name
# Add dev dependency
pnpm add -D package-nameUpdating Dependencies
# Update specific package
pnpm update package-name
# Update all in workspace
pnpm update -r
# Check outdated
pnpm outdatedRemoving Dependencies
cd apps/marketing
pnpm remove package-nameCode review and merge (the Release stage)
Review and merge are part of the pipeline, not a separate manual ritual. After Build hands over a
green PR, you smoke-test the Vercel preview and tick Ready to merge — the tick is the
attestation of that manual testing. Release then does the rest in one stage; its full contract
is .icm/stages/04_release/CONTEXT.md.
What Release handles, so the change merges sound:
- CI green — format, lint, typecheck, tests, and the Vercel preview builds, read as one settled verdict; Release refuses to merge on red or unsettled CI.
- Reviews — the code review (plus security / production-readiness passes when the diff
touches auth, payments, data, or env vars). Findings are triaged, not litigated: trivial
in-ticket fixes land on the branch; a security-critical or deploy-breaking finding stops the
merge; everything else is parked as a stub in
.icm/intake/triage/and the merge proceeds. - Docs + changelog — the affected
apps/docspages and the one changelog page are updated in the same PR (docs-sync,changelog-entry), so this site never lags the code. - The merge — one squash-merge on the ticked box. The Slack announcement and the run’s
close-out then run themselves in CI (
release.yaml).
When review comments come in, they are triaged by the same rule — follow-up commits on the same run branch for in-ticket fixes, triage stubs for the rest.
Testing Strategies
Manual Testing
- Visual testing: Check UI in browser
- Functional testing: Test user interactions
- Responsive testing: Different screen sizes
- Cross-browser testing: Chrome, Firefox, Safari
- Accessibility testing: Keyboard navigation, screen readers
Storybook Testing
- Component isolation: Test components individually
- Variant testing: Test all component variants
- Accessibility: Use a11y addon
- Visual regression: (Future) Chromatic integration
Future: Automated Testing
When implemented:
- Unit tests with Vitest
- Integration tests
- E2E tests with Playwright
Development Best Practices
Code Organization
- Logical grouping: Group related files
- Clear naming: Descriptive file and function names
- Consistent structure: Follow existing patterns
- Component size: Keep components focused
TypeScript
- Type everything: Avoid
any - Infer types: Use type inference where possible
- Shared types: Put shared types in packages
- Strict mode: Keep TypeScript strict
React
- Functional components: Use arrow functions
- Named exports: Prefer named over default exports
- Composition: Compose components from smaller ones
- Server components: Default to server components in Next.js
Styling
- Tailwind first: Use Tailwind utilities
- Consistent spacing: Use Tailwind spacing scale
- Responsive: Mobile-first approach
- Dark mode: Support both themes
Performance
- Code splitting: Use dynamic imports
- Image optimization: Use Next.js Image
- Bundle size: Monitor bundle size
- Lazy loading: Load components when needed
Debugging
Common Issues
Hot Reload Not Working
# Restart dev server
# Ctrl+C to stop
pnpm devBuild Errors
# Clean and rebuild
pnpm clean
pnpm buildType Errors
# Rebuild packages
pnpm packages:build
# Restart TypeScript server in VSCode
# Cmd+Shift+P > TypeScript: Restart TS ServerDebugging Tools
- Browser DevTools: Console, Network, Elements
- React DevTools: Component hierarchy, props
- VSCode Debugger: Breakpoints, step through code
- Turbo:
turbo run build --dry-runto see task graph
Quick Reference
Common Commands
# Development
pnpm dev # All apps
pnpm marketing:dev # Marketing app
pnpm docs:dev # Documentation
pnpm storybook:dev # Storybook
pnpm packages:dev # Packages watch mode
# Building
pnpm build # Build all
pnpm marketing:build # Build marketing
pnpm packages:build # Build packages
# Code Quality
pnpm lint # Lint all
pnpm format # Format all
pnpm clean # Clean builds
# Delivery pipeline (/pipeline command)
/pipeline scope "<story>" # Scope: commit the story verbatim + interrogate the business logic
/pipeline approve <slug> # Settle the author's answers into scope.md + cut the intake stubs
/pipeline new # Define: next stub → spec + draft PR (gated by the Spec approved tick)
/pipeline build <slug> # Build: implement on the run's branch, flip PR draft → open
/pipeline release <slug> # Release: reviews · docs + changelog → merge (gated by the Ready to merge tick)
/pipeline status [slug] # Where a feature (or all) stands
# Fast lanes (single merge gate)
/pipeline bug "<report>" # Reproduce → fix → PR
/pipeline tweak "<change>" # Tiny adjustment → small PR
/pipeline chore "<task>" # Refactor / dep-bump / migration → PRDirectory Navigation
# Root
cd ~/Developer/sustentus
# Apps
cd apps/marketing
cd apps/docs
cd apps/storybook
# Packages
cd packages/ui
cd packages/servicesError Logging and Tracking
The application automatically logs all errors to Linear for tracking and resolution. This system works transparently without requiring manual intervention.
Automatic Error Logging
All errors are automatically captured and logged:
- React Component Errors: Caught by ErrorBoundary
- Unhandled JavaScript Errors: Caught by global error handlers
- Unhandled Promise Rejections: Caught by rejection handlers
- API 5xx Server Errors: Caught by API interceptors
- Backend Process Errors: Caught by process error handlers
Monitoring Errors
- Check Linear Regularly: Monitor Linear for new error tickets
- Filter by Label: Filter tickets by the “bug” label
- Review Patterns: Look for patterns in errors
- Prioritize Fixes: Address critical errors first
Manual Issue Reporting
If you need to manually report an issue:
Email: developers-47c2a754df68@intake.linear.app
Learn More
- Error Logging Documentation - Complete error logging guide
- Error Logging Quick Reference - Quick reference guide