Skip to content

Latest commit

 

History

History
361 lines (285 loc) · 13.4 KB

File metadata and controls

361 lines (285 loc) · 13.4 KB

Agent rules for forks of zerostack

Read this file before every non-trivial task in a fork. It defines the contract between zerostack (the template, tracked as upstream) and the product code in this fork.

This file is companion to forking-checklist.md and the root AGENTS.md. When they disagree, this file wins inside a fork.

The rule of thumb: does upstream ship updates to this file?

  • Yes → it's frozen or customize, do not edit casually.
  • No → it's extend, write whatever the product needs.

Three classes of files

🔒 Frozen — do not edit

Auth, SEO, CI, and infrastructure. Edits here cause merge conflicts the next time we pull upstream/main. If you think one of these needs changing, say so to the user — they'll decide whether to patch the fork (and absorb a future conflict) or upstream the change.

# Auth + Supabase wiring
packages/supabase/src/{browser,server,service,index}.ts
apps/web/src/lib/supabase/{client,server,proxy,service,env}.ts
apps/web/src/lib/admin.ts
apps/web/src/proxy.ts
apps/web/src/app/login/
apps/web/src/app/auth/

# SEO surface
apps/web/src/app/robots.ts
apps/web/src/app/sitemap.ts
apps/web/src/app/opengraph-image.tsx

# Template surfaces (frozen — extend by composition, not by rewrite)
apps/web/src/app/app/layout.tsx
apps/web/src/app/app/app-nav.tsx
apps/web/src/app/app/settings/
apps/web/src/app/app/admin/
apps/web/src/components/feedback/
apps/web/src/components/waitlist/
apps/web/src/components/theme-*.tsx
apps/web/src/components/ui/

# Build + CI
.github/workflows/ci.yml
.husky/
turbo.json
pnpm-workspace.yaml
tsconfig*.json (root + packages/config/)

# Migrations that ship with the template
supabase/migrations/20260529000000_init_notes.sql
supabase/migrations/20260529001000_init_profiles.sql
supabase/migrations/20260529002000_init_waitlist_feedback.sql

Frozen files have a few legitimate exceptions in a fork:

  • apps/web/src/app/app/app-nav.tsx — you may add extra nav links if the upstream design exposed a slot for them. If not, propose to upstream instead.
  • apps/web/src/components/ui/ — these are generated by shadcn. Adding new components via pnpm dlx shadcn@latest add ... is fine. Editing existing ones is not.
  • Migrations are append-only. Adding a new migration file is extending (see below). Editing or deleting an existing template migration is frozen — it would diverge schemas across forks.

🎨 Customize — edit freely, conflicts auto-resolve to your version

Branding, copy, and config that every product changes. .gitattributes in zerostack marks these merge=ours, so git merge upstream/main keeps the fork's version on conflict.

apps/web/src/lib/site.ts
apps/web/src/app/layout.tsx          # metadata, fonts, providers
apps/web/src/app/page.tsx            # landing page
apps/web/src/components/site-nav.tsx
apps/web/src/components/site-footer.tsx
README.md
LICENSE
.env.example
package.json (root)                  # name, version, scripts

Editing here doesn't require a flag; it's the expected surface.

If you add a new file that's purely product copy (e.g. /pricing), it's extend, not customize. The customize list only covers files that also exist in upstream.

✏️ Extend — write product code here

Your product lives in these directories. Upstream rarely touches them; when it does, conflicts are real signals (someone duplicated a name) and worth reading.

apps/web/src/app/app/<your-feature>/   # new feature routes
apps/web/src/app/<your-marketing>/     # /pricing, /about, /blog, …
apps/web/src/components/<feature>/     # product-specific components
packages/contracts/src/index.ts        # ADD schemas; don't remove template ones
supabase/migrations/2026MMDD…_<name>.sql  # new migrations
docs/<your-doc>.md                     # product-specific docs

