Skip to Content
TechnicalDevelopmentDevelopment Workflow

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:dev

2. Start Relevant Applications

# Start the app you're working on pnpm marketing:dev # Marketing site pnpm docs:dev # Documentation pnpm storybook:dev # Component development

3. 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 build

5. 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.

StageCommandWhat it producesGate 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 newA 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.

CommandFor
/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:definestage:buildstage: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’s CONVENTIONS.md holds 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/docs in the same PR as the change (via the docs-sync skill), 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.yaml workflow posts that announcement to #product-update and 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

  1. Start UI in watch mode:

    pnpm ui:dev
  2. Start Storybook:

    pnpm storybook:dev
  3. Edit component in packages/ui/src/components/

  4. View in Storybook at http://localhost:3002

  5. Test in app by running pnpm marketing:dev

  6. Add story if new component:

    cd apps/storybook # Create src/NewComponent.stories.tsx

Services Package Development

  1. Start services in watch mode:

    pnpm services:dev
  2. Edit service in packages/services/src/

  3. Use in apps - they’ll pick up changes automatically

  4. Test with unit tests (when implemented)

Working with Applications

Adding New Page

In Marketing App

  1. Create page file:

    # For route /about touch apps/marketing/app/about/page.tsx
  2. Implement page:

    export default function AboutPage() { return <div>About</div>; }
  3. Add to navigation if needed

  4. Test at http://localhost:3001/about

In Documentation App

  1. Create MDX file:

    touch apps/docs/app/guides/new-guide/page.mdx
  2. Write content:

    # New Guide Guide content here...
  3. Appears automatically in navigation

Adding New Component

  1. Create component file:

    touch apps/marketing/components/new-component.tsx
  2. Implement component:

    type Props = { title: string; }; export const NewComponent = ({ title }: Props) => <div>{title}</div>;
  3. 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-name

Updating Dependencies

# Update specific package pnpm update package-name # Update all in workspace pnpm update -r # Check outdated pnpm outdated

Removing Dependencies

cd apps/marketing pnpm remove package-name

Code 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:

  1. 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.
  2. 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.
  3. Docs + changelog — the affected apps/docs pages and the one changelog page are updated in the same PR (docs-sync, changelog-entry), so this site never lags the code.
  4. 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

  1. Visual testing: Check UI in browser
  2. Functional testing: Test user interactions
  3. Responsive testing: Different screen sizes
  4. Cross-browser testing: Chrome, Firefox, Safari
  5. Accessibility testing: Keyboard navigation, screen readers

Storybook Testing

  1. Component isolation: Test components individually
  2. Variant testing: Test all component variants
  3. Accessibility: Use a11y addon
  4. 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

  1. Logical grouping: Group related files
  2. Clear naming: Descriptive file and function names
  3. Consistent structure: Follow existing patterns
  4. Component size: Keep components focused

TypeScript

  1. Type everything: Avoid any
  2. Infer types: Use type inference where possible
  3. Shared types: Put shared types in packages
  4. Strict mode: Keep TypeScript strict

React

  1. Functional components: Use arrow functions
  2. Named exports: Prefer named over default exports
  3. Composition: Compose components from smaller ones
  4. Server components: Default to server components in Next.js

Styling

  1. Tailwind first: Use Tailwind utilities
  2. Consistent spacing: Use Tailwind spacing scale
  3. Responsive: Mobile-first approach
  4. Dark mode: Support both themes

Performance

  1. Code splitting: Use dynamic imports
  2. Image optimization: Use Next.js Image
  3. Bundle size: Monitor bundle size
  4. Lazy loading: Load components when needed

Debugging

Common Issues

Hot Reload Not Working

# Restart dev server # Ctrl+C to stop pnpm dev

Build Errors

# Clean and rebuild pnpm clean pnpm build

Type Errors

# Rebuild packages pnpm packages:build # Restart TypeScript server in VSCode # Cmd+Shift+P > TypeScript: Restart TS Server

Debugging Tools

  1. Browser DevTools: Console, Network, Elements
  2. React DevTools: Component hierarchy, props
  3. VSCode Debugger: Breakpoints, step through code
  4. Turbo: turbo run build --dry-run to 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 → PR

Directory Navigation

# Root cd ~/Developer/sustentus # Apps cd apps/marketing cd apps/docs cd apps/storybook # Packages cd packages/ui cd packages/services

Error 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

  1. Check Linear Regularly: Monitor Linear for new error tickets
  2. Filter by Label: Filter tickets by the “bug” label
  3. Review Patterns: Look for patterns in errors
  4. 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

Learn More

Last updated on