Skip to content

Latest commit

 

History

History
122 lines (90 loc) · 8.93 KB

File metadata and controls

122 lines (90 loc) · 8.93 KB

Repository Guidelines

Project Structure & Module Organization

  • src/ — TanStack Start app code (routes, components, styles).
  • convex/ — Convex backend (schema, queries/mutations/actions, HTTP routes).
  • convex/_generated/ — generated Convex API/types; committed for builds.
  • docs/ — product/spec docs (see docs/spec.md).
  • public/ — static assets.

Build, Test, and Development Commands

  • bun run dev — local app server at http://localhost:3000.
  • bun run build — production build (Vite + Nitro).
  • bun run preview — preview built app.
  • bunx convex dev — Convex dev deployment + function watcher.
  • bunx convex codegen — regenerate convex/_generated.
  • bun run format:check — formatting check.
  • bun run lint — Biome + oxlint (type-aware).
  • bun run test — Vitest (unit tests).
  • bun run coverage — coverage run; keep global >= 80%.

Coding Style & Naming Conventions

  • TypeScript strict; ESM.
  • Indentation: 2 spaces, single quotes (Biome).
  • Lint/format: Biome + oxlint (type-aware).
  • Convex function names: verb-first (getBySlug, publishVersion).

Testing Guidelines

  • Framework: Vitest 4 + jsdom.
  • Tests live in src/** and convex/lib/**.
  • Coverage threshold: 80% global (lines/functions/branches/statements).
  • Example: convex/lib/skills.test.ts.

Commit & Pull Request Guidelines

  • Commit messages: Conventional Commits (feat:, fix:, chore:, docs:…).
  • Keep changes scoped; avoid repo-wide search/replace.
  • Before commit/PR handoff, run bun run format:check and bun run lint; include commands run in the PR summary.
  • PRs: include summary + test commands run. Add screenshots for UI changes.
  • Before merging any PR, verify TypeScript cleanly with bunx tsc -p packages/schema/tsconfig.json --noEmit and bunx tsc -p packages/clawhub/tsconfig.json --noEmit; if Convex code changed, also run the repo typecheck path used by deploy so bunx convex deploy will not fail on tsc.
  • GitHub comments: for multiline gh comments/close messages, use --body-file, --input, or stdin/heredoc with real newlines; never pass literal \\n in shell strings.
  • Reject PRs that add skills into source code/repo content directly (for example under skills/ or seed-only additions intended as published skills). Skills must be uploaded/published via CLI.
  • Repo-local Convex developer skills under .agents/skills/convex*/ are allowed when they support working on this codebase; keep top-level skills/ reserved for installed/published skill content and ignored by git.

Production Release

  • Production deploys are manual-only. Merging to main does not deploy.
  • To release production, start the GitHub Actions Deploy workflow from main: gh workflow run deploy.yml --repo openclaw/clawhub --ref main
  • The workflow supports full, backend, and frontend targets.
  • frontend currently means: wait for the Vercel production deploy for the selected main SHA, then run production smoke checks. It does not call vercel deploy directly yet.
  • The workflow uses the GitHub Production environment for deploy secrets, but it does not require a separate approval step.
  • Prod deploy secrets live on the Production environment, not as ordinary repo secrets. Required: CONVEX_DEPLOY_KEY. Optional: PLAYWRIGHT_AUTH_STORAGE_STATE_JSON.
  • CLI npm releases are also manual-only and tag-based. Stable tags only: vX.Y.Z. Start ClawHub CLI NPM Release from main, first with preflight_only=true, then rerun it with the same tag and the successful preflight_run_id.
  • Real CLI publishes wait at the GitHub npm-release environment and use npm trusted publishing. Required npm trusted publisher settings: repository openclaw/clawhub, workflow clawhub-cli-npm-release.yml, environment npm-release.

Git Notes

  • If git branch -d/-D <branch> is policy-blocked, delete the local ref directly: git update-ref -d refs/heads/<branch>.