The template notes feature was deliberately left in apps/web/src/app/app/ so you have a reference. You may delete it once your product replaces it — that's the user's call, made during forking (see step 5 of forking-checklist.md). Once the choice is made, record it at the top of this file (the fork's copy) so I don't re-add notes later thinking it was missing.


How to do common tasks

Add a new table

  1. Create supabase/migrations/<YYYYMMDDHHMMSS>_<name>.sql. Copy the structure from 20260529000000_init_notes.sql:
    • id uuid primary key default gen_random_uuid()
    • user_id uuid not null references auth.users(id) on delete cascade
    • timestamps
    • alter table … enable row level security
    • explicit policies for select / insert / update / delete, all gated by auth.uid() = user_id
  2. Add a schema to packages/contracts/src/index.ts<name>Schema for the row, create<Name>Schema for the input. Mirror the noteSchema / createNoteSchema pattern.
  3. Create the route under apps/web/src/app/app/<feature>/:
    • page.tsx — Server Component fetching rows + rendering.
    • actions.ts — Server Actions for create/update/delete.
    • <feature>-form.tsx"use client" form bound to the action via useActionState.
  4. Validate every input with the create<Name>Schema before touching the DB. Parse every row with <name>Schema before rendering — Zod is the boundary check between Postgres and the UI.
  5. Do not run pnpm db:types in a fork unless the user asks. The placeholder is permissive enough for product code; regenerating is a one-time-per-fork chore that lives in the checklist.

Add a public marketing page

Create apps/web/src/app/<route>/page.tsx. Use the existing landing as a reference for typography and spacing. Add <a href="#…"> entries to site-nav.tsx if it should be discoverable.

If the page needs metadata, export metadata: Metadata from the page file — metadataBase and the default title template come from apps/web/src/app/layout.tsx (frozen).

Add an admin section

Append a new <Card> to apps/web/src/app/app/admin/page.tsx. The service-role client and isAdminEmail guard are already wired up.

If the admin needs its own routes (/app/admin/users, /app/admin/stats), create them under apps/web/src/app/app/admin/ and reuse the existing guard at the top of each page.

Add a transactional email

The template does not ship an email integration. When the product needs one, the recommended path is Resend with RESEND_API_KEY in env and a small lib/email.ts helper. Coordinate with the user before adding the dependency.

Change the auth provider or add OAuth

This is not a fork-level change. Supabase Auth supports OAuth through dashboard config; flip the toggles in Authentication → Providers and add the OAuth callback URL. Code in apps/web/src/app/login/ stays the same.

Touch a frozen file anyway

Stop. Ask the user. Two paths:

  1. The change is product-specific. Use composition instead — wrap the frozen component, don't edit it. Add a sibling file that replaces the use of the frozen one in your product code.
  2. The change should benefit every zerostack fork. Make it in the upstream clone of zerostack, ship it, then git merge upstream/main into the fork.

If neither works and you really must patch a frozen file in the fork, document the patch in docs/upstream-drift.md (create if missing) so the next merge knows where the conflict will appear.


Commit messages and code style in a fork

The root AGENTS.md rules apply unchanged:

  • Conventional Commits with a why body.
  • No session-local references, no secrets.
  • Comments only when WHY is non-obvious.
  • Incremental commits — one logical step per commit.

The pre-commit hook (pnpm lint-staged + pnpm typecheck) is frozen. Don't disable it; if it slows you down, the cache makes the second run sub-second.


When you notice something wrong with the template itself

You're working in a fork, and you've spotted a bug, dead doc link, or inconsistency in upstream zerostack (frozen files, shipped docs, CI config). Apply this decision tree before doing anything:

                  Is the change useful only to THIS product?
                                │
                       ┌────────┴────────┐
                      Yes                No
                       │                 │
                  Patch locally      Is it a security issue?
                  in the fork.            │
                  Do not upstream.   ┌────┴────┐
                                    Yes        No
                                     │         │
                                Private        Is it a verifiable bug
                                disclosure     OR a doc fix < 20 lines?
                                (security           │
                                advisory).    ┌─────┴─────┐
                                             Yes          No
                                              │           │
                                         Open a PR    Is it a substantial
                                         upstream.    feature / refactor?
                                                          │
                                                     ┌────┴────┐
                                                    Yes        No
                                                     │         │
                                                Open an     Keep it local.
                                                Issue       Below threshold.
                                                ("proposal")

Hard severity threshold

Do not open issues or PRs upstream below this bar:

  • ✅ Real bug in shipped behaviour (auth flow, RLS policy, build, CI gate, type error from a default clone).

  • ✅ Documentation typo, broken link, or outdated instruction that misleads a future reader.

  • ✅ Security vulnerability — but private channel only, not public issue. See .github/SECURITY.md.

  • ✅ Concrete proposal for a feature that benefits every fork and the user explicitly agreed to upstream it.

  • ❌ Personal preferences ("I'd rename this", "I'd format this differently").

  • ❌ Refactors without a defect.

  • ❌ Vendor integrations (Stripe, Resend, OAuth provider, analytics) — those belong in forks.

  • ❌ "Wouldn't it be nice if…" without a concrete user.

  • ❌ Anything you can fix locally in the fork without affecting other forks.

When in doubt: don't open it. The maintainer prefers missing a mediocre suggestion over drowning in noise.

Before opening anything

Always check first:

# Are there existing issues / PRs covering this?
gh issue list --repo zerox9dev/zerostack --search "<keywords>"
gh pr list    --repo zerox9dev/zerostack --search "<keywords>"

If a duplicate exists, comment there instead of opening a new one.

How to open an issue upstream

gh issue create --repo zerox9dev/zerostack --web

--web opens the prefilled form. Pick the right template:

  • Bug report for reproducible defects.
  • Improvement proposal for new ideas (mandatory before any feature PR).

Fill every required field. Concrete reproduction beats long prose. Cross-link the upstream commit your fork is based on so the maintainer can rebase to that snapshot.

How to open a PR upstream

The maintainer requires a real GitHub user account for the PR — you cannot push to zerox9dev/zerostack directly. The flow:

# 1. One-time: fork zerostack on the user's account
gh repo fork zerox9dev/zerostack --clone=false

# 2. Clone the user's fork to a working directory outside the product
cd /tmp
gh repo clone <username>/zerostack zerostack-upstream-pr
cd zerostack-upstream-pr
git remote add upstream git@github.com:zerox9dev/zerostack.git
git fetch upstream
git checkout -b fix/<short-slug> upstream/main

# 3. Apply the smallest patch that fixes the problem
#    — copy from the product fork, do NOT bring along product code
pnpm install
pnpm typecheck && pnpm lint && pnpm build

# 4. Commit with conventional-commits, push, open PR
git add -p   # stage exactly the patch — nothing else
git commit -m "fix(scope): one-line subject

Body: what's wrong and why this fixes it. Reference the upstream
issue if one exists. Keep the surface minimal — see CODEOWNERS for
which files require maintainer review.
"
git push -u origin fix/<short-slug>
gh pr create --fill --web

Strict requirements:

  1. One concern per PR. Don't bundle the fix with a tangential cleanup, even a tempting one.
  2. Don't touch frozen files unless the bug is in a frozen file itself (and then say so in the PR body).
  3. No product code. Anything specific to this fork's product must not appear in the diff.
  4. PR template filled completely. Skipped checkboxes are a closed PR.
  5. CI must pass. Push again after fixing if it doesn't.

What to do instead most of the time

You are unlikely to need this flow often. The default is:

  1. Fix the problem locally in the fork.
  2. Note it in this fork's docs/upstream-drift.md (create the file if missing) so future merges with upstream/main know about the patch.
  3. Continue with the product work.

Upstreaming is for cases where keeping the patch local would mean solving the same problem in every future fork — i.e. it's a template-level concern, not a product-level one.