chore(harness): make repository agent-ready

Add concise routed context, task-aware agent roles, focused skills, and structured learning promotion.\n\nUnify local development and workspace tooling, add layered test and E2E verification, harden MCP authentication, and gate deployment behind verification and explicit production promotion.
This commit is contained in:
jjbrunton committed 2026-08-27 20:58:48 +01:00
1 parent 5a69bbc1a5
commit 01bb8225ae
125 files changed
+5078 -24529

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
# Supabase backend
This directory owns local configuration, migrations, Edge Functions, seeds, and
integration tests.
## Schema safety
- Create migrations only with `npm run db:migration:new -- <name>`.
- Edit only `apps/supabase/migrations/`; this is the authoritative migration tree.
- Never use any remote `apply_migration` tool.
- Never execute DDL through MCP SQL tools; those tools are `SELECT`-only.
- Never link or reset a remote project during local verification.
- Cron/function URLs must resolve per environment, never hardcode production.
## Function boundaries
- Set deployment JWT configuration deliberately per function. A function using
`verify_jwt = false` must implement the correct manual, webhook, cron, or internal
authentication boundary; it is not permission to omit authorization.
- Service-role clients never run in customer clients.
- Preserve CORS handling and return non-sensitive errors.
- Test RLS, authorization, and important side effects, not only HTTP status.
Verify with local reset, `npm run test:supabase`, invariant lint, and affected
client tests. See `docs/operations/database-migrations.md`.
-136
View File
@@ -1,136 +0,0 @@
# Supabase - Agent Guidelines
**STRICT RULES - DO NOT VIOLATE**
## Architecture
This contains Supabase configuration:
- **Migrations**: SQL migrations in `migrations/`
- **Edge Functions**: Deno functions in `functions/`
- **Email Templates**: Auth email templates in `email-templates/`
- **Config**: Local development config in `config.toml`
## Directory Structure
```
functions/
delete-relationship/ # Delete couple relationship
manage-couple/ # Join/leave couple
revenuecat-webhook/ # Subscription webhook handler
send-message-notification/ # Push notification for messages
send-notification/ # Push notification for matches
submit-response/ # Save response, check matches
sync-subscription/ # Sync RevenueCat status
migrations/ # SQL migrations
email-templates/ # Auth email templates
config.toml # Local dev configuration
```
## Critical Rules
### Edge Function Deployment
**ALWAYS use `verify_jwt: false`** when deploying via MCP tools.
Supabase's built-in JWT verification causes 401 errors. Handle auth manually:
```typescript
const { data: { user }, error: authError } = await supabase.auth.getUser(
authHeader.replace("Bearer ", "")
);
```
### Edge Function Pattern
All edge functions MUST follow this structure:
```typescript
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
Deno.serve(async (req) => {
// Handle CORS preflight
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
try {
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);
// Manual auth verification
const authHeader = req.headers.get("Authorization");
if (!authHeader) {
return new Response(
JSON.stringify({ error: "Missing authorization header" }),
{ status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }
);
}
const { data: { user }, error: authError } = await supabase.auth.getUser(
authHeader.replace("Bearer ", "")
);
// ... function logic
} catch (error) {
return new Response(
JSON.stringify({ error: "Internal server error" }),
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
);
}
});
```
### Function Auth Methods
| Function | Auth Method |
|----------|-------------|
| `manage-couple` | Manual via getUser() |
| `submit-response` | Manual via getUser() |
| `sync-subscription` | Manual via getUser() |
| `delete-relationship` | Manual via getUser() |
| `revenuecat-webhook` | Webhook signature validation |
| `send-notification` | Internal trigger (no auth) |
| `send-message-notification` | Internal trigger (no auth) |
### Migration Rules
- Use `apply_migration` MCP tool for DDL operations
- Never hardcode generated IDs in data migrations
- Always verify target environment (prod vs non-prod) before running
### Database Patterns
- Use UPSERT with `onConflict` for responses: `onConflict: "user_id,question_id"`
- Use UPSERT for matches: `onConflict: "couple_id,question_id"`
- RLS policies enforce couple-level data isolation
### Profile Creation
The `on_auth_user_created` trigger automatically creates a profile. Never assume a profile exists without checking - check for null before operations.
## MCP Environments
| Environment | MCP Server |
|-------------|------------|
| Production | `mcp__sauci-prod__*` |
| Non-Production | `mcp__sauci-non-prod__*` |
**ALWAYS verify environment before destructive operations.**
## Forbidden Actions
1. **DO NOT** use `verify_jwt: true` when deploying functions via MCP
2. **DO NOT** skip CORS headers in responses
3. **DO NOT** use service role key on client-side
4. **DO NOT** hardcode IDs in migrations
5. **DO NOT** assume profiles exist - always check for null
6. **DO NOT** run migrations on production without explicit confirmation
7. **DO NOT** expose internal error details in responses
+8 -2
View File
@@ -1,5 +1,11 @@
{
"name": "@sauci/supabase",
"version": "1.0.0",
"private": true
"version": "1.0.0",
"private": true,
"scripts": {
"lint": "node ../../scripts/lint-harness.mjs --scope supabase",
"test": "node -e \"console.log('Supabase integration tests run through npm run test:supabase')\"",
"typecheck": "node -e \"console.log('Edge Functions are checked by Deno during test:supabase')\"",
"build": "node -e \"console.log('Supabase deployables do not require a build step')\""
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ export async function createRedemptionCode() {
const { data, error } = await adminClient.from("redemption_codes").insert({
code,
max_uses: 1,
active: true,
is_active: true,
expires_at: new Date(Date.now() + 3600000).toISOString() // 1 hour
}).select().single();