URL Quick Reference

  • Canonical site: https://clawhub.ai (prefer this over legacy domains).
  • Skill page URL format: https://clawhub.ai/<owner>/<slug> (owner handle preferred; falls back to owner id).
  • Skill API detail URL: https://clawhub.ai/api/v1/skills/<slug>.
  • Skill file URL: https://clawhub.ai/api/v1/skills/<slug>/file?path=SKILL.md.
  • For “full URL?” requests, return the canonical page URL first, then API URL if useful.

Configuration & Security

  • Local env: .env.local (never commit secrets).
  • Convex env holds JWT keys; Vercel only needs VITE_CONVEX_URL + VITE_CONVEX_SITE_URL.
  • OAuth: GitHub OAuth App credentials required for login.

Convex Ops (Gotchas)

  • New Convex functions must be pushed before convex run: use bunx convex dev --once (dev) or bunx convex deploy (prod).
  • For non-interactive prod deploys, use bunx convex deploy -y to skip confirmation.
  • If bunx convex run --env-file .env.local ... returns 401 MissingAccessToken despite bunx convex login, workaround: omit --env-file and use --deployment-name <name> / --prod.

Convex Query & Bandwidth Rules

  • Always use .withIndex() instead of .filter() for fields that can be indexed. .filter() causes full table scans — every doc is read and billed. Even a single .filter() on a 16K-row table reads ~16 MB per call.
  • Convex reads entire documents — no field projections. If you only need a few fields from large docs (~6 KB+), denormalize a lightweight summary onto the parent doc or use a lookup table (see embeddingSkillMap, skill.latestVersionSummary, skill.badges for examples).
  • Denormalization pattern: persist computed fields so they can be indexed. Every mutation that updates source fields must also update the denormalized field. Always write a cursor-based backfill for new fields (see backfillIsSuspiciousInternal, backfillLatestVersionSummaryInternal, backfillDenormalizedBadgesInternal for examples).
  • Cron jobs must never scan entire tables. Use indexed queries with equality filters. Use cursor-based pagination for large datasets. Prefer incremental/delta tracking over full recounts.
  • 32K document limit per query. Split .collect() calls by a partition field (e.g., one day at a time instead of a 7-day range). See rebuildTrendingLeaderboardAction in convex/leaderboards.ts for an example.
  • Common mistakes: .filter().collect() without an index; ctx.db.get() on large docs in a loop for list views; while loops that paginate the whole table to find filtered results.
  • Before writing or reviewing Convex queries, check deployment health. Run bunx convex insights to check for OCC conflicts, bytesReadLimit, and documentsReadLimit errors. Run bunx convex logs --failure to see individual error messages and stack traces. This helps identify which functions are causing bandwidth issues so you can prioritize fixes.

This project uses Convex as its backend.

When working on Convex code, always read convex/_generated/ai/guidelines.md first for important guidelines on how to correctly use Convex APIs and patterns. The file contains rules that override what you may have learned about Convex from training data.

Convex agent skills for common tasks can be installed by running npx convex ai-files install.

Stat Field Migration Rules

The skills table maintains two parallel sets of stat fields as part of an in-progress field migration:

Legacy (nested, @deprecated) Top-level (source of truth, indexable)
stats.downloads statsDownloads
stats.stars statsStars
stats.installsCurrent statsInstallsCurrent
stats.installsAllTime statsInstallsAllTime

Rules:

  • Always use readCanonicalStat(skill, field) (convex/lib/skillStats.ts) to read any of the four migrated fields. It prefers the top-level field and falls back to the nested field for pre-migration documents. Never access skill.stats.downloads / .stars / .installsCurrent / .installsAllTime directly.
  • Always use applySkillStatDeltas() to write stat deltas. It writes both the top-level and nested fields in the same patch to keep them in sync.
  • Both sets of fields must be written together in any patch that touches stat values (see the return shape of applySkillStatDeltas).
  • Nested-only reads are acceptable only for stats.comments and stats.versions — no top-level field exists for these yet.
  • The four legacy nested fields are marked @deprecated in statsValidator (schema.ts). Any IDE access to skill.stats.downloads etc. will show a strikethrough warning — treat this as a signal to use readCanonicalStat() instead.
  • When adding new stat fields, follow the same dual-write pattern and add a cursor-based backfill mutation (see backfillSkillStatFieldsInternal for an example).