# Documentation Porting Guide ## Purpose and Scope This guide is the canonical rename and replacement map for porting inherited boilerplate documentation to the current generic boilerplate documentation set. It is for documentation updates only: use it to update prose, examples, tables, diagrams, and verification notes in docs, but do not use it as authority to rename runtime imports, package exports, queue names, database identifiers, or historical decision records. The current web runtime source lives under `apps/web/app` and `apps/web/public`. Root scripts still provide the top-level developer entrypoints, while `apps/api`, `apps/worker`, and selected `packages/*` boundaries document or host target extraction surfaces. Documentation should describe the current state precisely instead of implying that every package boundary is fully runtime-wired. ## Source Evidence Use these source files as the confirmed baseline for this guide: - `README.md`: current quick start, package manager, runtime commands, database setup, worker Redis note, CI summary, and manual E2E policy. - `docs/architecture-migration-status.md`: authoritative boundary status for root runtime, `apps/*`, `packages/*`, runtime-wired surfaces, skeleton-only surfaces, UrlUtils migration notes, and feature-pruning interpretation. - `docs/feature-inventory.md`: current pruned-core, plugin-candidate, and remove-candidate classifications for downstream feature documentation. - `scripts/verify-porting-docs-baseline.mjs`: Porting Docs documentation inventory roots and terms that the baseline scanner already tracks. - `.tc/baselines/porting-docs/summary.md`: baseline-health handoff and report location `.tc/baselines/porting-docs/baseline-health.md`. Downstream documentation slices should compare their proposed edits against the baseline report and explain whether remaining legacy terms are intentional historical references, protected runtime identifiers, or newly introduced drift. Use `docs/porting-verification.md` as the final post-port documentation checklist before closing downstream porting work. ## Rename/Replace Map | Legacy or ambiguous source term | Preferred documentation term | Apply in docs when | Notes and examples | | --- | --- | --- | --- | | `SaasRock`, `SaasRock Boilerplate`, `saasrock` | Current boilerplate wording | Product, project, setup, architecture, and roadmap prose that describes the current repository. | Keep historical references only when naming an archived upstream source or old decision context. | | `saasrock.com`, `www.saasrock.com`, SaasRock marketing domains | Neutral local/example domain wording | Domain examples, mail examples, deployment examples, and screenshots that should represent the current project. | Prefer `app.localhost:3000` and `admin.localhost:3000` for the default web dev loop; use `api.localhost:3001` only when the standalone API runtime is started separately. Use `example.com` / `app.example.com` for tenant-safe examples unless a deployment-specific domain is explicitly documented. | | `SaasRock LLC`, old SaasRock legal branding, old trademark/legal wording | Current project/legal owner wording or neutral placeholder | Legal, privacy, imprint, footer, and branding documentation. | Do not invent legal claims. If ownership is unknown, use a placeholder and mark the document as needing legal review outside the code task summary, not with inline TODO markers. | | Product-brand variants in prose | Generic boilerplate wording | Human-readable product or brand references. | Keep actual package names, queue names, runner labels, and other runtime identifiers unchanged. | | Root package display names inherited from old boilerplate | `toolctrl-boilerplate` | Documentation that names the root `package.json` project. | Confirm against root `package.json` before changing package-manager or script examples. | | Generic `SaaS boilerplate` where it means this repository | Current boilerplate | Repository-specific docs, quick-start text, architecture handoffs, and migration plans. | Keep generic `SaaS` only when the sentence describes the product category rather than the repository name. | | Old monolith wording that implies all runtime code is root-only | `apps/web` web source plus workspace boundaries | Architecture docs and migration status docs. | State that `apps/web/app` owns web source while root scripts keep top-level command compatibility and `apps/api`, `apps/worker`, and selected `packages/*` provide target or runtime-wired boundaries. | | `Remix` runtime wording where it describes current root app execution | `React Router` runtime | Current runtime commands, build, route, and app-shell descriptions. | Preserve `remix-i18next` and dependency names when referring to actual package names. | | `npm`, `yarn`, unpinned package-manager examples | `pnpm` with Corepack and pinned `pnpm@10.33.0` | Setup, CI, and local verification commands. | README confirms `corepack enable`, `corepack prepare pnpm@10.33.0 --activate`, and `pnpm install`. | | Root-only database ownership wording | `packages/db` boundary plus root Prisma schema/migrations | DB architecture and migration docs. | Current state: `packages/db` owns the server-side Prisma client boundary and command surface, while schema and migrations still physically live under `prisma/`. | | Broad `database provider` examples that imply multiple active providers | PostgreSQL through Prisma, with Supabase only as an example connection target | Setup and deployment docs. | README names PostgreSQL as default and gives a Supabase-style PostgreSQL URL example. Do not imply a second ORM or active non-Postgres provider. | | Generic Redis requirement for all local development | Optional worker Redis | Quick start and worker docs. | `WORKER_ENABLED=true` and `BULLMQ_REDIS_URL` are only required when running the worker; `REDIS_URL` is a platform-binding fallback. | | Mail providers such as Postmark when describing active transactional mail | Mailjet transactional mail boundary | Mail setup, architecture, and feature inventory docs. | `packages/mail` provides the Mailjet adapter and `MailProvider` contract. Historical provider comparisons may remain if clearly labelled historical. | | `Email` as a product/marketing feature | `transactional mail` for core mail, `Email Marketing` as plugin candidate | Feature classification docs. | Core keeps password reset, invitations, and system mail; Email Marketing is a plugin-candidate product surface. | | Full-boilerplate retention language | Pruned core with plugin candidates and removal candidates | Roadmap, feature inventory, and migration overview docs. | `docs/feature-inventory.md` is the current classification source. | | Claims that plugin candidates are already extracted plugins | Plugin-candidate or extraction target | Feature docs, architecture docs, and roadmap docs. | Use `plugin-candidate` unless the code has actually moved and the slice verifies the new boundary. | | Claims that remove candidates are supported legacy behavior | Remove-candidate or pruning target | Feature inventory and feature decision docs. | R003 means replacement/removal, not permanent compatibility adapters. | | Vercel-only or generic hosted deployment wording | Current CI/deployment wording with Gitea, Docker smoke, Coolify, and documented deploy guides | CI and deployment docs. | README confirms Gitea CI, Docker smoke gates, `docs/deployment.md`, and `docs/deployment-porting.md`. | | Registry-push or production-deploy claims in baseline CI | Local Docker smoke validation only | CI docs for this milestone. | The baseline Docker smoke job validates build/configuration without registry login, image push, artifact publishing, deployment, or browser/E2E flows. | | Broad `E2E runs by default` language | E2E/browser verification is manual unless explicitly approved | Verification sections and CI docs. | Auto-mode docs should name non-E2E checks and mark browser/E2E as manual. | | `apps/web` as skeleton-only | `apps/web` active web source home | Workspace and architecture docs. | The physical React Router app source and public assets live under `apps/web`; root-level `app/` is not the active tree. | | `apps/api` as health-only or fully extracted API | `apps/api` runtime-wired Hono API app with temporary proxy exceptions | API architecture docs. | It owns the canonical `/v1` route surface, route-access inventory, and OpenAPI docs. Some `/v1` routes proxy web-owned internal handlers until service extraction, so docs must name both boundaries when relevant. | | `apps/worker` as always-on production worker | Opt-in worker runtime with queue-group processors | Worker and queue docs. | The worker exits unless `WORKER_ENABLED=true`; current processors are wired by `WORKER_QUEUE_GROUP` for `general`, `mail`, `import`, `webhook`, and `plugin` workloads. | | `packages/config`, `packages/queue`, `packages/ui`, `packages/shared` as fully wired runtime packages | Skeleton-only or contract package surfaces | Package and architecture docs. | Describe their current contract status precisely. | | Public form/portal/site URLs as tenant-app routes | Separate public URL boundaries | UrlUtils and routing docs. | Do not normalize public slugs or share URLs through internal tenant-app helper wording. | | Direct tenant-app path concatenation as desired pattern | `UrlUtils` helper migration target | Internal routing migration docs. | Use `UrlUtils.getModulePath(...)` or documented helper plans for internal tenant-app links; public URLs are separate. | ## Do-Not-Replace Rules Do not blindly rename the following public, runtime, package, or historical identifiers while porting documentation: - `@toolctrl/*` workspace imports and package names, including `@toolctrl/config`, `@toolctrl/db`, `@toolctrl/mail`, `@toolctrl/queue`, and `@toolctrl/ui`. - Root package and workspace package names that are confirmed by `package.json` files, including `toolctrl-boilerplate`. - Queue names and runtime strings such as `toolctrl.email` when they represent actual queue identifiers or integration contracts. - Environment variable names such as `APP_NAME`, `SESSION_SECRET`, `CRYPTO_SECRET`, `DATABASE_URL`, `DIRECT_URL`, `WORKER_ENABLED`, `BULLMQ_REDIS_URL`, and `REDIS_URL`. - Database, Prisma, migration, and generated-client paths such as `prisma/`, `prisma/schema/schema.prisma`, `packages/db/generated/prisma/client`, and `@toolctrl/db/client.server`. - Current local examples that are still accurate, especially `app.localhost:3000` and `admin.localhost:3000` for the default web loop, `api.localhost:3001` only for the standalone API runtime, `127.0.0.1`, seeded users such as `admin@email.com`, and sample passwords used only in local seed documentation. - Third-party product names that describe actual dependencies or providers, including Prisma, PostgreSQL, Supabase-style PostgreSQL URLs, Redis, BullMQ, Mailjet, Hono, React Router, Gitea, Docker, Fly.io, Vercel, AWS Lightsail, and Coolify. - Historical decision/report references, milestone IDs, slice IDs, decision IDs, and files under `.tc/` or `docs/feature-decisions/` unless a task explicitly updates those records. - Public route shapes and user-facing slugs in examples when the document is explaining public boundaries, for example public forms, portals, sites, help pages, or share links. ## Doc-Specific Application Notes ### README and quick-start docs - Keep the quick start centered on Corepack, `pnpm@10.33.0`, `.env`, Prisma setup, seed data, and `pnpm dev`. - Preserve the distinction between required database settings and optional worker Redis settings. - Prefer `app.localhost:3000` and `admin.localhost:3000` for the default web dev loop; use `api.localhost:3001` only when the standalone API runtime is started separately. - Do not add browser/E2E execution as an automatic task-level check; label it manual unless a later human-approved verification plan says otherwise. ### Architecture and migration docs - Describe boundary status in present tense and distinguish `runtime-wired`, `partially runtime-wired`, `skeleton-only`, and `source-only` surfaces. - Keep the split clear: `apps/web/app` owns web source, while root scripts still expose the top-level developer command surface. - For `packages/db`, say the package owns the server-side Prisma client boundary and commands, while root Prisma schema and migrations still live under `prisma/`. - For `packages/mail`, say Mailjet is the active transactional mail provider boundary. - For `packages/queue`, say BullMQ contracts exist but production wiring is not complete unless a later task proves otherwise. ### Feature inventory and pruning docs - Use the pruned-core classification from `docs/feature-inventory.md` as the source of truth. - Keep Auth, Accounts, Tenants, Admin/Tenant shell, DB/ORM, transactional mail, Billing, i18n, and UI primitives described as core surfaces. - Describe CRM, Email Marketing, Workflow Engine, Knowledge Base, Portals/Sites, Forms/Surveys, Analytics/Metrics, Events/Webhooks/Audit Trails, No-code Entity Builder, AI/Chatbot/Prompt Builder/MCP, Help Desk, Onboarding Builder, Feature Flags, and Widgets as plugin candidates or review/removal targets according to the inventory. - Do not imply optional product areas are already extracted unless the relevant code and tests have moved. ### Deployment and CI docs - Align CI prose with the active Gitea pipeline summary from `README.md` and the baseline gate runner. - Treat `pnpm run verify:ci-baseline` and `pnpm run verify:gitea-ci-workflow` as static local verification commands for the baseline CI contract. - Explain Docker smoke as local image/build, web artifact validation, web/api/worker runtime smoke, and compose validation, not registry publishing or production deployment. - Keep baseline CI wording separate from the deploy workflow: baseline CI Docker smoke does not push or deploy, while `.gitea/workflows/deploy.yml` publishes full-SHA staging images, updates the staging alias, triggers Coolify staging, and promotes verified staged images to the production `main` alias through `workflow_dispatch`. ### Provider and runtime wording - Use `PostgreSQL` and `Prisma` for the active database stack. - Use `Mailjet` for transactional mail provider wording. - Use `BullMQ` and Redis only where worker/queue behavior is being described. - Use `React Router` for the current `@toolctrl/web` runtime under `apps/web/app`. - Use `Hono` for the `apps/api` runtime-wired API app. ## Verification Use these exact non-E2E checks after creating or editing this guide: ```sh test -s docs/porting-guide.md grep -n "^## Rename/Replace Map" docs/porting-guide.md grep -n "^## Do-Not-Replace Rules" docs/porting-guide.md grep -n "^## Verification" docs/porting-guide.md node -e "const fs=require('fs'); const text=fs.readFileSync('docs/porting-guide.md','utf8'); const banned=/\\b(TODO|TBD|FIXME|XXX)\\b/; if (banned.test(text)) { console.error('unresolved marker in docs/porting-guide.md'); process.exit(1); }" ``` Manual downstream review: after each documentation slice ports another file, compare remaining legacy terms against this map and the baseline inventory report at `.tc/baselines/porting-docs/baseline-health.md`. Any remaining old brand, provider, domain, package, database, runtime, or deployment wording should be classified as intentionally protected, historically accurate, or changed in the same documentation slice.