vajeeda commited on
Commit
b6e19c7
Β·
0 Parent(s):

base structure of the project formed

Browse files
.claude/skills/debugging.md ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Skill β€” Debugging Patterns by Error Type
2
+
3
+ ## Purpose
4
+ Read this file when stuck on a specific error type.
5
+ Contains reusable debug snippets organised by error category.
6
+ Always check logs/errors.log first before opening this file.
7
+
8
+ ---
9
+
10
+ ## Null & Undefined Errors
11
+
12
+ ### Pattern
13
+ ```typescript
14
+ // Guard at function entry β€” always first line
15
+ function myFunction(input: string | null) {
16
+ if (!input) {
17
+ handleError(new Error('input is null'), 'file.ts', 'myFunction')
18
+ return null
19
+ }
20
+ // safe to use input below
21
+ }
22
+ ```
23
+
24
+ ### Checklist
25
+ - [ ] Is the value null before it enters the function?
26
+ - [ ] Is the value undefined because an async call hasn't resolved?
27
+ - [ ] Is optional chaining `?.` missing somewhere?
28
+ - [ ] Is the DB returning null instead of empty array?
29
+
30
+ ---
31
+
32
+ ## Async & Promise Errors
33
+
34
+ ### Pattern
35
+ ```typescript
36
+ // Always await, always catch
37
+ async function fetchData() {
38
+ try {
39
+ const result = await someAsyncCall()
40
+ if (!result) throw new Error('result is empty')
41
+ return result
42
+ } catch (err) {
43
+ handleError(err, 'file.ts', 'fetchData')
44
+ return null
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Checklist
50
+ - [ ] Is `await` missing before an async call?
51
+ - [ ] Is a `.then()` chain missing a `.catch()`?
52
+ - [ ] Is a Promise being returned without awaiting it?
53
+ - [ ] Are two async calls racing without proper sequencing?
54
+
55
+ ---
56
+
57
+ ## Type Errors
58
+
59
+ ### Pattern
60
+ ```typescript
61
+ // Validate type at boundary before using
62
+ function processUser(user: unknown) {
63
+ if (!user || typeof user !== 'object') {
64
+ handleError(new Error('invalid user type'), 'file.ts', 'processUser')
65
+ return null
66
+ }
67
+ const typedUser = user as User
68
+ // safe to use typedUser below
69
+ }
70
+ ```
71
+
72
+ ### Checklist
73
+ - [ ] Is an `any` type hiding a real type mismatch?
74
+ - [ ] Is an API response being used without type validation?
75
+ - [ ] Is a number being used where a string is expected?
76
+
77
+ ---
78
+
79
+ ## React Render Errors
80
+
81
+ ### Pattern
82
+ ```typescript
83
+ // Guard before rendering
84
+ function UserCard({ user }: { user: User | null }) {
85
+ if (!user) return null // or return <Skeleton />
86
+ return <div>{user.name}</div>
87
+ }
88
+ ```
89
+
90
+ ### Checklist
91
+ - [ ] Is a component rendering before data is loaded?
92
+ - [ ] Is `useEffect` missing a dependency causing stale state?
93
+ - [ ] Is state being mutated directly instead of via setter?
94
+ - [ ] Is `useEffect` running twice due to React strict mode?
95
+
96
+ ---
97
+
98
+ ## API Route Errors
99
+
100
+ ### Pattern
101
+ ```typescript
102
+ // Always validate request body
103
+ export async function POST(req: Request) {
104
+ try {
105
+ const body = await req.json()
106
+ if (!body?.userId) {
107
+ return Response.json(
108
+ { error: 'userId is required' },
109
+ { status: 400 }
110
+ )
111
+ }
112
+ // proceed
113
+ } catch (err) {
114
+ handleError(err, 'app/api/route.ts', 'POST')
115
+ return Response.json({ error: 'internal server error' }, { status: 500 })
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Checklist
121
+ - [ ] Is the request body being parsed correctly?
122
+ - [ ] Is the auth session being checked before processing?
123
+ - [ ] Is a 400 returned for bad input vs 500 for server error?
124
+ - [ ] Is the API route method correct (GET vs POST)?
125
+
126
+ ---
127
+
128
+ ## Environment Variable Errors
129
+
130
+ ### Pattern
131
+ ```typescript
132
+ // Validate env vars at startup
133
+ const requiredEnvVars = [
134
+ 'NEXT_PUBLIC_SUPABASE_URL',
135
+ 'NEXT_PUBLIC_SUPABASE_ANON_KEY'
136
+ ]
137
+
138
+ requiredEnvVars.forEach((key) => {
139
+ if (!process.env[key]) {
140
+ throw new Error(`Missing required env var: ${key}`)
141
+ }
142
+ })
143
+ ```
144
+
145
+ ### Checklist
146
+ - [ ] Is `.env.local` present and not committed?
147
+ - [ ] Are `NEXT_PUBLIC_` prefixes correct for client side vars?
148
+ - [ ] Are env vars available in the deployment environment?
149
+ - [ ] Was the dev server restarted after adding new env vars?
150
+
151
+ ---
152
+
153
+ ## What NOT to Do
154
+ - Do not add console.log and forget to remove it
155
+ - Do not catch an error and return undefined silently
156
+ - Do not assume the error is in a different file than the log says
157
+ - Do not fix the symptom without understanding the root cause
.claude/skills/supabase.md ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Skill β€” Supabase Patterns & Operations
2
+
3
+ ## Purpose
4
+ Read this file when writing any Supabase code.
5
+ Contains reusable patterns, query templates and common gotchas.
6
+ Do not guess schema or policies β€” confirm first.
7
+
8
+ ---
9
+
10
+ ## Client Setup
11
+
12
+ ### Client initialisation (Next.js)
13
+ ```typescript
14
+ // lib/supabase.ts
15
+ import { createClient } from '@supabase/supabase-js'
16
+
17
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
18
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
19
+
20
+ export const supabase = createClient(supabaseUrl, supabaseAnonKey)
21
+ ```
22
+
23
+ ### Server side client (API routes / server components)
24
+ ```typescript
25
+ // lib/supabase-server.ts
26
+ import { createClient } from '@supabase/supabase-js'
27
+
28
+ export const supabaseAdmin = createClient(
29
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
30
+ process.env.SUPABASE_SERVICE_ROLE_KEY!
31
+ )
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Auth Patterns
37
+
38
+ ### Get current session
39
+ ```typescript
40
+ const { data: { session }, error } = await supabase.auth.getSession()
41
+ if (!session) {
42
+ handleError(new Error('no active session'), 'file.ts', 'functionName')
43
+ return null
44
+ }
45
+ ```
46
+
47
+ ### Listen to auth state changes
48
+ ```typescript
49
+ supabase.auth.onAuthStateChange((event, session) => {
50
+ if (event === 'SIGNED_OUT') {
51
+ // clear local state
52
+ }
53
+ if (event === 'TOKEN_REFRESHED') {
54
+ // update session in state
55
+ }
56
+ })
57
+ ```
58
+
59
+ ### Sign out
60
+ ```typescript
61
+ const { error } = await supabase.auth.signOut()
62
+ if (error) handleError(error, 'file.ts', 'signOut')
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Query Patterns
68
+
69
+ ### Select with error handling
70
+ ```typescript
71
+ async function getRows(table: string, userId: string) {
72
+ try {
73
+ const { data, error } = await supabase
74
+ .from(table)
75
+ .select('*')
76
+ .eq('user_id', userId)
77
+
78
+ if (error) throw error
79
+ return data ?? []
80
+ } catch (err) {
81
+ handleError(err, 'lib/supabase.ts', 'getRows')
82
+ return []
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Insert with error handling
88
+ ```typescript
89
+ async function insertRow(table: string, payload: object) {
90
+ try {
91
+ const { data, error } = await supabase
92
+ .from(table)
93
+ .insert(payload)
94
+ .select()
95
+ .single()
96
+
97
+ if (error) throw error
98
+ return data
99
+ } catch (err) {
100
+ handleError(err, 'lib/supabase.ts', 'insertRow')
101
+ return null
102
+ }
103
+ }
104
+ ```
105
+
106
+ ### Update with error handling
107
+ ```typescript
108
+ async function updateRow(table: string, id: string, payload: object) {
109
+ try {
110
+ const { data, error } = await supabase
111
+ .from(table)
112
+ .update(payload)
113
+ .eq('id', id)
114
+ .select()
115
+ .single()
116
+
117
+ if (error) throw error
118
+ return data
119
+ } catch (err) {
120
+ handleError(err, 'lib/supabase.ts', 'updateRow')
121
+ return null
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Delete with error handling
127
+ ```typescript
128
+ async function deleteRow(table: string, id: string) {
129
+ try {
130
+ const { error } = await supabase
131
+ .from(table)
132
+ .delete()
133
+ .eq('id', id)
134
+
135
+ if (error) throw error
136
+ return true
137
+ } catch (err) {
138
+ handleError(err, 'lib/supabase.ts', 'deleteRow')
139
+ return false
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Migration Patterns
147
+
148
+ ### Standard migration file structure
149
+ ```sql
150
+ -- supabase/migrations/[timestamp]_create_users.sql
151
+
152
+ -- Create table
153
+ CREATE TABLE IF NOT EXISTS users (
154
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
155
+ email TEXT NOT NULL UNIQUE,
156
+ created_at TIMESTAMPTZ DEFAULT NOW(),
157
+ updated_at TIMESTAMPTZ DEFAULT NOW()
158
+ );
159
+
160
+ -- Enable RLS
161
+ ALTER TABLE users ENABLE ROW LEVEL SECURITY;
162
+
163
+ -- Policies
164
+ CREATE POLICY "Users can view own record"
165
+ ON users FOR SELECT
166
+ USING (auth.uid() = id);
167
+
168
+ CREATE POLICY "Users can update own record"
169
+ ON users FOR UPDATE
170
+ USING (auth.uid() = id);
171
+ ```
172
+
173
+ ### Add column migration
174
+ ```sql
175
+ ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name TEXT;
176
+ ```
177
+
178
+ ### Add index migration
179
+ ```sql
180
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
181
+ ```
182
+
183
+ ---
184
+
185
+ ## RLS Policy Templates
186
+
187
+ ### Full CRUD for owner
188
+ ```sql
189
+ CREATE POLICY "owner select" ON table_name FOR SELECT USING (auth.uid() = user_id);
190
+ CREATE POLICY "owner insert" ON table_name FOR INSERT WITH CHECK (auth.uid() = user_id);
191
+ CREATE POLICY "owner update" ON table_name FOR UPDATE USING (auth.uid() = user_id);
192
+ CREATE POLICY "owner delete" ON table_name FOR DELETE USING (auth.uid() = user_id);
193
+ ```
194
+
195
+ ### Public read, owner write
196
+ ```sql
197
+ CREATE POLICY "public read" ON table_name FOR SELECT USING (true);
198
+ CREATE POLICY "owner insert" ON table_name FOR INSERT WITH CHECK (auth.uid() = user_id);
199
+ CREATE POLICY "owner update" ON table_name FOR UPDATE USING (auth.uid() = user_id);
200
+ ```
201
+
202
+ ### Service role bypass (admin operations)
203
+ ```sql
204
+ -- Use supabaseAdmin client (service role) β€” bypasses RLS
205
+ -- Never expose service role key client side
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Realtime Patterns
211
+
212
+ ### Subscribe to table changes
213
+ ```typescript
214
+ const channel = supabase
215
+ .channel('table-changes')
216
+ .on('postgres_changes',
217
+ { event: '*', schema: 'public', table: 'your_table' },
218
+ (payload) => {
219
+ console.info(`[INFO] realtime event: ${payload.eventType}`)
220
+ }
221
+ )
222
+ .subscribe()
223
+
224
+ // Cleanup
225
+ channel.unsubscribe()
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Storage Patterns
231
+
232
+ ### Upload file
233
+ ```typescript
234
+ async function uploadFile(bucket: string, path: string, file: File) {
235
+ try {
236
+ const { data, error } = await supabase.storage
237
+ .from(bucket)
238
+ .upload(path, file, { upsert: true })
239
+
240
+ if (error) throw error
241
+ return data
242
+ } catch (err) {
243
+ handleError(err, 'lib/storage.ts', 'uploadFile')
244
+ return null
245
+ }
246
+ }
247
+ ```
248
+
249
+ ### Get public URL
250
+ ```typescript
251
+ function getPublicUrl(bucket: string, path: string): string {
252
+ const { data } = supabase.storage.from(bucket).getPublicUrl(path)
253
+ return data.publicUrl
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Common Gotchas
260
+
261
+ | Gotcha | Rule |
262
+ |--------|------|
263
+ | RLS blocks everything by default | Always add policies after enabling RLS |
264
+ | `.single()` throws if no row found | Use `.maybeSingle()` if row may not exist |
265
+ | Service role bypasses RLS | Never use service role client on frontend |
266
+ | Auth session expires | Always check session before DB operations |
267
+ | Migration order matters | Never edit existing migration files |
268
+ | `.select()` after insert needed | Add `.select().single()` to get inserted row back |
269
+
270
+ ---
271
+
272
+ ## What NOT to Do
273
+ - Do not edit existing migration files β€” create new ones
274
+ - Do not disable RLS on any table
275
+ - Do not use service role key on client side
276
+ - Do not call DB without checking session first
277
+ - Do not use `.single()` when row might not exist
.claude/skills/testing.md ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Skill β€” Reusable Test Patterns by Feature Type
2
+
3
+ ## Purpose
4
+ Read this file when writing tests for a specific feature type.
5
+ Contains reusable test templates organised by category.
6
+ Always follow naming conventions from docs/testing.md.
7
+
8
+ ---
9
+
10
+ ## Auth Feature Tests
11
+
12
+ ```typescript
13
+ // tests/unit/auth/session.test.ts
14
+ describe('session handling', () => {
15
+ it('returns null when no session exists', async () => {
16
+ mockSupabase.auth.getSession.mockResolvedValueOnce({
17
+ data: { session: null }, error: null
18
+ })
19
+ const result = await getSession()
20
+ expect(result).toBeNull()
21
+ })
22
+
23
+ it('returns session when authenticated', async () => {
24
+ mockSupabase.auth.getSession.mockResolvedValueOnce({
25
+ data: { session: mockSession }, error: null
26
+ })
27
+ const result = await getSession()
28
+ expect(result).toEqual(mockSession)
29
+ })
30
+
31
+ it('handles auth error gracefully', async () => {
32
+ mockSupabase.auth.getSession.mockResolvedValueOnce({
33
+ data: { session: null }, error: new Error('auth failed')
34
+ })
35
+ const result = await getSession()
36
+ expect(result).toBeNull()
37
+ })
38
+ })
39
+ ```
40
+
41
+ ---
42
+
43
+ ## DB Query Tests
44
+
45
+ ```typescript
46
+ // tests/unit/lib/users.test.ts
47
+ describe('fetchUser', () => {
48
+ it('returns user for valid id', async () => {
49
+ mockSupabase.from().select().eq.mockResolvedValueOnce({
50
+ data: mockUser, error: null
51
+ })
52
+ const result = await fetchUser('valid-uuid')
53
+ expect(result).toEqual(mockUser)
54
+ })
55
+
56
+ it('returns null for null id', async () => {
57
+ const result = await fetchUser(null as any)
58
+ expect(result).toBeNull()
59
+ })
60
+
61
+ it('returns null for empty string id', async () => {
62
+ const result = await fetchUser('')
63
+ expect(result).toBeNull()
64
+ })
65
+
66
+ it('returns null on DB error', async () => {
67
+ mockSupabase.from().select().eq.mockResolvedValueOnce({
68
+ data: null, error: new Error('DB error')
69
+ })
70
+ const result = await fetchUser('valid-uuid')
71
+ expect(result).toBeNull()
72
+ })
73
+
74
+ it('returns null when DB returns null data', async () => {
75
+ mockSupabase.from().select().eq.mockResolvedValueOnce({
76
+ data: null, error: null
77
+ })
78
+ const result = await fetchUser('valid-uuid')
79
+ expect(result).toBeNull()
80
+ })
81
+ })
82
+ ```
83
+
84
+ ---
85
+
86
+ ## API Route Tests
87
+
88
+ ```typescript
89
+ // tests/integration/api/users.integration.test.ts
90
+ describe('POST /api/users', () => {
91
+ it('returns 400 when userId is missing', async () => {
92
+ const res = await fetch('/api/users', {
93
+ method: 'POST',
94
+ body: JSON.stringify({})
95
+ })
96
+ expect(res.status).toBe(400)
97
+ })
98
+
99
+ it('returns 401 when not authenticated', async () => {
100
+ mockGetSession.mockResolvedValueOnce(null)
101
+ const res = await fetch('/api/users', {
102
+ method: 'POST',
103
+ body: JSON.stringify({ userId: 'uuid' })
104
+ })
105
+ expect(res.status).toBe(401)
106
+ })
107
+
108
+ it('returns 200 with valid input and session', async () => {
109
+ mockGetSession.mockResolvedValueOnce(mockSession)
110
+ const res = await fetch('/api/users', {
111
+ method: 'POST',
112
+ body: JSON.stringify({ userId: 'uuid' })
113
+ })
114
+ expect(res.status).toBe(200)
115
+ })
116
+
117
+ it('returns 500 on unexpected server error', async () => {
118
+ mockGetSession.mockRejectedValueOnce(new Error('unexpected'))
119
+ const res = await fetch('/api/users', {
120
+ method: 'POST',
121
+ body: JSON.stringify({ userId: 'uuid' })
122
+ })
123
+ expect(res.status).toBe(500)
124
+ })
125
+ })
126
+ ```
127
+
128
+ ---
129
+
130
+ ## React Component Tests
131
+
132
+ ```typescript
133
+ // tests/components/UserCard.test.tsx
134
+ import { render, screen } from '@testing-library/react'
135
+ import UserCard from '@/components/UserCard'
136
+
137
+ describe('UserCard', () => {
138
+ it('renders user name when user exists', () => {
139
+ render(<UserCard user={mockUser} />)
140
+ expect(screen.getByText(mockUser.name)).toBeInTheDocument()
141
+ })
142
+
143
+ it('renders nothing when user is null', () => {
144
+ const { container } = render(<UserCard user={null} />)
145
+ expect(container.firstChild).toBeNull()
146
+ })
147
+
148
+ it('renders loading state when isLoading is true', () => {
149
+ render(<UserCard user={null} isLoading={true} />)
150
+ expect(screen.getByTestId('skeleton')).toBeInTheDocument()
151
+ })
152
+ })
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Utility Function Tests
158
+
159
+ ```typescript
160
+ // tests/unit/utils/validation.test.ts
161
+ describe('isValidId', () => {
162
+ it('returns true for valid uuid', () => {
163
+ expect(isValidId('550e8400-e29b-41d4-a716-446655440000')).toBe(true)
164
+ })
165
+
166
+ it('returns false for null', () => {
167
+ expect(isValidId(null)).toBe(false)
168
+ })
169
+
170
+ it('returns false for empty string', () => {
171
+ expect(isValidId('')).toBe(false)
172
+ })
173
+
174
+ it('returns false for whitespace only', () => {
175
+ expect(isValidId(' ')).toBe(false)
176
+ })
177
+
178
+ it('returns false for undefined', () => {
179
+ expect(isValidId(undefined)).toBe(false)
180
+ })
181
+ })
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Mock Templates
187
+
188
+ ### Mock user
189
+ ```typescript
190
+ export const mockUser = {
191
+ id: '550e8400-e29b-41d4-a716-446655440000',
192
+ email: 'test@example.com',
193
+ created_at: '2025-01-01T00:00:00Z'
194
+ }
195
+ ```
196
+
197
+ ### Mock session
198
+ ```typescript
199
+ export const mockSession = {
200
+ access_token: 'mock-token',
201
+ user: mockUser,
202
+ expires_at: Date.now() + 3600
203
+ }
204
+ ```
205
+
206
+ ### Reset mocks between tests
207
+ ```typescript
208
+ beforeEach(() => {
209
+ jest.clearAllMocks()
210
+ })
211
+ ```
212
+
213
+ ---
214
+
215
+ ## What NOT to Do
216
+ - Do not write tests that always pass regardless of logic
217
+ - Do not skip null and undefined test cases
218
+ - Do not forget to reset mocks between tests
219
+ - Do not test implementation details β€” test behaviour
220
+ - Do not mock everything β€” let utility functions run real logic
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
2
+ *.joblib filter=lfs diff=lfs merge=lfs -text
README.md ADDED
File without changes
claude.md ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claude.md β€” Global Instructions
2
+
3
+ ## Identity
4
+ You are a focused, phase-gated AI engineer on this project.
5
+ Read only what is needed. Act only on the current phase prompt.
6
+
7
+ ---
8
+
9
+ ## On Every Session Start
10
+ 1. Read `docs/index.md` to orient yourself
11
+ 2. Read `session/phase-log.md` to know current progress
12
+ 3. Read `session/context.md` for any carry-over from last session
13
+ 4. Load the current phase prompt from `prompts/phase-X.md`
14
+ 5. Do NOT read all docs at once β€” load on demand only
15
+
16
+ ---
17
+
18
+ ## Mandatory Rules (Non-Negotiable)
19
+
20
+ ### Phase Gating
21
+ - Work only on the current phase prompt
22
+ - Do not proceed to next phase without explicit user confirmation
23
+ - Confirm all tests pass before marking a phase done
24
+
25
+ ### Error Handling
26
+ - Follow `docs/debugging.md` for all error handling patterns
27
+ - Every error must log: file name, function, reason, fix hint
28
+ - When debugging: check the specific file first, nothing else
29
+
30
+ ### Testing
31
+ - Follow `docs/testing.md` for all test patterns
32
+ - Update test files after every feature, not at project end
33
+ - Provide CLI commands to run tests with every implementation
34
+
35
+ ### Supabase
36
+ - Follow `.claude/skills/supabase.md` for all DB operations
37
+ - Always provide CLI command first, dashboard steps if CLI not possible
38
+
39
+ ### Code Quality
40
+ - Handle all edge cases inline
41
+ - No infinite loops, no redundant API calls
42
+ - Fail fast, log clearly
43
+
44
+ ---
45
+
46
+ ## On Every Session End
47
+ 1. Give a one-liner git commit message for every change made
48
+ 2. Do NOT push to GitHub
49
+ 3. Update `session/summary.md` with what was done
50
+ 4. Append one line to `session/phase-log.md`
51
+ 5. Update `docs/progress.md` with completed items
52
+ 6. If something failed or a workaround was used β†’ add to `docs/learnings.md`
53
+
54
+ ---
55
+
56
+ ## Reference Map (Load on Demand)
57
+
58
+ | Need | File |
59
+ |------|------|
60
+ | What does each file do | `docs/index.md` |
61
+ | Requirements + phases | `docs/prd.md` |
62
+ | Current progress | `docs/progress.md` |
63
+ | Past failures + fixes | `docs/learnings.md` |
64
+ | System architecture | `docs/architecture.md` |
65
+ | Error handling patterns | `docs/debugging.md` |
66
+ | Test strategy + commands | `docs/testing.md` |
67
+ | Supabase operations | `.claude/skills/supabase.md` |
68
+ | Deploy steps | `docs/deployment.md` |
69
+ | Feature workflow | `docs/workflows/feature.md` |
70
+ | Bug fix workflow | `docs/workflows/bugfix.md` |
71
+ | Refactor workflow | `docs/workflows/refactor.md` |
72
+
73
+ ---
74
+
75
+ ## Never Do
76
+ - Do not read all `.md` files at session start
77
+ - Do not push to GitHub
78
+ - Do not skip writing tests for a feature
79
+ - Do not proceed to next phase without user confirmation
80
+ - Do not guess Supabase schema β€” always confirm before migrating
docs/architecture.md ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Architecture β€” Stack, Structure & Data Models
2
+
3
+ ## Purpose
4
+ Read this file when building or modifying any structural part
5
+ of the project. Update this file when architecture changes.
6
+ Do not guess the stack β€” confirm here first.
7
+
8
+ ---
9
+
10
+ ## Tech Stack
11
+ Frontend: [e.g. Next.js 14, React, TypeScript]
12
+ Styling: [e.g. Tailwind CSS, shadcn/ui]
13
+ Backend: [e.g. Next.js API Routes / Edge Functions]
14
+ Database: Supabase (PostgreSQL)
15
+ Auth: Supabase Auth
16
+ Storage: Supabase Storage
17
+ Deployment: [e.g. Vercel / Railway]
18
+ Testing: Jest, ts-jest, React Testing Library
19
+ Package Mgr: [e.g. npm / pnpm]
20
+
21
+ ---
22
+
23
+ ## Folder Structure
24
+ project-root/
25
+ β”œβ”€β”€ src/
26
+ β”‚ β”œβ”€β”€ app/ ← Next.js app router pages
27
+ β”‚ β”‚ β”œβ”€β”€ (auth)/ ← auth route group
28
+ β”‚ β”‚ β”œβ”€β”€ (dashboard)/ ← protected route group
29
+ β”‚ β”‚ └── api/ ← API routes
30
+ β”‚ β”œβ”€β”€ components/ ← reusable UI components
31
+ β”‚ β”‚ β”œβ”€β”€ ui/ ← base components (shadcn)
32
+ β”‚ β”‚ └── [feature]/ ← feature specific components
33
+ β”‚ β”œβ”€β”€ lib/ ← shared utilities and clients
34
+ β”‚ β”‚ β”œβ”€β”€ supabase.ts ← supabase client
35
+ β”‚ β”‚ β”œβ”€β”€ supabase-server.ts← server side supabase client
36
+ β”‚ β”‚ └── utils.ts ← shared utility functions
37
+ β”‚ β”œβ”€β”€ hooks/ ← custom React hooks
38
+ β”‚ β”œβ”€β”€ types/ ← TypeScript type definitions
39
+ β”‚ └── constants/ ← app wide constants
40
+ β”œβ”€β”€ supabase/
41
+ β”‚ └── migrations/ ← all DB migration files
42
+ β”œβ”€β”€ tests/ ← all test files
43
+ β”œβ”€β”€ docs/ ← project documentation
44
+ β”œβ”€β”€ scripts/ ← shell scripts
45
+ └── logs/ ← runtime and test logs
46
+
47
+ ---
48
+
49
+ ## Database Schema
50
+
51
+ ### Tables
52
+ [Update this section as tables are created]
53
+ users
54
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid()
55
+ email TEXT NOT NULL UNIQUE
56
+ created_at TIMESTAMPTZ DEFAULT NOW()
57
+ updated_at TIMESTAMPTZ DEFAULT NOW()
58
+
59
+ ### Relationships
60
+ [Document foreign keys and relationships here]
61
+ users.id ← referenced by [table].[column]
62
+
63
+ ### RLS Summary
64
+ [Document which tables have RLS enabled and policy types]
65
+ Table RLS Policies
66
+
67
+ users YES owner CRUD
68
+
69
+ ---
70
+
71
+ ## Auth Flow
72
+
73
+ User lands on /login
74
+ Supabase Auth handles email/OAuth
75
+ On success β†’ session stored in cookie
76
+ Protected routes check session via middleware
77
+ API routes validate session server side
78
+ On signout β†’ session cleared, redirect to /login
79
+
80
+
81
+ ---
82
+
83
+ ## API Routes
84
+ [Document API routes as they are created]
85
+ POST /api/auth/login ← handle login
86
+ POST /api/auth/logout ← handle logout
87
+ GET /api/user ← get current user
88
+
89
+ ---
90
+
91
+ ## Environment Variables
92
+ NEXT_PUBLIC_SUPABASE_URL ← supabase project URL
93
+ NEXT_PUBLIC_SUPABASE_ANON_KEY ← supabase anon key
94
+ SUPABASE_SERVICE_ROLE_KEY ← server only, never expose
95
+
96
+ ---
97
+
98
+ ## Key Architectural Decisions
99
+ [Document WHY decisions were made as project grows]
100
+ [YYYY-MM-DD] β€” Used app router over pages router for better
101
+ server component support
102
+ [YYYY-MM-DD] β€” Used Supabase RLS over API-level auth checks
103
+ for defence in depth
104
+
105
+ ---
106
+
107
+ ## Constraints
108
+ [Document hard technical constraints]
109
+
110
+ No direct DB access from client side components
111
+ Service role key only used in server side code
112
+ All DB changes must go through migration files
113
+ RLS must be enabled on every table
114
+
115
+
116
+ ---
117
+
118
+ ## Rules for This File
119
+ - Update when a new table is added
120
+ - Update when a new API route is created
121
+ - Update when a key architectural decision is made
122
+ - Keep schema section in sync with actual migrations
123
+ - Do not document implementation details β€” only structure
docs/debugging.md ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Debugging β€” Error Handling, Logging & Debug Strategy
2
+
3
+ ## Purpose
4
+ Read this file when an error occurs or when implementing error
5
+ handling for any feature. Do not guess β€” follow this exactly.
6
+
7
+ ---
8
+
9
+ ## Core Principle
10
+ **Check the specific file where the error occurred. Nothing else.**
11
+ Do not scan the entire codebase. Logs must tell you:
12
+ - WHERE it broke (file + function)
13
+ - WHY it broke (reason)
14
+ - HOW to fix it (hint)
15
+
16
+ ---
17
+
18
+ ## Standard Error Log Format
19
+
20
+ Every error logged must follow this exact structure:
21
+
22
+ [ERROR] [timestamp] [file:function] β€” reason β€” fix hint
23
+
24
+ **Example:**
25
+ [ERROR] [2025-01-15T10:32:00Z] [lib/supabase.ts:fetchUser] β€” user_id is null β€” check auth session before calling fetchUser
26
+
27
+ ---
28
+
29
+ ## Error Handler Template
30
+
31
+ ### TypeScript / Next.js
32
+ ```typescript
33
+ function handleError(error: unknown, file: string, fn: string): void {
34
+ const timestamp = new Date().toISOString()
35
+ const reason = error instanceof Error ? error.message : String(error)
36
+ const hint = getFixHint(reason)
37
+
38
+ console.error(`[ERROR] [${timestamp}] [${file}:${fn}] β€” ${reason} β€” ${hint}`)
39
+
40
+ // Write to logs/errors.log in dev
41
+ if (process.env.NODE_ENV === 'development') {
42
+ appendToLog('logs/errors.log', `[ERROR] [${timestamp}] [${file}:${fn}] β€” ${reason} β€” ${hint}`)
43
+ }
44
+ }
45
+
46
+ function getFixHint(reason: string): string {
47
+ if (reason.includes('null')) return 'check for null before using this value'
48
+ if (reason.includes('undefined')) return 'confirm the value exists before accessing'
49
+ if (reason.includes('network')) return 'check network connection and API endpoint'
50
+ if (reason.includes('permission')) return 'verify Supabase RLS policies'
51
+ if (reason.includes('timeout')) return 'increase timeout or check slow query'
52
+ return 'check function inputs and dependencies'
53
+ }
54
+ ```
55
+
56
+ ### Usage in any function
57
+ ```typescript
58
+ // file: lib/supabase.ts
59
+ async function fetchUser(userId: string) {
60
+ try {
61
+ if (!userId) throw new Error('user_id is null')
62
+ const { data, error } = await supabase.from('users').select('*').eq('id', userId)
63
+ if (error) throw error
64
+ return data
65
+ } catch (err) {
66
+ handleError(err, 'lib/supabase.ts', 'fetchUser')
67
+ return null
68
+ }
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Logging Levels
75
+
76
+ | Level | When to Use |
77
+ |-------|------------|
78
+ | `[ERROR]` | Something broke, feature cannot continue |
79
+ | `[WARN]` | Something unexpected but recoverable |
80
+ | `[INFO]` | Key state changes, successful operations |
81
+ | `[DEBUG]` | Verbose, dev-only, remove before production |
82
+
83
+ ---
84
+
85
+ ## Debug Steps β€” When Something Breaks
86
+
87
+ 1. Read `logs/errors.log` β€” find the exact `[file:function]`
88
+ 2. Open only that file
89
+ 3. Check the function mentioned in the log
90
+ 4. Verify inputs to that function
91
+ 5. Check for null/undefined before the failure point
92
+ 6. Fix inline, do not refactor adjacent code
93
+ 7. Re-run the specific test for that function only
94
+
95
+ **Do not open other files unless the log explicitly points to them.**
96
+
97
+ ---
98
+
99
+ ## Supabase Specific Errors
100
+
101
+ | Error | Likely Cause | Fix |
102
+ |-------|-------------|-----|
103
+ | `permission denied` | RLS policy blocking query | Check policy in Supabase dashboard β†’ Auth β†’ Policies |
104
+ | `relation does not exist` | Migration not applied | Run `supabase db push` |
105
+ | `violates foreign key` | Referenced row missing | Insert parent record first |
106
+ | `JWT expired` | Auth token stale | Refresh session with `supabase.auth.refreshSession()` |
107
+ | `null value in column` | Missing required field | Validate inputs before insert |
108
+
109
+ ---
110
+
111
+ ## Edge Case Checklist
112
+ Before shipping any function, verify:
113
+ - [ ] What happens if input is null or undefined?
114
+ - [ ] What happens if the DB returns empty array?
115
+ - [ ] What happens if the API call times out?
116
+ - [ ] What happens if the user is not authenticated?
117
+ - [ ] What happens if this runs twice simultaneously?
118
+
119
+ ---
120
+
121
+ ## What NOT to Do
122
+ - Do not use `console.log` for errors β€” use `console.error` with the format above
123
+ - Do not catch an error and do nothing with it
124
+ - Do not log sensitive data (passwords, tokens, PII)
125
+ - Do not open unrelated files to debug an error
126
+ - Do not refactor while debugging β€” fix first, refactor later
docs/deployment.md ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment β€” Supabase CLI & Dashboard Instructions
2
+
3
+ ## Purpose
4
+ Read this file when deploying any change to Supabase or your
5
+ environment. Always try CLI first. Use dashboard only if CLI
6
+ is not possible β€” dashboard steps will be clearly marked.
7
+
8
+ ---
9
+
10
+ ## Core Principle
11
+ **CLI first. Dashboard only when CLI cannot do it.**
12
+ Every deployment step must be logged and reversible.
13
+
14
+ ---
15
+
16
+ ## Prerequisites
17
+ ```bash
18
+ # Install Supabase CLI
19
+ npm install -g supabase
20
+
21
+ # Login
22
+ supabase login
23
+
24
+ # Link to your project (run once per project)
25
+ supabase link --project-ref YOUR_PROJECT_REF
26
+
27
+ # Confirm link
28
+ supabase status
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Environment Variables
34
+ ```bash
35
+ # .env.local (never commit this file)
36
+ NEXT_PUBLIC_SUPABASE_URL=your_project_url
37
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
38
+ SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
39
+ ```
40
+
41
+ **Where to find these:**
42
+ Dashboard β†’ Project Settings β†’ API β†’ Project URL + Keys
43
+
44
+ ---
45
+
46
+ ## Database Migrations
47
+
48
+ ### Create a new migration
49
+ ```bash
50
+ supabase migration new migration_name
51
+ # Creates: supabase/migrations/[timestamp]_migration_name.sql
52
+ # Write your SQL inside this file, then push
53
+ ```
54
+
55
+ ### Push migration to remote
56
+ ```bash
57
+ supabase db push
58
+ ```
59
+
60
+ ### Check migration status
61
+ ```bash
62
+ supabase migration list
63
+ ```
64
+
65
+ ### Reset local DB (dev only)
66
+ ```bash
67
+ supabase db reset
68
+ ```
69
+
70
+ ### Pull remote schema to local
71
+ ```bash
72
+ supabase db pull
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Local Development
78
+
79
+ ### Start local Supabase
80
+ ```bash
81
+ supabase start
82
+ # Gives you local URL, anon key, service role key
83
+ ```
84
+
85
+ ### Stop local Supabase
86
+ ```bash
87
+ supabase stop
88
+ ```
89
+
90
+ ### View local DB in browser
91
+ ```bash
92
+ supabase studio
93
+ # Opens Supabase Studio at localhost:54323
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Edge Functions
99
+
100
+ ### Create a new edge function
101
+ ```bash
102
+ supabase functions new function-name
103
+ ```
104
+
105
+ ### Serve locally
106
+ ```bash
107
+ supabase functions serve function-name --env-file .env.local
108
+ ```
109
+
110
+ ### Deploy edge function
111
+ ```bash
112
+ supabase functions deploy function-name
113
+ ```
114
+
115
+ ### Set environment secret for edge function
116
+ ```bash
117
+ supabase secrets set KEY=value
118
+ ```
119
+
120
+ ### List secrets
121
+ ```bash
122
+ supabase secrets list
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Storage
128
+
129
+ ### CLI β€” not fully supported for bucket creation
130
+ **β†’ Use Dashboard for bucket creation**
131
+
132
+ #### Dashboard Steps β€” Create Storage Bucket
133
+
134
+ Go to Supabase Dashboard β†’ Storage
135
+ Click "New bucket"
136
+ Enter bucket name
137
+ Toggle public/private
138
+ Click "Create bucket"
139
+
140
+ ### Upload file via CLI
141
+ ```bash
142
+ supabase storage cp ./local-file.png ss:///bucket-name/path/file.png
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Row Level Security (RLS)
148
+
149
+ ### CLI β€” enable RLS on a table
150
+ ```sql
151
+ -- Inside a migration file
152
+ ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
153
+ ```
154
+
155
+ ### CLI β€” create a policy via migration
156
+ ```sql
157
+ CREATE POLICY "Users can view own data"
158
+ ON your_table
159
+ FOR SELECT
160
+ USING (auth.uid() = user_id);
161
+ ```
162
+
163
+ ### Dashboard Steps β€” RLS Policy (if migration not possible)
164
+
165
+ Go to Supabase Dashboard β†’ Authentication β†’ Policies
166
+ Select your table
167
+ Click "New Policy"
168
+ Choose template or write custom
169
+ Click "Review" then "Save Policy"
170
+
171
+ ---
172
+
173
+ ## Auth Configuration
174
+
175
+ ### CLI β€” not supported for OAuth provider setup
176
+ **β†’ Use Dashboard for OAuth setup**
177
+
178
+ #### Dashboard Steps β€” Enable OAuth Provider
179
+
180
+ Go to Dashboard β†’ Authentication β†’ Providers
181
+ Select provider (Google, GitHub, etc.)
182
+ Enter Client ID and Secret
183
+ Copy callback URL β†’ paste into provider's OAuth app
184
+ Click "Save"
185
+
186
+ ### Set auth email templates
187
+
188
+ Dashboard β†’ Authentication β†’ Email Templates
189
+ β†’ Edit confirm signup / reset password templates
190
+
191
+ ---
192
+
193
+ ## scripts/deploy.sh
194
+ ```bash
195
+ #!/bin/bash
196
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting deployment..." >> logs/errors.log
197
+
198
+ # Run tests first β€” abort if they fail
199
+ bash scripts/test.sh
200
+ if [ $? -ne 0 ]; then
201
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Deployment aborted β€” tests failed" >> logs/errors.log
202
+ exit 1
203
+ fi
204
+
205
+ # Push DB migrations
206
+ echo "Pushing DB migrations..."
207
+ supabase db push
208
+ if [ $? -ne 0 ]; then
209
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Migration failed β€” check supabase logs" >> logs/errors.log
210
+ exit 1
211
+ fi
212
+
213
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Deployment complete" >> logs/errors.log
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Rollback
219
+
220
+ ### Rollback last migration
221
+ ```bash
222
+ # Supabase does not have auto-rollback
223
+ # Write a reverse migration manually
224
+
225
+ supabase migration new rollback_migration_name
226
+ # Write reverse SQL (DROP TABLE, ALTER, etc.)
227
+ supabase db push
228
+ ```
229
+
230
+ ### Dashboard Steps β€” Rollback (if CLI fails)
231
+
232
+ Go to Dashboard β†’ Database β†’ Migrations
233
+ Identify the migration to reverse
234
+ Go to Dashboard β†’ SQL Editor
235
+ Write and run reverse SQL manually
236
+
237
+ ---
238
+
239
+ ## Deployment Checklist
240
+ - [ ] All tests pass (`bash scripts/test.sh`)
241
+ - [ ] `.env.local` is not committed
242
+ - [ ] Migration file created for every schema change
243
+ - [ ] RLS enabled on every new table
244
+ - [ ] Edge functions tested locally before deploy
245
+ - [ ] `supabase db push` confirms no errors
246
+ - [ ] One-liner git commit message written
247
+
248
+ ---
249
+
250
+ ## What NOT to Do
251
+ - Do not edit schema directly in Dashboard without a migration file
252
+ - Do not push migrations without running tests first
253
+ - Do not commit `.env.local` or any file with keys
254
+ - Do not disable RLS on any table in production
255
+ - Do not deploy edge functions without local testing first
docs/index.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Index β€” What Every File Does & When to Read It
2
+
3
+ ## Purpose
4
+ This is your orientation file. Read this once at session start.
5
+ It tells you what exists, what it does, and when to load it.
6
+
7
+ ---
8
+
9
+ ## Core Files
10
+
11
+ | File | Purpose | When to Read |
12
+ |------|---------|--------------|
13
+ | `claude.md` | Global rules, session start/end checklist | Every session start |
14
+ | `docs/index.md` | This file β€” master map | Every session start |
15
+ | `session/phase-log.md` | One-liner per phase, current status | Every session start |
16
+ | `session/context.md` | Carry-over notes from last session | Every session start |
17
+ | `session/summary.md` | What was done in last session | Only if context is unclear |
18
+
19
+ ---
20
+
21
+ ## Project State Files
22
+
23
+ | File | Purpose | When to Read |
24
+ |------|---------|--------------|
25
+ | `docs/prd.md` | Full PRD, all 8 phases, requirements | Phase start or on demand |
26
+ | `docs/progress.md` | One-liner per feature, pass/fail status | When checking what's done |
27
+ | `docs/learnings.md` | Past failures, workarounds, gotchas | When hitting a repeated error |
28
+ | `docs/architecture.md` | Stack, data models, folder structure | When building or modifying structure |
29
+
30
+ ---
31
+
32
+ ## How-To Reference Files
33
+
34
+ | File | Purpose | When to Read |
35
+ |------|---------|--------------|
36
+ | `docs/debugging.md` | Error format, log structure, debug steps | When an error occurs |
37
+ | `docs/testing.md` | Test strategy, CLI commands, log format | When writing or running tests |
38
+ | `docs/deployment.md` | Supabase CLI + dashboard deploy steps | When deploying any change |
39
+ | `.claude/skills/supabase.md` | Supabase patterns, queries, migrations | When writing DB code |
40
+ | `.claude/skills/debugging.md` | Reusable debug snippets by error type | When stuck on a specific error |
41
+ | `.claude/skills/testing.md` | Reusable test patterns by feature type | When writing tests |
42
+
43
+ ---
44
+
45
+ ## Workflow Files
46
+
47
+ | File | Purpose | When to Read |
48
+ |------|---------|--------------|
49
+ | `docs/workflows/feature.md` | Step-by-step for building a new feature | Every new feature |
50
+ | `docs/workflows/bugfix.md` | Step-by-step for fixing a bug | Every bug fix |
51
+ | `docs/workflows/refactor.md` | Step-by-step for refactoring code | Every refactor |
52
+
53
+ ---
54
+
55
+ ## Phase Prompts
56
+
57
+ | File | Purpose | When to Read |
58
+ |------|---------|--------------|
59
+ | `prompts/phase-1.md` through `phase-8.md` | Individual phase instructions | Only the current phase |
60
+
61
+ ---
62
+
63
+ ## Scripts & Logs
64
+
65
+ | File | Purpose | When to Read |
66
+ |------|---------|--------------|
67
+ | `scripts/test.sh` | Run all tests with logging | When running tests |
68
+ | `scripts/dev.sh` | Start dev environment | When starting dev |
69
+ | `scripts/deploy.sh` | Deploy to Supabase + environment | When deploying |
70
+ | `logs/errors.log` | Runtime error output | When debugging a failure |
71
+ | `logs/test.log` | Test run output | When a test fails |
72
+
73
+ ---
74
+
75
+ ## Rules for This File
76
+ - Do not add implementation details here
77
+ - Do not add code here
78
+ - Only update this file if a new reference file is added to the project
docs/learnings.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Learnings β€” Failures, Workarounds & Time Savers
2
+
3
+ ## Purpose
4
+ Read this file when hitting a repeated error or unexpected behaviour.
5
+ Each bullet is a one-liner under 15 words.
6
+ No explanations. Only things that save time in future sessions.
7
+
8
+ ---
9
+
10
+ ## Rules for This File
11
+ - One line per learning, under 15 words
12
+ - Add immediately when a failure or workaround is found
13
+ - Never delete a line β€” they compound over time
14
+ - Group by category as list grows
15
+
16
+ ---
17
+
18
+ ## Supabase
19
+ - [ ] Add learnings here as they are discovered
20
+
21
+ ## Auth
22
+ - [ ] Add learnings here as they are discovered
23
+
24
+ ## Testing
25
+ - [ ] Add learnings here as they are discovered
26
+
27
+ ## Next.js
28
+ - [ ] Add learnings here as they are discovered
29
+
30
+ ## Deployment
31
+ - [ ] Add learnings here as they are discovered
32
+
33
+ ## General
34
+ - [ ] Add learnings here as they are discovered
35
+
36
+ ---
37
+
38
+ ## Example Format (remove when real entries exist)
39
+ ```
40
+ ## Supabase
41
+ - RLS blocks all queries by default β€” always add select policy first
42
+ - supabase db push fails silently if migration has syntax error
43
+ - Foreign key inserts require parent row to exist first
44
+
45
+ ## Auth
46
+ - JWT expires after 1hr β€” always call refreshSession before DB ops
47
+ - getSession returns null on hard refresh β€” use onAuthStateChange instead
48
+
49
+ ## Testing
50
+ - Jest mock must be reset between tests or state bleeds across cases
51
+ - Supabase client must be mocked at module level not inside test
52
+
53
+ ## Next.js
54
+ - useEffect runs twice in strict mode β€” guard with cleanup function
55
+ - API routes do not have access to cookies without explicit parsing
56
+ ```
docs/prd.md ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PRD β€” Product Requirements Document
2
+
3
+ ## Purpose
4
+ Read this file at phase start to understand requirements.
5
+ Contains full product vision, all phases and acceptance criteria.
6
+ Do not implement anything not listed here without user confirmation.
7
+
8
+ ---
9
+
10
+ ## Product Overview
11
+ Product Name: [name]
12
+ Description: [one paragraph max]
13
+ Primary User: [who is this for]
14
+ Core Problem: [what problem does this solve]
15
+
16
+ ---
17
+
18
+ ## Success Metrics
19
+
20
+ [metric one β€” measurable]
21
+ [metric two β€” measurable]
22
+ [metric three β€” measurable]
23
+
24
+
25
+ ---
26
+
27
+ ## Technical Constraints
28
+
29
+ [constraint one]
30
+ [constraint two]
31
+ [constraint three]
32
+
33
+
34
+ ---
35
+
36
+ ## Phase Overview
37
+ Phase 1 β€” [name] β€” [one liner]
38
+ Phase 2 β€” [name] β€” [one liner]
39
+ Phase 3 β€” [name] β€” [one liner]
40
+ Phase 4 β€” [name] β€” [one liner]
41
+ Phase 5 β€” [name] β€” [one liner]
42
+ Phase 6 β€” [name] β€” [one liner]
43
+ Phase 7 β€” [name] β€” [one liner]
44
+ Phase 8 β€” [name] β€” [one liner]
45
+
46
+ ---
47
+
48
+ ## Phase 1 β€” [Phase Name]
49
+
50
+ ### Goal
51
+ [One paragraph β€” what this phase achieves]
52
+
53
+ ### Features
54
+
55
+ [feature name] β€” [one liner description]
56
+ [feature name] β€” [one liner description]
57
+ [feature name] β€” [one liner description]
58
+
59
+
60
+ ### Acceptance Criteria
61
+
62
+ [specific testable criterion]
63
+ [specific testable criterion]
64
+ [specific testable criterion]
65
+
66
+
67
+ ### Out of Scope for This Phase
68
+
69
+ [what is explicitly NOT included]
70
+ [what is explicitly NOT included]
71
+
72
+
73
+ ---
74
+
75
+ ## Phase 2 β€” [Phase Name]
76
+
77
+ ### Goal
78
+ [One paragraph]
79
+
80
+ ### Features
81
+
82
+ [feature name] β€” [one liner]
83
+ [feature name] β€” [one liner]
84
+
85
+
86
+ ### Acceptance Criteria
87
+
88
+ [criterion]
89
+ [criterion]
90
+
91
+
92
+ ### Out of Scope for This Phase
93
+
94
+ [excluded item]
95
+
96
+
97
+ ---
98
+
99
+ ## Phase 3 β€” [Phase Name]
100
+
101
+ ### Goal
102
+ [One paragraph]
103
+
104
+ ### Features
105
+
106
+ [feature name] β€” [one liner]
107
+
108
+
109
+ ### Acceptance Criteria
110
+
111
+ [criterion]
112
+
113
+
114
+ ### Out of Scope for This Phase
115
+
116
+ [excluded item]
117
+
118
+
119
+ ---
120
+
121
+ ## Phase 4 β€” [Phase Name]
122
+
123
+ ### Goal
124
+ [One paragraph]
125
+
126
+ ### Features
127
+
128
+ [feature name] β€” [one liner]
129
+
130
+
131
+ ### Acceptance Criteria
132
+
133
+ [criterion]
134
+
135
+
136
+ ### Out of Scope for This Phase
137
+
138
+ [excluded item]
139
+
140
+
141
+ ---
142
+
143
+ ## Phase 5 β€” [Phase Name]
144
+
145
+ ### Goal
146
+ [One paragraph]
147
+
148
+ ### Features
149
+
150
+ [feature name] β€” [one liner]
151
+
152
+
153
+ ### Acceptance Criteria
154
+
155
+ [criterion]
156
+
157
+
158
+ ### Out of Scope for This Phase
159
+
160
+ [excluded item]
161
+
162
+
163
+ ---
164
+
165
+ ## Phase 6 β€” [Phase Name]
166
+
167
+ ### Goal
168
+ [One paragraph]
169
+
170
+ ### Features
171
+
172
+ [feature name] β€” [one liner]
173
+
174
+
175
+ ### Acceptance Criteria
176
+
177
+ [criterion]
178
+
179
+
180
+ ### Out of Scope for This Phase
181
+
182
+ [excluded item]
183
+
184
+
185
+ ---
186
+
187
+ ## Phase 7 β€” [Phase Name]
188
+
189
+ ### Goal
190
+ [One paragraph]
191
+
192
+ ### Features
193
+
194
+ [feature name] β€” [one liner]
195
+
196
+
197
+ ### Acceptance Criteria
198
+
199
+ [criterion]
200
+
201
+
202
+ ### Out of Scope for This Phase
203
+
204
+ [excluded item]
205
+
206
+
207
+ ---
208
+
209
+ ## Phase 8 β€” [Phase Name]
210
+
211
+ ### Goal
212
+ [One paragraph]
213
+
214
+ ### Features
215
+
216
+ [feature name] β€” [one liner]
217
+
218
+
219
+ ### Acceptance Criteria
220
+
221
+ [criterion]
222
+
223
+
224
+ ### Out of Scope for This Phase
225
+
226
+ [excluded item]
227
+
228
+
229
+ ---
230
+
231
+ ## Phase Gate Rules
232
+ - Do not start a phase without user confirmation
233
+ - Do not proceed to next phase if current tests are failing
234
+ - Do not implement features outside current phase scope
235
+ - Mark acceptance criteria as complete before closing phase
236
+ - All criteria must be checked before phase is marked COMPLETE
237
+
238
+ ---
239
+
240
+ ## Rules for This File
241
+ - Fill in placeholders before starting Phase 1
242
+ - Do not modify acceptance criteria mid phase
243
+ - Do not add features to a phase that is in progress
244
+ - New features go into the next available phase or a new phase
docs/progress.md ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Progress β€” Feature & Phase Status
2
+
3
+ ## Purpose
4
+ Read this file to know exactly what is done, in progress, or pending.
5
+ One liner per feature. Update after every feature implementation.
6
+ Do not read entire codebase to understand progress β€” read this file.
7
+
8
+ ---
9
+
10
+ ## Status Legend
11
+
12
+ βœ… done β€” implemented, tested, committed
13
+ πŸ”„ in progress β€” currently being worked on
14
+ ⏳ pending β€” not started yet
15
+ ❌ blocked β€” cannot proceed, reason noted
16
+ πŸ› bug β€” implemented but has known failing test
17
+
18
+ ---
19
+
20
+ ## Phase 1 β€” [Phase Name]
21
+ ⏳ [feature name] β€” [one line description]
22
+ ⏳ [feature name] β€” [one line description]
23
+ ⏳ [feature name] β€” [one line description]
24
+
25
+ ## Phase 2 β€” [Phase Name]
26
+ ⏳ [feature name] β€” [one line description]
27
+ ⏳ [feature name] β€” [one line description]
28
+
29
+ ## Phase 3 β€” [Phase Name]
30
+ ⏳ [feature name] β€” [one line description]
31
+
32
+ ## Phase 4 β€” [Phase Name]
33
+ ⏳ [feature name] β€” [one line description]
34
+
35
+ ## Phase 5 β€” [Phase Name]
36
+ ⏳ [feature name] β€” [one line description]
37
+
38
+ ## Phase 6 β€” [Phase Name]
39
+ ⏳ [feature name] β€” [one line description]
40
+
41
+ ## Phase 7 β€” [Phase Name]
42
+ ⏳ [feature name] β€” [one line description]
43
+
44
+ ## Phase 8 β€” [Phase Name]
45
+ ⏳ [feature name] β€” [one line description]
46
+
47
+ ---
48
+
49
+ ## Blocked Items
50
+ ❌ [feature name] β€” blocked by: [reason]
51
+
52
+ ---
53
+
54
+ ## Rules for This File
55
+ - One line per feature, no paragraphs
56
+ - Update status after every feature, not at end of phase
57
+ - Never delete a line β€” only update its status
58
+ - If blocked, note the reason inline
docs/testing.md ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Testing β€” Strategy, Patterns & CLI Commands
2
+
3
+ ## Purpose
4
+ Read this file when writing tests or running them.
5
+ Follow this exactly for every feature implementation.
6
+
7
+ ---
8
+
9
+ ## Core Principle
10
+ **Write tests after every feature. Never at the end of the project.**
11
+ Every test failure must tell you:
12
+ - WHICH test failed (test name + file)
13
+ - WHY it failed (expected vs received)
14
+ - WHERE it failed (line number)
15
+
16
+ ---
17
+
18
+ ## Testing Stack
19
+ - **Unit + Integration:** Jest + ts-jest
20
+ - **API Routes:** Supertest
21
+ - **UI Components:** React Testing Library
22
+ - **DB (Supabase):** Mocked with jest.mock or Supabase local instance
23
+
24
+ ---
25
+
26
+ ## Folder Structure
27
+
28
+ tests/
29
+ β”œβ”€β”€ unit/
30
+ β”‚ β”œβ”€β”€ lib/
31
+ β”‚ └── utils/
32
+ β”œβ”€β”€ integration/
33
+ β”‚ β”œβ”€β”€ api/
34
+ β”‚ └── db/
35
+ β”œβ”€β”€ components/
36
+ └── setup.ts
37
+
38
+ ---
39
+
40
+ ## Test File Naming Convention
41
+ [feature-name].test.ts ← unit test
42
+ [feature-name].integration.test.ts ← integration test
43
+ [component-name].test.tsx ← component test
44
+
45
+ ---
46
+
47
+ ## Standard Test Template
48
+
49
+ ```typescript
50
+ // tests/unit/lib/fetchUser.test.ts
51
+ import { fetchUser } from '@/lib/supabase'
52
+
53
+ describe('fetchUser', () => {
54
+ it('returns user when valid userId is provided', async () => {
55
+ const result = await fetchUser('valid-uuid')
56
+ expect(result).not.toBeNull()
57
+ expect(result).toHaveProperty('id')
58
+ })
59
+
60
+ it('returns null when userId is null', async () => {
61
+ const result = await fetchUser(null as any)
62
+ expect(result).toBeNull()
63
+ })
64
+
65
+ it('returns null when userId is empty string', async () => {
66
+ const result = await fetchUser('')
67
+ expect(result).toBeNull()
68
+ })
69
+
70
+ it('handles DB error gracefully', async () => {
71
+ // mock supabase to throw
72
+ jest.spyOn(supabase, 'from').mockImplementationOnce(() => {
73
+ throw new Error('DB connection failed')
74
+ })
75
+ const result = await fetchUser('valid-uuid')
76
+ expect(result).toBeNull()
77
+ })
78
+ })
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Edge Cases to Test for Every Feature
84
+
85
+ | Scenario | What to Test |
86
+ |----------|-------------|
87
+ | Empty input | null, undefined, empty string |
88
+ | Auth state | unauthenticated user, expired token |
89
+ | DB response | empty array, null, malformed data |
90
+ | Network | timeout, connection failure |
91
+ | Duplicates | calling same function twice simultaneously |
92
+ | Boundary | max length strings, zero values, negative numbers |
93
+
94
+ ---
95
+
96
+ ## CLI Commands
97
+
98
+ ### Run all tests
99
+ ```bash
100
+ npm test 2>&1 | tee logs/test.log
101
+ ```
102
+
103
+ ### Run a specific test file
104
+ ```bash
105
+ npm test -- tests/unit/lib/fetchUser.test.ts 2>&1 | tee logs/test.log
106
+ ```
107
+
108
+ ### Run tests in watch mode
109
+ ```bash
110
+ npm test -- --watch
111
+ ```
112
+
113
+ ### Run tests with coverage
114
+ ```bash
115
+ npm test -- --coverage 2>&1 | tee logs/test.log
116
+ ```
117
+
118
+ ### Run only failed tests
119
+ ```bash
120
+ npm test -- --onlyFailures 2>&1 | tee logs/test.log
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Test Log Format
126
+ All test output pipes to `logs/test.log`.
127
+ When a test fails, the log will show:
128
+
129
+ FAIL tests/unit/lib/fetchUser.test.ts
130
+ ● fetchUser β€Ί returns null when userId is null
131
+
132
+ expect(received).toBeNull()
133
+
134
+ Received: { id: 'abc', name: 'test' }
135
+
136
+ 14 | it('returns null when userId is null', async () => {
137
+ 15 | const result = await fetchUser(null as any)
138
+ > 16 | expect(result).toBeNull()
139
+ | ^
140
+ 17 | })
141
+
142
+ at Object.<anonymous> (tests/unit/lib/fetchUser.test.ts:16:20)
143
+
144
+ ---
145
+
146
+ ## scripts/test.sh
147
+ ```bash
148
+ #!/bin/bash
149
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting test run..." >> logs/test.log
150
+ npm test 2>&1 | tee -a logs/test.log
151
+ EXIT_CODE=${PIPESTATUS[0]}
152
+ if [ $EXIT_CODE -ne 0 ]; then
153
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] TESTS FAILED β€” see above for details" >> logs/test.log
154
+ else
155
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ALL TESTS PASSED" >> logs/test.log
156
+ fi
157
+ exit $EXIT_CODE
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Test Update Rules
163
+ - [ ] Write tests immediately after each feature is implemented
164
+ - [ ] Never delete existing tests β€” only add or update
165
+ - [ ] If a test is skipped, add a comment explaining why
166
+ - [ ] All tests must pass before marking a phase complete
167
+ - [ ] Run full test suite before every deployment
168
+
169
+ ---
170
+
171
+ ## Mocking Supabase
172
+ ```typescript
173
+ // tests/setup.ts
174
+ jest.mock('@/lib/supabase', () => ({
175
+ supabase: {
176
+ from: jest.fn().mockReturnValue({
177
+ select: jest.fn().mockReturnValue({
178
+ eq: jest.fn().mockResolvedValue({ data: [], error: null })
179
+ }),
180
+ insert: jest.fn().mockResolvedValue({ data: null, error: null }),
181
+ update: jest.fn().mockResolvedValue({ data: null, error: null }),
182
+ delete: jest.fn().mockResolvedValue({ data: null, error: null })
183
+ }),
184
+ auth: {
185
+ getSession: jest.fn().mockResolvedValue({ data: { session: null }, error: null }),
186
+ refreshSession: jest.fn().mockResolvedValue({ data: null, error: null })
187
+ }
188
+ }
189
+ }))
190
+ ```
191
+
192
+ ---
193
+
194
+ ## What NOT to Do
195
+ - Do not write tests after the entire project is done
196
+ - Do not mock everything β€” integration tests must hit real logic
197
+ - Do not skip edge case tests to save time
198
+ - Do not ignore a failing test β€” fix it before moving on
199
+ - Do not write tests that always pass regardless of logic
docs/workflows/bugfix.md ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Workflow β€” Fixing a Bug
2
+
3
+ ## Purpose
4
+ Read this file every time you need to fix a bug.
5
+ Follow steps in order. Do not open files not mentioned in the log.
6
+
7
+ ---
8
+
9
+ ## Core Principle
10
+ **The log tells you where to look. Trust the log. Check one file.**
11
+ Do not scan the codebase. Do not refactor while fixing.
12
+
13
+ ---
14
+
15
+ ## Step 1 β€” Read the Error First
16
+ Before touching any code:
17
+ - [ ] Read `logs/errors.log` β€” find the exact error entry
18
+ - [ ] Identify: file, function, reason, fix hint from the log
19
+ - [ ] Read `docs/learnings.md` β€” has this failed before?
20
+ - [ ] If yes β†’ apply the known fix, skip to Step 5
21
+
22
+ Error log format to look for:
23
+
24
+ [ERROR] [timestamp] [file:function] β€” reason β€” fix hint
25
+
26
+ ---
27
+
28
+ ## Step 2 β€” Isolate Before Fixing
29
+ State this out loud before touching code:
30
+ Error in: [file:function]
31
+ Reason: [from log]
32
+ Fix hint: [from log]
33
+ Only file I will open: [file]
34
+
35
+ - [ ] Open only the file named in the log
36
+ - [ ] Find only the function named in the log
37
+ - [ ] Do not open any other file unless the log explicitly points there
38
+
39
+ ---
40
+
41
+ ## Step 3 β€” Understand the Failure
42
+ Inside the identified function:
43
+ - [ ] Check what input is coming in
44
+ - [ ] Check where exactly it breaks (line from log)
45
+ - [ ] Check if it is a null/undefined issue
46
+ - [ ] Check if it is an auth/session issue
47
+ - [ ] Check if it is a Supabase RLS issue
48
+ - [ ] Reference `docs/debugging.md` β†’ Supabase Specific Errors table
49
+
50
+ ---
51
+
52
+ ## Step 4 β€” Write the Fix
53
+ - [ ] Fix only the broken logic
54
+ - [ ] Do not rename variables or restructure the function
55
+ - [ ] Do not fix adjacent code that looks messy
56
+ - [ ] Add or improve error handling inline if it was missing
57
+ - [ ] Confirm fix hint from log is addressed
58
+
59
+ ---
60
+
61
+ ## Step 5 β€” Write or Update the Test
62
+ - [ ] Find the existing test file for this feature
63
+ - [ ] If the failing case was not covered β†’ add a test for it now
64
+ - [ ] The test must reproduce the exact failure condition
65
+ - [ ] Run only the test for this file first:
66
+ ```bash
67
+ npm test -- tests/unit/[feature-name].test.ts 2>&1 | tee logs/test.log
68
+ ```
69
+ - [ ] Confirm it passes before running full suite
70
+
71
+ ---
72
+
73
+ ## Step 6 β€” Run Full Test Suite
74
+ ```bash
75
+ bash scripts/test.sh
76
+ ```
77
+ - [ ] All tests pass
78
+ - [ ] No previously passing test is now failing
79
+ - [ ] If a new failure appears β†’ treat it as a new bug, do not chain fixes
80
+
81
+ ---
82
+
83
+ ## Step 7 β€” Update Docs
84
+ - [ ] Add one-liner to `docs/progress.md` under the feature
85
+ - [ ] If this was a repeated failure or needed a workaround β†’ add to `docs/learnings.md`
86
+ - [ ] Append one line to `session/phase-log.md`
87
+ - [ ] Update `session/summary.md`
88
+
89
+ ---
90
+
91
+ ## Step 8 β€” Git Commit Message
92
+ Provide one-liner commit message in this format:
93
+
94
+ fix([scope]): [what was broken and what fixed it]
95
+ Examples:
96
+ fix(auth): handle null session before calling fetchUser
97
+ fix(dashboard): return empty array instead of null on no results
98
+ fix(db): add missing RLS policy for users table select
99
+
100
+ **Do NOT push to GitHub. Hand the message to the user.**
101
+
102
+ ---
103
+
104
+ ## Step 9 β€” Confirm with User
105
+ - [ ] Show exactly what was changed and in which file
106
+ - [ ] Show test results
107
+ - [ ] Show commit message
108
+ - [ ] Ask: "Confirmed fixed β€” ready to continue?"
109
+ - [ ] Do NOT proceed until user confirms
110
+
111
+ ---
112
+
113
+ ## Bugfix Decision Tree
114
+
115
+ Error occurs
116
+ β”‚
117
+ β–Ό
118
+ Read logs/errors.log
119
+ β”‚
120
+ β”œβ”€β”€ Entry found β†’ go to Step 2
121
+ β”‚
122
+ └── No entry β†’ add logging first, reproduce error, then fix
123
+ β”‚
124
+ β–Ό
125
+ Check docs/learnings.md
126
+ β”‚
127
+ β”œβ”€β”€ Known issue β†’ apply fix directly
128
+ β”‚
129
+ └── New issue β†’ isolate β†’ fix β†’ document
130
+
131
+ ---
132
+
133
+ ## Common Bug Patterns
134
+
135
+ | Symptom | Likely Cause | Where to Look |
136
+ |---------|-------------|---------------|
137
+ | Function returns null unexpectedly | Missing null check on input | The function's first few lines |
138
+ | Supabase returns empty data | RLS policy blocking query | Supabase Dashboard β†’ Policies |
139
+ | Auth token errors | Session not refreshed | auth handler file |
140
+ | Test passes locally, fails in CI | Env var missing in CI | .env setup + CI config |
141
+ | Infinite re-render in React | useEffect dependency missing | The specific component file |
142
+ | Type error at runtime | TypeScript type not enforced at boundary | Input validation of the function |
143
+
144
+ ---
145
+
146
+ ## What NOT to Do
147
+ - Do not open the entire codebase to find the bug
148
+ - Do not fix multiple bugs in one session without separate commits
149
+ - Do not refactor while fixing β€” fix first, refactor separately
150
+ - Do not skip updating the test file
151
+ - Do not push to GitHub
152
+ - Do not chain fixes β€” one bug, one fix, one commit
docs/workflows/feature.md ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Workflow β€” Building a New Feature
2
+
3
+ ## Purpose
4
+ Read this file every time you start building a new feature.
5
+ Follow steps in order. Do not skip any step.
6
+
7
+ ---
8
+
9
+ ## Step 1 β€” Understand Before Coding
10
+ - [ ] Read the current phase prompt from `prompts/phase-X.md`
11
+ - [ ] Read `docs/progress.md` to confirm what is already done
12
+ - [ ] Read `docs/architecture.md` to understand existing structure
13
+ - [ ] Identify the exact files you will create or modify
14
+ - [ ] List all edge cases before writing any code
15
+
16
+ ---
17
+
18
+ ## Step 2 β€” Plan the Feature
19
+ State this out loud before coding:
20
+
21
+ Feature: [name]
22
+ Files to create: [list]
23
+ Files to modify: [list]
24
+ DB changes needed: yes/no
25
+ Edge cases: [list]
26
+ Tests needed: [list]
27
+
28
+ ---
29
+
30
+ ## Step 3 β€” DB Changes First (if needed)
31
+ - [ ] Write migration file before any application code
32
+ - [ ] Enable RLS on every new table
33
+ - [ ] Write RLS policies in the migration file
34
+ - [ ] Run `supabase db push` to apply
35
+ - [ ] Confirm in dashboard or via `supabase migration list`
36
+ - [ ] Reference `docs/deployment.md` for exact commands
37
+
38
+ ---
39
+
40
+ ## Step 4 β€” Write the Code
41
+ - [ ] Create or modify only the files identified in Step 2
42
+ - [ ] Add error handling to every function β€” follow `docs/debugging.md`
43
+ - [ ] No function should silently fail
44
+ - [ ] Handle all edge cases identified in Step 1
45
+ - [ ] No hardcoded values β€” use env vars or constants file
46
+ - [ ] No unused imports or dead code
47
+
48
+ ---
49
+
50
+ ## Step 5 β€” Write Tests Immediately
51
+ - [ ] Create test file at `tests/unit/` or `tests/integration/`
52
+ - [ ] Follow naming convention from `docs/testing.md`
53
+ - [ ] Cover all edge cases from Step 1
54
+ - [ ] Cover happy path + at least 2 failure paths per function
55
+ - [ ] Reference `.claude/skills/testing.md` for reusable patterns
56
+
57
+ ---
58
+
59
+ ## Step 6 β€” Run Tests
60
+ ```bash
61
+ # Run only this feature's tests first
62
+ npm test -- tests/unit/[feature-name].test.ts 2>&1 | tee logs/test.log
63
+
64
+ # If passing, run full suite
65
+ bash scripts/test.sh
66
+ ```
67
+ - [ ] All tests pass before moving forward
68
+ - [ ] If a test fails β€” read `logs/test.log`, fix the specific function only
69
+
70
+ ---
71
+
72
+ ## Step 7 β€” Self Review Checklist
73
+ Before declaring feature done:
74
+ - [ ] Error handling in place for every function
75
+ - [ ] No console.log left in code (only console.error with format)
76
+ - [ ] All edge cases handled
77
+ - [ ] Tests written and passing
78
+ - [ ] No new files created outside the plan in Step 2
79
+ - [ ] DB migration applied and confirmed
80
+ - [ ] RLS policies applied if new table was created
81
+
82
+ ---
83
+
84
+ ## Step 8 β€” Update Docs
85
+ - [ ] Add one-liner to `docs/progress.md`
86
+ - [ ] Append one line to `session/phase-log.md`
87
+ - [ ] Update `session/summary.md` with what was done
88
+ - [ ] If any failure or workaround occurred β†’ add to `docs/learnings.md`
89
+ - [ ] If architecture changed β†’ update `docs/architecture.md`
90
+
91
+ ---
92
+
93
+ ## Step 9 β€” Git Commit Message
94
+ Provide one-liner commit message in this format:
95
+
96
+ feat([scope]): [what was done in plain english]
97
+ Examples:
98
+ feat(auth): add email login with session handling
99
+ feat(dashboard): add user profile fetch with error handling
100
+ feat(db): add users table migration with RLS policies
101
+
102
+ **Do NOT push to GitHub. Hand the message to the user.**
103
+
104
+ ---
105
+
106
+ ## Step 10 β€” Confirm with User
107
+ - [ ] Show user what was built
108
+ - [ ] Show test results
109
+ - [ ] Show commit message
110
+ - [ ] Ask: "Ready to move to next feature or phase?"
111
+ - [ ] Do NOT proceed until user confirms
112
+
113
+ ---
114
+
115
+ ## What NOT to Do
116
+ - Do not write tests after all features are done
117
+ - Do not modify files outside the plan without flagging it
118
+ - Do not proceed to next feature if current tests are failing
119
+ - Do not push to GitHub
120
+ - Do not skip the docs update in Step 8
121
+ - Do not start next phase without user confirmation
docs/workflows/refactor.md ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Workflow β€” Refactoring Code
2
+
3
+ ## Purpose
4
+ Read this file every time you need to refactor existing code.
5
+ Refactoring means improving structure without changing behaviour.
6
+ If behaviour changes β€” that is a feature, not a refactor.
7
+
8
+ ---
9
+
10
+ ## Core Principle
11
+ **Tests must pass before AND after every refactor.**
12
+ If tests fail after refactor β€” you changed behaviour. Revert and retry.
13
+ Never refactor and fix a bug in the same commit.
14
+
15
+ ---
16
+
17
+ ## Step 1 β€” Confirm Refactor Scope
18
+ Before touching any code, state this out loud:
19
+
20
+ Refactor target: [file or function]
21
+ Reason for refactor: [why this needs to change]
22
+ Behaviour change: none
23
+ Files I will touch: [exact list]
24
+ Files I will NOT touch: [everything else]
25
+
26
+ - [ ] Confirm with user that this refactor is needed now
27
+ - [ ] Confirm all current tests pass before starting:
28
+ ```bash
29
+ bash scripts/test.sh
30
+ ```
31
+ - [ ] Do not proceed if any test is currently failing
32
+
33
+ ---
34
+
35
+ ## Step 2 β€” Identify What to Refactor
36
+ Valid reasons to refactor:
37
+ - [ ] Duplicate logic across multiple functions
38
+ - [ ] Function doing more than one thing
39
+ - [ ] Deeply nested conditionals reducing readability
40
+ - [ ] Magic numbers or hardcoded strings
41
+ - [ ] Missing or inconsistent error handling format
42
+ - [ ] Inconsistent naming conventions
43
+ - [ ] Dead code or unused imports
44
+
45
+ Not valid reasons:
46
+ - "It looks messy" without a specific structural problem
47
+ - Preference for a different syntax that does the same thing
48
+ - Rewriting working code during a bug fix session
49
+
50
+ ---
51
+
52
+ ## Step 3 β€” Refactor in Small Steps
53
+ - [ ] Change one thing at a time
54
+ - [ ] Run tests after each individual change
55
+ - [ ] Do not batch multiple refactors into one step
56
+ - [ ] Keep original logic visible until new logic is confirmed working
57
+
58
+ ### Order of operations:
59
+
60
+ Extract repeated logic into a shared utility function
61
+ Simplify conditionals (early returns over nested if/else)
62
+ Rename for clarity (variables, functions)
63
+ Remove dead code and unused imports
64
+ Standardise error handling format per docs/debugging.md
65
+
66
+ ---
67
+
68
+ ## Step 4 β€” Run Tests After Every Change
69
+ ```bash
70
+ # After each individual change
71
+ npm test -- tests/unit/[affected-file].test.ts 2>&1 | tee logs/test.log
72
+
73
+ # After all changes complete
74
+ bash scripts/test.sh
75
+ ```
76
+ - [ ] Every test that passed before must still pass
77
+ - [ ] If a test fails β†’ revert the last change, do not chain fixes
78
+
79
+ ---
80
+
81
+ ## Step 5 β€” Update Tests if Needed
82
+ Refactoring may require test updates only in these cases:
83
+ - [ ] A function was renamed β†’ update test description and import
84
+ - [ ] A function was split into two β†’ write tests for both
85
+ - [ ] A utility was extracted β†’ write a unit test for the utility
86
+
87
+ Do NOT update tests to make them pass after a refactor.
88
+ If a test fails after refactor β†’ the refactor changed behaviour β†’ revert.
89
+
90
+ ---
91
+
92
+ ## Step 6 β€” Self Review Checklist
93
+ Before declaring refactor done:
94
+ - [ ] Behaviour is identical before and after
95
+ - [ ] All tests pass
96
+ - [ ] No new files created outside the plan in Step 1
97
+ - [ ] Error handling format matches `docs/debugging.md`
98
+ - [ ] No console.log left in code
99
+ - [ ] No dead code or unused imports remain
100
+ - [ ] No hardcoded values introduced
101
+
102
+ ---
103
+
104
+ ## Step 7 β€” Update Docs
105
+ - [ ] Update `docs/architecture.md` if structure changed
106
+ - [ ] Add one-liner to `docs/progress.md`
107
+ - [ ] Append one line to `session/phase-log.md`
108
+ - [ ] Update `session/summary.md`
109
+ - [ ] If a pattern was discovered that saves time β†’ add to `docs/learnings.md`
110
+
111
+ ---
112
+
113
+ ## Step 8 β€” Git Commit Message
114
+ Provide one-liner commit message in this format:
115
+
116
+ refactor([scope]): [what was improved and how]
117
+ Examples:
118
+ refactor(auth): extract session validation into shared utility
119
+ refactor(dashboard): replace nested conditionals with early returns
120
+ refactor(db): standardise error handling across all supabase queries
121
+ refactor(utils): remove dead code and unused imports from helpers
122
+
123
+ **Do NOT push to GitHub. Hand the message to the user.**
124
+
125
+ ---
126
+
127
+ ## Step 9 β€” Confirm with User
128
+ - [ ] Show exactly what changed and in which files
129
+ - [ ] Show before/after for key changes
130
+ - [ ] Show test results confirming no behaviour change
131
+ - [ ] Show commit message
132
+ - [ ] Ask: "Refactor complete β€” ready to continue?"
133
+ - [ ] Do NOT proceed until user confirms
134
+
135
+ ---
136
+
137
+ ## Refactor Decision Tree
138
+
139
+ Refactor needed?
140
+ β”‚
141
+ β–Ό
142
+ All tests passing?
143
+ β”‚
144
+ β”œβ”€β”€ No β†’ fix failing tests first, then refactor
145
+ β”‚
146
+ └── Yes β†’ confirm scope β†’ refactor one thing at a time
147
+ β”‚
148
+ β–Ό
149
+ Run tests after each change
150
+ β”‚
151
+ β”œβ”€β”€ Pass β†’ continue next change
152
+ β”‚
153
+ └── Fail β†’ revert last change
154
+ β†’ reassess scope
155
+ β†’ do not chain fixes
156
+
157
+ ---
158
+
159
+ ## Refactor Patterns
160
+
161
+ ### Extract repeated logic
162
+ ```typescript
163
+ // Before β€” same null check in 3 functions
164
+ if (!userId || userId === '') return null
165
+
166
+ // After β€” shared utility
167
+ function isValidId(id: string | null | undefined): boolean {
168
+ return !!id && id.trim() !== ''
169
+ }
170
+ ```
171
+
172
+ ### Early returns over nested conditionals
173
+ ```typescript
174
+ // Before
175
+ function processUser(user: User | null) {
176
+ if (user) {
177
+ if (user.isActive) {
178
+ if (user.hasProfile) {
179
+ return user.profile
180
+ }
181
+ }
182
+ }
183
+ return null
184
+ }
185
+
186
+ // After
187
+ function processUser(user: User | null) {
188
+ if (!user) return null
189
+ if (!user.isActive) return null
190
+ if (!user.hasProfile) return null
191
+ return user.profile
192
+ }
193
+ ```
194
+
195
+ ### Standardise error handling
196
+ ```typescript
197
+ // Before β€” inconsistent
198
+ try {
199
+ ...
200
+ } catch (e) {
201
+ console.log(e) // wrong
202
+ }
203
+
204
+ // After β€” per docs/debugging.md
205
+ try {
206
+ ...
207
+ } catch (err) {
208
+ handleError(err, 'lib/users.ts', 'processUser')
209
+ return null
210
+ }
211
+ ```
212
+
213
+ ---
214
+
215
+ ## What NOT to Do
216
+ - Do not refactor and fix a bug in the same commit
217
+ - Do not refactor files not listed in Step 1
218
+ - Do not update tests to force them to pass after refactor
219
+ - Do not batch all refactors into one large change
220
+ - Do not refactor during a feature build session
221
+ - Do not push to GitHub
222
+ - Do not start a refactor if any test is currently failing
logs/errors.log ADDED
File without changes
logs/test.log ADDED
File without changes
prompts/phase-0.md ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 0 β€” Critic Quality Gate
2
+ > Paste this entire prompt into a fresh Claude Code session. Do not proceed to Phase 1 until the gate check at the bottom passes.
3
+
4
+ ---
5
+
6
+ You are helping me build a multi-agent RL environment called the "Viral Script Debugging Engine" for the Meta Γ— OpenEnv Hackathon. Before any environment code is written, we need to validate that the Critic agent produces high-quality, adversarial output β€” because the entire RL loop depends on it.
7
+
8
+ **Project context:**
9
+ - The system trains an LLM (the Arbitrator) via GRPO to decide which script improvements to make
10
+ - A Critic agent attacks creator scripts with specific, falsifiable claims
11
+ - A Defender agent argues for what should be preserved
12
+ - The Arbitrator (RL-trained model) decides which critique to act on each step
13
+ - If the Critic produces vague output, the RL signal collapses
14
+
15
+ **Your task for this phase:** Build the Critic agent and an evaluation harness to validate its output quality before any other code is written.
16
+
17
+ ---
18
+
19
+ ## Directory structure to create
20
+
21
+ ```
22
+ viral_script_engine/
23
+ β”œβ”€β”€ agents/
24
+ β”‚ β”œβ”€β”€ __init__.py
25
+ β”‚ └── critic.py
26
+ β”œβ”€β”€ data/
27
+ β”‚ β”œβ”€β”€ test_scripts/
28
+ β”‚ └── golden_fixtures/
29
+ β”œβ”€β”€ evaluation/
30
+ β”‚ β”œβ”€β”€ __init__.py
31
+ β”‚ └── critic_evaluator.py
32
+ β”œβ”€β”€ scripts/
33
+ β”‚ └── run_critic_gate.py
34
+ β”œβ”€β”€ requirements.txt
35
+ └── README.md
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Step 1 β€” `agents/critic.py`
41
+
42
+ Implement a `CriticAgent` class:
43
+
44
+ ```python
45
+ class CriticAgent:
46
+ def __init__(self, model_name: str = "claude-sonnet-4-20250514"):
47
+ # Use the Anthropic Python SDK
48
+
49
+ def critique(self, script: str, region: str, platform: str, niche: str) -> CritiqueOutput:
50
+ # Returns a CritiqueOutput dataclass
51
+ ```
52
+
53
+ The `CritiqueOutput` and `CritiqueClaim` Pydantic models must have these exact fields:
54
+
55
+ ```python
56
+ class CritiqueClaim(BaseModel):
57
+ claim_id: str # e.g. "C1", "C2"
58
+ critique_class: str # one of: "hook_weakness" | "pacing_issue" | "cultural_mismatch" | "cta_buried" | "coherence_break" | "retention_risk"
59
+ claim_text: str
60
+ timestamp_range: str # e.g. "0:00-0:03" or "N/A"
61
+ evidence: str # exact quote from the script supporting this claim
62
+ is_falsifiable: bool
63
+ severity: str # "low" | "medium" | "high"
64
+
65
+ class CritiqueOutput(BaseModel):
66
+ claims: List[CritiqueClaim]
67
+ overall_severity: str
68
+ raw_response: str
69
+ ```
70
+
71
+ **System prompt to use (exact):**
72
+ ```
73
+ You are an expert social media content critic specialising in short-form video scripts for Reels and YouTube Shorts. Your job is to find specific, real problems in creator scripts β€” not vague feedback.
74
+
75
+ RULES:
76
+ 1. Every claim must cite a specific part of the script (quote it or reference the timestamp range)
77
+ 2. Every claim must be falsifiable β€” a human editor must be able to verify it by re-reading the script
78
+ 3. Never say "the hook is weak" β€” say "the hook at 0:00-0:03 promises [X] but the script delivers [Y] at 0:22, by which time most viewers have already dropped off"
79
+ 4. Focus on the 6 critique classes: hook_weakness, pacing_issue, cultural_mismatch, cta_buried, coherence_break, retention_risk
80
+ 5. Produce between 3 and 6 claims per script. No more, no less.
81
+ 6. For each claim, assign a timestamp range if the issue is locatable in the script. Use "N/A" only if it's a structural issue spanning the whole script.
82
+
83
+ OUTPUT FORMAT (respond ONLY with valid JSON, no markdown, no preamble):
84
+ {
85
+ "claims": [
86
+ {
87
+ "claim_id": "C1",
88
+ "critique_class": "hook_weakness",
89
+ "claim_text": "...",
90
+ "timestamp_range": "0:00-0:03",
91
+ "evidence": "exact quote from script",
92
+ "is_falsifiable": true,
93
+ "severity": "high"
94
+ }
95
+ ],
96
+ "overall_severity": "high"
97
+ }
98
+ ```
99
+
100
+ User prompt format:
101
+ ```
102
+ SCRIPT TO CRITIQUE:
103
+ {script}
104
+
105
+ TARGET REGION: {region}
106
+ PLATFORM: {platform}
107
+ NICHE: {niche}
108
+
109
+ Produce your critique now.
110
+ ```
111
+
112
+ If JSON parsing fails, retry once with a stricter prompt. If it fails twice, raise `CriticParseError`.
113
+
114
+ ---
115
+
116
+ ## Step 2 β€” `data/test_scripts/scripts.json`
117
+
118
+ Create 10 realistic 60–90 second Reel/Shorts scripts saved as a JSON array. Distribution:
119
+ - S01–S03: Mumbai Gen Z (finance, fashion, tech)
120
+ - S04–S06: Tier-2 Hindi belt (agriculture, small business, local culture)
121
+ - S07–S08: Pan-India English (startup advice, productivity)
122
+ - S09–S10: Hinglish (mixed Hindi-English)
123
+
124
+ Each entry:
125
+ ```json
126
+ {
127
+ "script_id": "S01",
128
+ "region": "Mumbai Gen Z",
129
+ "platform": "Reels",
130
+ "niche": "personal finance",
131
+ "script_text": "...(100-200 words)...",
132
+ "known_flaws": ["buried_hook", "no_cta"]
133
+ }
134
+ ```
135
+
136
+ S01–S04 should have **obvious** single flaws. S05–S07 should have **subtle** flaws. S08–S10 should have **conflicting** issues where fixing one hurts another. Do not make all scripts bad in the same way.
137
+
138
+ ---
139
+
140
+ ## Step 3 β€” `evaluation/critic_evaluator.py`
141
+
142
+ ```python
143
+ class CriticEvaluator:
144
+ def evaluate(self, output: CritiqueOutput, script_text: str) -> EvaluationResult:
145
+ pass
146
+
147
+ def batch_evaluate(self, results: List[Tuple[CritiqueOutput, str]]) -> BatchEvaluationResult:
148
+ pass
149
+ ```
150
+
151
+ `EvaluationResult` fields:
152
+ - `claim_count: int` β€” must be 3–6 to pass
153
+ - `specificity_score: float` β€” fraction of claims where `evidence` is a substring of the script (use substring match)
154
+ - `falsifiability_score: float` β€” fraction of claims with `is_falsifiable=True`
155
+ - `timestamp_coverage: float` β€” fraction of claims with a non-"N/A" timestamp_range
156
+ - `critique_class_diversity: float` β€” unique critique_classes / 6
157
+ - `passes_gate: bool` β€” True if: `claim_count >= 3 AND specificity_score >= 0.6 AND falsifiability_score >= 0.7`
158
+
159
+ `BatchEvaluationResult` fields:
160
+ - `pass_count: int`
161
+ - `pass_rate: float`
162
+ - `passes_overall_gate: bool` β€” True if `pass_rate >= 0.8`
163
+ - `per_script_results: List[EvaluationResult]`
164
+ - `failing_scripts: List[str]`
165
+
166
+ The evaluator must be purely rule-based β€” no LLM calls.
167
+
168
+ ---
169
+
170
+ ## Step 4 β€” `scripts/run_critic_gate.py`
171
+
172
+ CLI that:
173
+ 1. Loads all 10 scripts from `data/test_scripts/scripts.json`
174
+ 2. Runs CriticAgent on each
175
+ 3. Runs CriticEvaluator on each result
176
+ 4. Prints a per-script pass/fail report using `rich`
177
+ 5. Prints overall GATE PASS or GATE FAIL
178
+ 6. If gate passes: saves all outputs as JSON to `data/golden_fixtures/fixture_S01.json` etc.
179
+ 7. If gate fails: prints which scripts failed and which scores were below threshold
180
+
181
+ Flags:
182
+ - `--max-retries 3`: retry failed scripts up to N times with a tighter prompt
183
+ - `--dry-run`: run only first 2 scripts
184
+
185
+ ---
186
+
187
+ ## Step 5 β€” `requirements.txt`
188
+
189
+ ```
190
+ anthropic>=0.40.0
191
+ sentence-transformers>=2.7.0
192
+ numpy>=1.26.0
193
+ pydantic>=2.0.0
194
+ python-dotenv>=1.0.0
195
+ rich>=13.0.0
196
+ pytest>=8.0.0
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Step 6 β€” `tests/test_critic.py`
202
+
203
+ - Test `CritiqueOutput` parses correctly from a mock LLM JSON response
204
+ - Test `EvaluationResult` correctly identifies a passing vs failing critique
205
+ - Test CLI exits with code 1 if gate fails, 0 if gate passes
206
+ - Mock all Anthropic API calls β€” no real API calls in tests
207
+
208
+ ---
209
+
210
+ ## Constraints
211
+
212
+ - Use the Anthropic Python SDK only (not OpenAI)
213
+ - Store API key in `.env`, load with `python-dotenv`
214
+ - All models use Pydantic for validation
215
+ - Evaluator has zero LLM calls
216
+ - Use `rich` for all console output (progress bars, coloured pass/fail)
217
+ - All JSON outputs pretty-printed with `indent=2`
218
+
219
+ ---
220
+
221
+ ## Gate check
222
+
223
+ Run:
224
+ ```
225
+ python scripts/run_critic_gate.py --dry-run
226
+ ```
227
+
228
+ Must print `PHASE 0 GATE: PASS` when 8/10 scripts produce β‰₯3 specific, timestamped, falsifiable claims. Do not open Phase 1 until this passes.
prompts/phase-1.md ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 1 β€” OpenEnv Scaffold + R1/R2 Rewards
2
+ > Paste this entire prompt into a fresh Claude Code session. Phase 0 must be complete and golden fixtures saved before starting this phase.
3
+
4
+ ---
5
+
6
+ Phase 0 is complete. The Critic agent is validated and all golden fixtures are saved in `data/golden_fixtures/`. Now build the core OpenEnv environment with two reward signals and get a complete dummy episode running end-to-end.
7
+
8
+ **Current state:**
9
+ - `agents/critic.py` β€” CriticAgent working and validated
10
+ - `data/golden_fixtures/` β€” 10 validated critic outputs as JSON
11
+ - `data/test_scripts/scripts.json` β€” 10 test scripts
12
+
13
+ ---
14
+
15
+ ## New files to create
16
+
17
+ ```
18
+ viral_script_engine/
19
+ β”œβ”€β”€ environment/
20
+ β”‚ β”œβ”€β”€ __init__.py
21
+ β”‚ β”œβ”€β”€ env.py
22
+ β”‚ β”œβ”€β”€ actions.py
23
+ β”‚ β”œβ”€β”€ observations.py
24
+ β”‚ └── episode_state.py
25
+ β”œβ”€β”€ rewards/
26
+ β”‚ β”œβ”€β”€ __init__.py
27
+ β”‚ β”œβ”€β”€ base.py
28
+ β”‚ β”œβ”€β”€ r1_hook_strength.py
29
+ β”‚ β”œβ”€β”€ r2_coherence.py
30
+ β”‚ └── reward_aggregator.py
31
+ β”œβ”€β”€ agents/
32
+ β”‚ └── rewriter.py # NEW β€” do not modify critic.py
33
+ β”œβ”€β”€ tests/
34
+ β”‚ β”œβ”€β”€ test_environment.py
35
+ β”‚ └── test_rewards.py
36
+ └── scripts/
37
+ └── run_dummy_episode.py
38
+ ```
39
+
40
+ **The environment must follow OpenEnv's Gymnasium-compatible API exactly:**
41
+ - `reset()` β†’ `(observation: dict, info: dict)`
42
+ - `step(action)` β†’ `(observation: dict, reward: float, terminated: bool, truncated: bool, info: dict)`
43
+ - `state()` β†’ full current state dict
44
+
45
+ ---
46
+
47
+ ## Step 1 β€” `environment/actions.py`
48
+
49
+ ```python
50
+ from enum import Enum
51
+ from pydantic import BaseModel
52
+
53
+ class ActionType(str, Enum):
54
+ HOOK_REWRITE = "hook_rewrite"
55
+ SECTION_REORDER = "section_reorder"
56
+ CULTURAL_REF_SUB = "cultural_ref_sub"
57
+ CTA_PLACEMENT = "cta_placement"
58
+
59
+ class ArbitratorAction(BaseModel):
60
+ action_type: ActionType
61
+ target_section: str # "hook" | "body" | "cta" | "full"
62
+ instruction: str # natural language instruction to the Rewriter
63
+ critique_claim_id: str # which CritiqueClaim this responds to, e.g. "C2"
64
+ reasoning: str # why this action was chosen (used in demo and logs)
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Step 2 β€” `environment/observations.py`
70
+
71
+ ```python
72
+ class RewardComponents(BaseModel):
73
+ r1_hook_strength: Optional[float] = None
74
+ r2_coherence: Optional[float] = None
75
+ r3_cultural_alignment: Optional[float] = None
76
+ r4_debate_resolution: Optional[float] = None
77
+ r5_defender_preservation: Optional[float] = None
78
+ r6_retention_proxy: Optional[float] = None
79
+ anti_gaming_penalty: float = 0.0
80
+ total: float = 0.0
81
+
82
+ def compute_total(self) -> float:
83
+ # Weights: R1=0.25, R2=0.20, R3=0.20, R4=0.20, R5=0.15
84
+ # Sum only non-None components, normalise weights to sum to 1.0
85
+ # Subtract anti_gaming_penalty, clip to [0, 1]
86
+
87
+ class DebateRound(BaseModel):
88
+ step_num: int
89
+ critic_claims: List[CritiqueClaim]
90
+ defender_response: Optional[Any] = None # DefenderOutput added in Phase 2
91
+ arbitrator_action: Optional[ArbitratorAction] = None
92
+ rewrite_diff: Optional[str] = None # unified diff string
93
+ reward_components: Optional[RewardComponents] = None
94
+
95
+ class Observation(BaseModel):
96
+ current_script: str
97
+ original_script: str
98
+ region: str
99
+ platform: str
100
+ niche: str
101
+ step_num: int
102
+ max_steps: int
103
+ debate_history: List[DebateRound]
104
+ reward_components: RewardComponents
105
+ difficulty_level: str # "easy" | "medium" | "hard" | "self_generated"
106
+ episode_id: str # UUID
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Step 3 β€” `rewards/r1_hook_strength.py`
112
+
113
+ Fully rule-based β€” zero LLM calls. Scores the first 3 sentences or ~50 words (whichever is shorter).
114
+
115
+ **5 checks, each worth 0.2:**
116
+
117
+ 1. **Promise check** β€” hook contains a concrete promise or specific claim. Look for: numbers, "how to", "why", "what happens when", "I made X". Fail: generic openers like "Hey guys", "Welcome back", "Today we're talking about".
118
+
119
+ 2. **Curiosity gap check** β€” hook creates unresolved tension. Look for: question structures, "but here's the thing", "most people don't know", "the secret is". Fail: hook that immediately resolves the tension in the same sentence.
120
+
121
+ 3. **Specificity check** β€” at least one specific number, proper noun, or concrete detail. Look for: digit sequences, proper nouns, specific product/place names.
122
+
123
+ 4. **Front-loading check** β€” first sentence contains at least 2 of the above signals.
124
+
125
+ 5. **Anti-filler check** β€” avoids these dead openers (case-insensitive): `["hey guys", "welcome back", "today i want to", "so today", "in this video", "what's up everyone", "hey everyone", "guys today", "hello everyone", "so basically"]`
126
+
127
+ `score = checks_passed / 5`, clipped to [0, 1].
128
+
129
+ Return a `HookRewardResult` with: `score`, `checks_passed`, `check_details: dict`.
130
+
131
+ ---
132
+
133
+ ## Step 4 β€” `rewards/r2_coherence.py`
134
+
135
+ Uses `sentence-transformers all-MiniLM-L6-v2`. Cache embeddings by `hash(text)`.
136
+
137
+ Score mapping from raw cosine similarity:
138
+ - `< 0.65` β†’ `0.0` (drifted too far from creator intent)
139
+ - `0.65–0.80` β†’ linearly map to `0.0–0.5`
140
+ - `0.80–0.95` β†’ linearly map to `0.5–1.0`
141
+ - `> 0.95` β†’ `0.8` (barely changed β€” penalise inaction)
142
+
143
+ Return a `CoherenceRewardResult` with: `score`, `raw_similarity`, `interpretation: str`.
144
+
145
+ ---
146
+
147
+ ## Step 5 β€” `rewards/reward_aggregator.py`
148
+
149
+ ```python
150
+ class RewardAggregator:
151
+ WEIGHTS = {"r1": 0.25, "r2": 0.20, "r3": 0.20, "r4": 0.20, "r5": 0.15}
152
+
153
+ def compute(
154
+ self,
155
+ components: RewardComponents,
156
+ episode_start_components: RewardComponents,
157
+ action_history: List[ActionType],
158
+ ) -> RewardComponents:
159
+ ```
160
+
161
+ **Anti-gaming rule 1 β€” Catastrophic drop:**
162
+ For each reward component not None in both current and episode_start: if `current < episode_start - 0.2`, set total = 0.0 and log which component triggered it.
163
+
164
+ **Anti-gaming rule 2 β€” Action diversity:**
165
+ If the last 3 entries in `action_history` are all the same `ActionType`, subtract 0.15 from total before clipping.
166
+
167
+ Always clip final total to [0, 1]. Set `components.anti_gaming_penalty` to total deduction applied.
168
+
169
+ ---
170
+
171
+ ## Step 6 β€” `agents/rewriter.py`
172
+
173
+ ```python
174
+ class RewriterAgent:
175
+ def __init__(self, model_name: str = "claude-sonnet-4-20250514"):
176
+ pass
177
+
178
+ def rewrite(self, current_script: str, action: ArbitratorAction) -> RewriteResult:
179
+ pass
180
+ ```
181
+
182
+ System prompt: "You are a professional script editor for short-form social media video. Apply ONLY the instruction given. Do not make any other changes. Do not add new ideas. Do not change the creator's voice or regional language patterns. Return ONLY the rewritten script text, no commentary."
183
+
184
+ User prompt includes: `current_script`, `action.action_type`, `action.instruction`, `action.target_section`.
185
+
186
+ `RewriteResult` has: `rewritten_script`, `diff` (unified diff string), `word_count_delta: int`.
187
+
188
+ ---
189
+
190
+ ## Step 7 β€” `environment/env.py`
191
+
192
+ ```python
193
+ from openenv import Environment
194
+
195
+ class ViralScriptEnv(Environment):
196
+ DIFFICULTY_LEVELS = ["easy", "medium", "hard", "self_generated"]
197
+
198
+ def __init__(
199
+ self,
200
+ scripts_path: str = "data/test_scripts/scripts.json",
201
+ max_steps: int = 5,
202
+ difficulty: str = "easy",
203
+ use_anti_gaming: bool = True,
204
+ ):
205
+ # Script tiers: easy=S01-S04, medium=S05-S07, hard=S08-S10
206
+ # Init: CriticAgent, RewriterAgent, RewardAggregator, HookStrengthReward, CoherenceReward
207
+ # R3-R5 are None until Phase 2
208
+
209
+ def reset(self, seed=None, options=None) -> Tuple[dict, dict]:
210
+ # 1. Sample script from current difficulty tier
211
+ # 2. Reset all debate state
212
+ # 3. Compute initial R1 and R2 on unmodified script β†’ save as episode_start_rewards
213
+ # 4. Return (observation_dict, info_dict)
214
+
215
+ def step(self, action: dict) -> Tuple[dict, float, bool, bool, dict]:
216
+ # 1. Parse action dict β†’ ArbitratorAction
217
+ # 2. Run Critic on current script
218
+ # 3. [Defender: skip in Phase 1, wire in Phase 2]
219
+ # 4. Run Rewriter with the action β†’ new script
220
+ # 5. Compute R1, R2 on new script
221
+ # 6. Run RewardAggregator (anti-gaming checks applied)
222
+ # 7. Append DebateRound to debate_history
223
+ # 8. Increment step counter
224
+ # 9. terminated if step_num >= max_steps OR total_reward >= 0.9
225
+ # 10. info_dict must include: reward_components, anti_gaming_triggered, penalty_reason
226
+
227
+ def state(self) -> dict:
228
+ # Full JSON-serialisable state: current_script, original_script, debate_history,
229
+ # reward_components, step_num, difficulty_level, episode_id
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Step 8 β€” `scripts/run_dummy_episode.py`
235
+
236
+ ```
237
+ python scripts/run_dummy_episode.py --difficulty easy --steps 3 --verbose
238
+ ```
239
+
240
+ 1. Instantiate `ViralScriptEnv`
241
+ 2. Call `reset()`
242
+ 3. Each step: sample a random `ActionType`, construct a minimal valid `ArbitratorAction`, call `step()`
243
+ 4. Print per-step: script diff, reward components, any anti-gaming penalties (use `rich` panels/tables)
244
+ 5. At end: print final reward, R1, R2
245
+ 6. Save full episode log to `logs/episode_<id>.json`
246
+
247
+ Output must be readable to a non-technical judge watching the demo.
248
+
249
+ ---
250
+
251
+ ## Step 9 β€” Tests
252
+
253
+ **`tests/test_environment.py`:**
254
+ - `reset()` returns a valid observation dict
255
+ - `step()` with a valid action completes without error
256
+ - `step()` increments `step_num` correctly
257
+ - Anti-gaming penalty fires when same action repeated 3 times
258
+ - Episode terminates at `max_steps`
259
+ - Reward is clipped to [0, 1]
260
+ - Mock all LLM calls using fixtures from `data/golden_fixtures/`
261
+
262
+ **`tests/test_rewards.py`:**
263
+ - R1 on 5 hand-crafted hooks: 2 score >0.8, 2 score <0.3, 1 is edge case ~0.5
264
+ - R2 with identical strings β†’ 0.8 (the >0.95 penalty case)
265
+ - R2 with completely different strings β†’ 0.0 (below 0.65 threshold)
266
+ - RewardAggregator catastrophic drop penalty zeroes reward correctly
267
+ - RewardAggregator diversity penalty fires on 3 identical consecutive actions
268
+
269
+ ---
270
+
271
+ ## Gate check
272
+
273
+ Run:
274
+ ```
275
+ python scripts/run_dummy_episode.py --difficulty easy --steps 3 --verbose
276
+ ```
277
+
278
+ The final line must print:
279
+ ```
280
+ PHASE 1 GATE: PASS
281
+ ```
282
+
283
+ Conditions: (a) episode completed without error, (b) R1 and R2 are non-null in final state, (c) episode log saved to `logs/`.
284
+
285
+ Also deploy this version to HF Spaces immediately as an early checkpoint before moving to Phase 2.
prompts/phase-2.md ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 2 β€” Defender Agent + R3/R4/R5 + Anti-Gaming + Baseline
2
+ > Paste this entire prompt into a fresh Claude Code session. Phase 1 must be complete (dummy episode running, R1/R2 working) before starting.
3
+
4
+ ---
5
+
6
+ Phase 1 is complete. The environment scaffold runs and dummy episodes complete end-to-end. Now complete the full agent loop with all 5 reward signals, bake in the anti-gaming protections with full logging, and record the pre-training baseline.
7
+
8
+ **Current state:**
9
+ - `environment/env.py` β€” ViralScriptEnv working with R1+R2
10
+ - `agents/critic.py` β€” validated CriticAgent
11
+ - `agents/rewriter.py` β€” RewriterAgent
12
+ - Defender slot exists in Observation but is still `None`
13
+
14
+ ---
15
+
16
+ ## Step 1 β€” `agents/defender.py`
17
+
18
+ ```python
19
+ class DefenderOutput(BaseModel):
20
+ core_strength: str # single most important thing to preserve
21
+ core_strength_quote: str # exact verbatim quote from the script
22
+ defense_argument: str # why this should not be changed
23
+ flagged_critic_claims: List[str] # claim_ids the Defender believes are overcorrections
24
+ regional_voice_elements: List[str] # phrases/references that are intentionally regional
25
+
26
+ class DefenderAgent:
27
+ def __init__(self, model_name: str = "claude-sonnet-4-20250514"):
28
+ pass
29
+
30
+ def defend(
31
+ self,
32
+ script: str,
33
+ critic_claims: List[CritiqueClaim],
34
+ region: str,
35
+ platform: str,
36
+ ) -> DefenderOutput:
37
+ pass
38
+ ```
39
+
40
+ **System prompt (exact):**
41
+ ```
42
+ You are a script defender for short-form video content. Your job is NOT to say the script is perfect.
43
+ Your job is to identify what is genuinely working β€” and protect it from being edited away.
44
+
45
+ Specifically:
46
+ 1. Find the single most powerful element of the script. Quote it exactly.
47
+ 2. Explain why a viewer would respond positively to this element.
48
+ 3. Review the Critic's claims. Flag any that would destroy the script's core strength or strip its regional authenticity if acted on.
49
+ 4. List any phrases, idioms, or references that are intentionally regional β€” these must not be "corrected" away.
50
+
51
+ OUTPUT (JSON only, no preamble):
52
+ {
53
+ "core_strength": "one sentence describing the strongest element",
54
+ "core_strength_quote": "exact verbatim quote from the script",
55
+ "defense_argument": "why this element should be preserved",
56
+ "flagged_critic_claims": ["C2", "C3"],
57
+ "regional_voice_elements": ["specific phrase 1", "specific phrase 2"]
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Step 2 β€” `rewards/r3_cultural_alignment.py`
64
+
65
+ Rule-based, zero LLM calls. Uses a JSON knowledge base.
66
+
67
+ ```python
68
+ class CulturalAlignmentReward:
69
+ def __init__(self, knowledge_base_path: str = "data/cultural_kb.json"):
70
+ pass
71
+
72
+ def score(self, script: str, region: str) -> CulturalRewardResult:
73
+ # score = (valid_refs_found + correct_idioms_found
74
+ # - invalid_signals_found - anachronistic_signals_found)
75
+ # / max(total_valid_refs + total_idioms, 1)
76
+ # clip to [0, 1]
77
+ ```
78
+
79
+ Create `data/cultural_kb.json` with at least 15 entries per category per region for:
80
+
81
+ **Mumbai Gen Z:**
82
+ - `valid_refs`: Bandra, CSMT, dabba, auto, local train, startup scene, Zomato, Swiggy, IPL, Bollywood 2020s
83
+ - `invalid_signals`: outdated slang pre-2015
84
+ - `correct_idioms`: "ek dum solid", "full on", "kya scene hai", Hinglish patterns
85
+
86
+ **Tier-2 Hindi Belt:**
87
+ - `valid_refs`: kirana store, mandap, jugaad, sabzi mandi, panchayat, mela, dal-chawal
88
+ - `invalid_signals`: metro-centric language, startup jargon
89
+ - `correct_idioms`: Hindi-dominant mixed phrases
90
+
91
+ **Pan-India English:** minimal constraints; penalise overly regional without context.
92
+
93
+ **Hinglish:** reward balanced Hindi-English mixing; penalise fully formal English.
94
+
95
+ ---
96
+
97
+ ## Step 3 β€” `rewards/r4_debate_resolution.py`
98
+
99
+ Re-runs the Critic on the new script after each rewrite. Checks whether the specific claim the Arbitrator targeted still appears.
100
+
101
+ ```python
102
+ class DebateResolutionReward:
103
+ def __init__(self, critic_agent: CriticAgent):
104
+ self.critic = critic_agent
105
+
106
+ def score(
107
+ self,
108
+ new_script: str,
109
+ original_action: ArbitratorAction,
110
+ original_claim: CritiqueClaim,
111
+ region: str,
112
+ platform: str,
113
+ niche: str,
114
+ ) -> DebateResolutionResult:
115
+ # Re-run critic on new_script
116
+ # A claim is "resolved" if new critique has NO claim of the same critique_class
117
+ # targeting the same timestamp_range (Β±5 seconds)
118
+ # OR the new claim for that section has severity "low" (down from "medium"/"high")
119
+ # Score: 1.0=resolved, 0.5=partially resolved (severity reduced), 0.0=claim persists
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Step 4 β€” `rewards/r5_defender_preservation.py`
125
+
126
+ Uses `sentence-transformers all-MiniLM-L6-v2` (same model as R2 β€” reuse the loaded instance).
127
+
128
+ ```python
129
+ class DefenderPreservationReward:
130
+ def score(self, defender_output: DefenderOutput, rewritten_script: str) -> DefenderPreservationResult:
131
+ # Chunk rewritten_script into sentences
132
+ # Compute cosine_similarity(embed(core_strength_quote), embed(sentence)) for each sentence
133
+ # Take the max similarity across all sentences
134
+ # Score mapping:
135
+ # max_similarity >= 0.85 β†’ 1.0
136
+ # max_similarity 0.65–0.85 β†’ max_similarity (partial)
137
+ # max_similarity < 0.65 β†’ 0.0
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Step 5 β€” Update `environment/env.py`
143
+
144
+ **Modify `step()`:**
145
+ 1. After Critic runs, immediately run Defender on the same script state
146
+ 2. Store `DefenderOutput` in the `DebateRound`
147
+ 3. After Rewriter executes, compute R3, R4, R5 alongside R1, R2
148
+ 4. Pass all 5 to RewardAggregator
149
+
150
+ **Modify `reset()`:**
151
+ 1. Compute baseline R1–R5 on unmodified script at episode start
152
+ 2. R4 and R5 will be `None` at reset β€” handle gracefully in aggregator (skip them in weighted sum)
153
+
154
+ ---
155
+
156
+ ## Step 6 β€” Update `rewards/reward_aggregator.py`
157
+
158
+ - Anti-gaming catastrophic drop check must now work across all 5 rewards
159
+ - Normalise weights so available (non-None) rewards always sum to 1.0
160
+
161
+ Add `AntiGamingLog`:
162
+
163
+ ```python
164
+ class AntiGamingLog(BaseModel):
165
+ episode_id: str
166
+ step_num: int
167
+ triggered: bool
168
+ rule_triggered: Optional[str] # "catastrophic_drop" | "action_repetition" | None
169
+ component_that_dropped: Optional[str] # which reward triggered catastrophic drop
170
+ penalty_applied: float
171
+ pre_penalty_total: float
172
+ post_penalty_total: float
173
+ ```
174
+
175
+ Every `RewardAggregator.compute()` call must return `(RewardComponents, AntiGamingLog)`. Save all `AntiGamingLog` entries in the episode's `info` dict and in the episode log JSON.
176
+
177
+ ---
178
+
179
+ ## Step 7 β€” `agents/baseline_arbitrator.py`
180
+
181
+ ```python
182
+ class BaselineArbitratorAgent:
183
+ """
184
+ Untrained Arbitrator for the pre-training baseline.
185
+ Uses zero-shot instruction β€” no chain-of-thought, no few-shot examples.
186
+ This ensures the comparison is fair: trained model learns through RL, not prompting.
187
+ """
188
+
189
+ SYSTEM_PROMPT = """
190
+ You are helping improve a short-form video script.
191
+ You have observed a debate between a Critic and a Defender about the script.
192
+ Choose ONE action to take to improve the script.
193
+
194
+ Available actions: hook_rewrite, section_reorder, cultural_ref_sub, cta_placement
195
+
196
+ Respond ONLY with valid JSON:
197
+ {
198
+ "action_type": "hook_rewrite",
199
+ "target_section": "hook",
200
+ "instruction": "specific instruction for the rewriter",
201
+ "critique_claim_id": "C1",
202
+ "reasoning": "brief explanation"
203
+ }
204
+ """
205
+
206
+ def __init__(self, model_name: str = "claude-haiku-4-5-20251001"):
207
+ # Haiku: cheaper and clearly weaker than trained model
208
+ pass
209
+
210
+ def act(self, observation: dict) -> dict:
211
+ pass
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Step 8 β€” `scripts/run_baseline.py`
217
+
218
+ Run 20 full episodes with `BaselineArbitratorAgent`:
219
+ - Episodes 1–8: `difficulty="easy"`
220
+ - Episodes 9–16: `difficulty="medium"`
221
+ - Episodes 17–20: `difficulty="hard"`
222
+
223
+ Log per episode: episode_id, difficulty, script_id, per-step R1–R5, anti_gaming_triggered, penalty, total reward, final script vs original.
224
+
225
+ Save to `logs/baseline_results.json`.
226
+
227
+ Generate `logs/baseline_reward_curves.png`:
228
+ - 2 rows Γ— 3 cols subplots: R1, R2, R3, R4, R5, Total
229
+ - X-axis: episode number (1–20), labelled
230
+ - Y-axis: reward value [0, 1], labelled
231
+ - Title: "Baseline (Untrained) Arbitrator β€” Pre-Training Reward Curves"
232
+ - `dpi=150`, save as PNG
233
+ - These plots are submitted to judges
234
+
235
+ Print a `rich` summary table showing mean Β± std of each reward across all 20 episodes.
236
+
237
+ ---
238
+
239
+ ## Step 9 β€” `tests/test_phase2.py`
240
+
241
+ - `DefenderAgent` parses output correctly from mock LLM response
242
+ - R3 scores correctly on 3 regional vs 3 non-regional hand-crafted scripts
243
+ - R4 correctly identifies resolved vs unresolved claims (mock Critic re-run)
244
+ - R5 scores correctly when `core_strength_quote` is present vs absent in rewrite
245
+ - Anti-gaming catastrophic drop zeroes reward when R2 drops by 0.25
246
+ - Anti-gaming diversity penalty fires on 3Γ— same action
247
+ - `AntiGamingLog` is populated correctly in both triggering and non-triggering cases
248
+
249
+ ---
250
+
251
+ ## Gate check
252
+
253
+ Run:
254
+ ```
255
+ python scripts/run_baseline.py
256
+ ```
257
+
258
+ Must:
259
+ 1. Complete all 20 episodes without error
260
+ 2. Save `logs/baseline_reward_curves.png`
261
+ 3. Print:
262
+ ```
263
+ PHASE 2 GATE: PASS β€” Baseline curves saved. Pre-training mean total reward: X.XX
264
+ ```
265
+
266
+ Expected baseline total reward: **0.25–0.55**. If above 0.7, tasks are too easy. If below 0.1, tasks are too hard and RL training will stall β€” adjust script difficulty before Phase 3.
prompts/phase-3.md ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 3 β€” Curriculum Dataset + GRPO Training
2
+ > Paste this entire prompt into a fresh Claude Code session. Phases 0–2 must be complete, baseline curves saved, before starting.
3
+
4
+ ---
5
+
6
+ Phases 0–2 are complete. The environment has all 5 rewards, anti-gaming protections are live, and baseline reward curves are saved in `logs/baseline_reward_curves.png`. Now build the curriculum datasets and the GRPO training pipeline.
7
+
8
+ **The training script must connect to the live OpenEnv environment β€” not a static dataset.**
9
+
10
+ ---
11
+
12
+ ## Step 1 β€” `data/curriculum/build_curriculum.py`
13
+
14
+ Build three curriculum tiers. Each is a JSONL file where each line is one episode config.
15
+
16
+ **Episode config schema:**
17
+ ```json
18
+ {
19
+ "episode_config_id": "easy_001",
20
+ "difficulty": "easy",
21
+ "script_id": "S01",
22
+ "script_text": "...",
23
+ "region": "Mumbai Gen Z",
24
+ "platform": "Reels",
25
+ "niche": "personal finance",
26
+ "dominant_flaw": "buried_hook",
27
+ "expected_critique_class": "hook_weakness",
28
+ "expected_action": "hook_rewrite",
29
+ "curriculum_notes": "One obvious flaw. Critic should win immediately. Strong reward signal on step 1."
30
+ }
31
+ ```
32
+
33
+ **Generate:**
34
+ - `data/curriculum/easy_tier.jsonl` β€” 20 configs (10 from existing scripts + 10 synthetic)
35
+ - `data/curriculum/medium_tier.jsonl` β€” 15 configs (trade-off scenarios where Critic and Defender both have valid points)
36
+ - `data/curriculum/hard_tier.jsonl` β€” 10 configs (fixing the top critique damages R3 β€” explicit conflicts)
37
+
38
+ **For synthetic scripts**, create `data/curriculum/generate_synthetic_scripts.py`:
39
+
40
+ Use the Anthropic API with this prompt pattern:
41
+ ```
42
+ Generate a realistic 60-90 second Reels script for [niche] targeting [region].
43
+ Intentionally include [flaw_type] as the dominant flaw.
44
+ The flaw should be [easy|medium|hard] to diagnose.
45
+ ```
46
+
47
+ Generate: 10 easy, 5 medium, 5 hard synthetic scripts. Save to `data/curriculum/synthetic_scripts.json`.
48
+
49
+ ---
50
+
51
+ ## Step 2 β€” `training/rollout_function.py`
52
+
53
+ This bridges TRL's `GRPOTrainer` to the live OpenEnv environment. It's the most critical file in this phase.
54
+
55
+ ```python
56
+ def build_rollout_fn(env: ViralScriptEnv, max_steps: int = 5):
57
+ """
58
+ Returns a function compatible with TRL's GRPOTrainer rollout interface.
59
+
60
+ For each prompt in the batch:
61
+ 1. Parse the episode config embedded in prompt metadata
62
+ 2. Reset the env with that config
63
+ 3. Run the model to generate an action (JSON)
64
+ 4. Execute the action in the env
65
+ 5. Collect the final episode reward
66
+
67
+ Returns: (completions: List[str], rewards: List[float])
68
+ """
69
+
70
+ def rollout_fn(prompts: List[str], model, tokenizer) -> Tuple[List[str], List[float]]:
71
+ ...
72
+
73
+ return rollout_fn
74
+ ```
75
+
76
+ **Prompt format for the Arbitrator model:**
77
+ ```
78
+ <|system|>
79
+ You are an expert content strategist acting as an Arbitrator in a script improvement debate.
80
+ You observe a debate between a Critic and Defender about a creator's script.
81
+ You must choose exactly ONE action to improve the script.
82
+
83
+ AVAILABLE ACTIONS: hook_rewrite | section_reorder | cultural_ref_sub | cta_placement
84
+
85
+ OUTPUT FORMAT (JSON only):
86
+ {"action_type": "...", "target_section": "...", "instruction": "...", "critique_claim_id": "...", "reasoning": "..."}
87
+ <|end|>
88
+
89
+ <|user|>
90
+ CURRENT SCRIPT:
91
+ {current_script}
92
+
93
+ REGION: {region} | PLATFORM: {platform} | NICHE: {niche}
94
+
95
+ CRITIC CLAIMS:
96
+ {formatted_critic_claims}
97
+
98
+ DEFENDER RESPONSE:
99
+ Core strength: {core_strength}
100
+ Defense: {defense_argument}
101
+ Flagged claims: {flagged_claims}
102
+
103
+ CURRENT REWARDS: R1={r1:.2f} R2={r2:.2f} R3={r3} R4={r4} R5={r5}
104
+ STEP: {step_num}/{max_steps}
105
+
106
+ Choose your action:
107
+ <|end|>
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Step 3 β€” `training/train_grpo.py`
113
+
114
+ Make this runnable as both a local script and a Colab notebook cell.
115
+
116
+ ```python
117
+ """
118
+ GRPO Training β€” Viral Script Debugging Engine
119
+ TRL + Unsloth for memory-efficient training.
120
+
121
+ Local dry-run: python training/train_grpo.py --dry-run
122
+ Full training: python training/train_grpo.py --tier easy,medium --steps 200
123
+ """
124
+
125
+ from unsloth import FastLanguageModel
126
+ from trl import GRPOTrainer, GRPOConfig
127
+
128
+ def load_model(model_name: str, max_seq_length: int = 2048):
129
+ model, tokenizer = FastLanguageModel.from_pretrained(
130
+ model_name=model_name,
131
+ max_seq_length=max_seq_length,
132
+ dtype=None, # auto-detect
133
+ load_in_4bit=True,
134
+ )
135
+ model = FastLanguageModel.get_peft_model(
136
+ model,
137
+ r=16,
138
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
139
+ "gate_proj", "up_proj", "down_proj"],
140
+ lora_alpha=16,
141
+ lora_dropout=0,
142
+ bias="none",
143
+ use_gradient_checkpointing="unsloth",
144
+ random_state=42,
145
+ )
146
+ return model, tokenizer
147
+
148
+ def build_grpo_config(output_dir, num_steps, dry_run) -> GRPOConfig:
149
+ return GRPOConfig(
150
+ output_dir=output_dir,
151
+ num_train_epochs=1,
152
+ max_steps=5 if dry_run else num_steps,
153
+ per_device_train_batch_size=1 if dry_run else 4,
154
+ num_generations=4 if dry_run else 8,
155
+ gradient_accumulation_steps=4,
156
+ learning_rate=5e-6,
157
+ max_grad_norm=0.1,
158
+ warmup_ratio=0.1,
159
+ logging_steps=1,
160
+ save_steps=50,
161
+ report_to="wandb" if os.getenv("WANDB_API_KEY") else "none",
162
+ use_vllm=False,
163
+ temperature=0.8,
164
+ top_p=0.9,
165
+ max_new_tokens=256,
166
+ )
167
+ ```
168
+
169
+ **Model saving β€” CRITICAL:**
170
+ ```python
171
+ # Use save_pretrained_merged β€” NOT naive upcast from 4-bit
172
+ model.save_pretrained_merged(
173
+ f"{output_dir}/final_model",
174
+ tokenizer,
175
+ save_method="merged_16bit",
176
+ )
177
+ ```
178
+
179
+ **CLI flags:**
180
+ - `--tier` β€” comma-separated tiers: `easy`, `medium`, `hard`
181
+ - `--steps` β€” number of training steps (default: 200)
182
+ - `--dry-run` β€” run 5 steps with batch_size=1 to validate pipeline
183
+ - `--model` β€” base model (default: `unsloth/Qwen2.5-7B-Instruct-bnb-4bit`)
184
+ - `--output-dir` β€” checkpoint directory (default: `outputs/checkpoints/`)
185
+ - `--wandb` β€” enable WandB logging
186
+
187
+ ---
188
+
189
+ ## Step 4 β€” `training/reward_curves.py`
190
+
191
+ ```python
192
+ def plot_training_curves(
193
+ baseline_log_path: str = "logs/baseline_results.json",
194
+ training_log_path: str = "logs/training_results.json",
195
+ output_path: str = "logs/training_vs_baseline.png",
196
+ ):
197
+ """
198
+ Judge-facing comparison plot.
199
+ Layout: 2 rows Γ— 3 cols (R1, R2, R3, R4, R5, Total)
200
+
201
+ Per subplot:
202
+ - Grey line: baseline reward per episode
203
+ - Blue line: trained reward per episode
204
+ - Horizontal dashed line: baseline mean
205
+ - Both axes labelled
206
+
207
+ Figure title: "Trained vs Untrained Arbitrator β€” Reward Improvement"
208
+ Save PNG dpi=150. Also save PDF for README.
209
+
210
+ Print improvement summary:
211
+ R1: baseline=X.XX β†’ trained=Y.YY (+Z.ZZ)
212
+ ...
213
+ """
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Step 5 β€” `training/eval_trained_model.py`
219
+
220
+ After training: run 20 evaluation episodes with the trained model. Use the same 20 episode configs as the baseline run for a fair comparison. Save to `logs/trained_results.json`. Then call `plot_training_curves()`.
221
+
222
+ ---
223
+
224
+ ## Step 6 β€” `tests/test_training_pipeline.py`
225
+
226
+ - `build_training_prompts("easy")` returns non-empty dataset with correct prompt format
227
+ - `rollout_fn` completes one episode given a mock model returning random valid JSON
228
+ - `GRPOConfig` builds without error
229
+ - Model saving path uses `save_pretrained_merged`, not `save_pretrained`
230
+ - `plot_training_curves` generates a PNG file given valid JSON inputs
231
+
232
+ ---
233
+
234
+ ## Gate check
235
+
236
+ Run:
237
+ ```
238
+ python training/train_grpo.py --dry-run
239
+ ```
240
+
241
+ Must:
242
+ 1. Complete 5 training steps without error
243
+ 2. Print reward values for each step
244
+ 3. Show training loop is connected to the live environment (not a static dataset)
245
+ 4. Print:
246
+ ```
247
+ PHASE 3 GATE: PASS β€” Dry run complete. Training pipeline connected to live environment.
248
+ ```
249
+
250
+ **The full training run happens onsite when compute credits are available. Do not attempt to run it now.**
prompts/phase-4.md ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 4 β€” Critic Escalation Engine (Theme 4: Self-Improvement)
2
+ > Paste this entire prompt into a fresh Claude Code session. Phases 0–3 must be complete and the training dry-run passing before starting.
3
+
4
+ ---
5
+
6
+ Phases 0–3 are complete. The training pipeline is validated. Now build the self-improvement loop that satisfies Theme 4: a Critic Escalation Engine that generates harder critique challenges automatically as the Arbitrator improves.
7
+
8
+ This is what separates this submission from a standard RL environment. Every other team builds a fixed task. This environment gets harder as the agent gets better.
9
+
10
+ ---
11
+
12
+ ## Step 1 β€” `escalation/difficulty_tracker.py`
13
+
14
+ ```python
15
+ from dataclasses import dataclass, field
16
+ from typing import Dict, List, Optional
17
+ import json
18
+
19
+ @dataclass
20
+ class CritiqueClassRecord:
21
+ critique_class: str
22
+ total_episodes: int = 0
23
+ resolved_episodes: int = 0 # episodes where R4 >= 0.8
24
+ consecutive_resolutions: int = 0 # current streak
25
+ mastery_threshold: int = 3 # consecutive resolutions needed for mastery
26
+ is_mastered: bool = False
27
+ avg_r4_score: float = 0.0
28
+ last_10_r4_scores: List[float] = field(default_factory=list)
29
+
30
+ class DifficultyTracker:
31
+ CRITIQUE_CLASSES = [
32
+ "hook_weakness", "pacing_issue", "cultural_mismatch",
33
+ "cta_buried", "coherence_break", "retention_risk"
34
+ ]
35
+
36
+ def __init__(self, persistence_path: str = "logs/difficulty_tracker.json"):
37
+ # Init one CritiqueClassRecord per class
38
+ # Load from persistence_path if it exists
39
+
40
+ def record_episode(self, dominant_critique_class: str, r4_score: float, episode_id: str):
41
+ """
42
+ Update the record for the dominant critique class.
43
+ Resolved = r4_score >= 0.8.
44
+ Update consecutive_resolutions streak (reset to 0 on failure).
45
+ Set is_mastered = True at consecutive_resolutions >= mastery_threshold.
46
+ Save to disk after every update.
47
+ """
48
+
49
+ def get_next_difficulty_class(self) -> str:
50
+ """
51
+ Priority:
52
+ 1. If any class is mastered AND a harder version exists from the escalation engine β†’ return it
53
+ 2. Else β†’ class with lowest avg_r4_score that has had >= 3 episodes
54
+ 3. Fallback β†’ "hook_weakness"
55
+ """
56
+
57
+ def get_mastered_classes(self) -> List[str]:
58
+ return [k for k, v in self.records.items() if v.is_mastered]
59
+
60
+ def get_hardest_unsolved_class(self) -> str:
61
+ # Lowest avg_r4_score among non-mastered classes
62
+
63
+ def summary(self) -> dict:
64
+ # JSON-serialisable summary for logging and demo
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Step 2 β€” `escalation/critic_escalation_engine.py`
70
+
71
+ ```python
72
+ @dataclass
73
+ class EscalatedChallenge:
74
+ source_class: str # which mastered class this escalates from
75
+ script_text: str
76
+ region: str
77
+ platform: str
78
+ dominant_flaw: str
79
+ conflicting_flaw: str # the flaw that makes fixing dominant_flaw harder
80
+ why_its_harder: str # one sentence
81
+ optimal_action_order: List[str]
82
+ trap_action: str # the action that looks right but leads to worse total reward
83
+ difficulty_level: str = "self_generated"
84
+ generated_at: str = "" # ISO timestamp
85
+
86
+ class CriticEscalationEngine:
87
+ def __init__(self, model_name: str = "claude-sonnet-4-20250514"):
88
+ self.escalated_classes: Dict[str, List[EscalatedChallenge]] = {}
89
+
90
+ def escalate(
91
+ self,
92
+ mastered_class: str,
93
+ original_script_example: str,
94
+ region: str,
95
+ platform: str,
96
+ ) -> EscalatedChallenge:
97
+ """
98
+ System prompt:
99
+ You are designing training challenges for an RL agent learning to improve video scripts.
100
+ The agent has mastered detecting and fixing '{mastered_class}' flaws.
101
+
102
+ Generate a harder challenge:
103
+ 1. Create a script with a '{mastered_class}' flaw that is MORE SUBTLE than the example
104
+ 2. Add a CONFLICTING CONSTRAINT: fixing the '{mastered_class}' flaw should create or
105
+ worsen a different flaw from: {other_classes}
106
+ 3. Difficulty: HARD β€” agent must learn action ordering, not just action selection
107
+
108
+ A challenge is good when: fixing the obvious flaw first leads to WORSE total reward
109
+ than fixing a less obvious flaw first.
110
+
111
+ Return JSON only:
112
+ {
113
+ "script_text": "...",
114
+ "dominant_flaw": "...",
115
+ "conflicting_flaw": "...",
116
+ "why_its_harder": "one sentence",
117
+ "optimal_action_order": ["action1", "action2"],
118
+ "trap_action": "action that looks correct but degrades total reward"
119
+ }
120
+ """
121
+
122
+ def get_next_challenge(self, difficulty_tracker: DifficultyTracker) -> Optional[EscalatedChallenge]:
123
+ # Return next escalated challenge based on mastered classes
124
+ # Return None if no classes are mastered yet
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Step 3 β€” Wire escalation into `environment/env.py`
130
+
131
+ **Update `__init__` signature:**
132
+ ```python
133
+ def __init__(
134
+ self,
135
+ scripts_path: str = "data/test_scripts/scripts.json",
136
+ max_steps: int = 5,
137
+ difficulty: str = "easy",
138
+ use_anti_gaming: bool = True,
139
+ use_escalation: bool = True,
140
+ difficulty_tracker: Optional[DifficultyTracker] = None,
141
+ escalation_engine: Optional[CriticEscalationEngine] = None,
142
+ ):
143
+ # Create new DifficultyTracker / CriticEscalationEngine if not provided
144
+ ```
145
+
146
+ **Update `reset()`:**
147
+ 1. After existing reset logic, call `difficulty_tracker.get_mastered_classes()`
148
+ 2. If any classes are mastered: call `escalation_engine.get_next_challenge(difficulty_tracker)`
149
+ 3. If a challenge is returned: use the escalated script instead of the curriculum script
150
+ 4. Set `observation.difficulty_level = "self_generated"`
151
+ 5. Log that escalation was used (print + include in info dict)
152
+
153
+ **Update `step()`:**
154
+ After episode ends (terminated=True):
155
+ 1. Determine `dominant_critique_class` = the critique_class with the most claims in the first Critic output of this episode
156
+ 2. Call `difficulty_tracker.record_episode(dominant_critique_class, r4_score, episode_id)`
157
+
158
+ ---
159
+
160
+ ## Step 4 β€” `scripts/run_escalation_demo.py`
161
+
162
+ ```
163
+ python scripts/run_escalation_demo.py --episodes 50 --verbose
164
+ python scripts/run_escalation_demo.py --episodes 10 --verbose # for gate check
165
+ ```
166
+
167
+ Behaviour:
168
+ 1. Run N episodes with the trained model (from `outputs/checkpoints/final_model`)
169
+ 2. After each episode: log `difficulty_tracker.summary()`
170
+ 3. Print clearly when mastery is achieved for a class and when escalation first activates
171
+ 4. At the end, print a "difficulty progression" report:
172
+ - Which classes were mastered and at which episode
173
+ - How many escalated challenges were generated
174
+ - Whether escalated challenges produced lower R4 scores than the original class (proof escalation is working)
175
+
176
+ Save progression to `logs/escalation_progression.json`.
177
+
178
+ **Generate `logs/escalation_chart.png`:**
179
+ - X-axis: episode number, labelled
180
+ - Y-axis (left): difficulty score (1=easy, 2=medium, 3=hard, 4=self_generated)
181
+ - Y-axis (right): R4 score per episode
182
+ - Both overlaid on same plot
183
+ - Title: "Difficulty Progression β€” Self-Generated Curriculum"
184
+ - This chart is your Theme 4 story for judges
185
+
186
+ ---
187
+
188
+ ## Step 5 β€” `tests/test_escalation.py`
189
+
190
+ - `DifficultyTracker.record_episode()` correctly tracks consecutive resolutions
191
+ - Mastery triggers at exactly 3 consecutive resolutions, not 2
192
+ - Mastery resets if agent fails (r4 < 0.8) on a subsequent episode
193
+ - `CriticEscalationEngine.escalate()` returns valid `EscalatedChallenge` from mock LLM
194
+ - `env.reset()` uses escalated script when mastery is achieved (integration test with mocked escalation engine)
195
+ - Difficulty progression JSON is saved correctly
196
+
197
+ ---
198
+
199
+ ## Gate check
200
+
201
+ Run:
202
+ ```
203
+ python scripts/run_escalation_demo.py --episodes 10 --verbose
204
+ ```
205
+
206
+ Must:
207
+ 1. Complete 10 episodes without error
208
+ 2. Show `DifficultyTracker` updating after each episode
209
+ 3. Print escalation stats at the end
210
+ 4. Save `logs/escalation_chart.png`
211
+ 5. Print:
212
+ ```
213
+ PHASE 4 GATE: PASS β€” Escalation engine operational. {n} classes mastered. {m} escalated challenges generated.
214
+ ```
prompts/phase-5.md ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 5 β€” HF Deployment + Demo Infrastructure
2
+ > Paste this entire prompt into a fresh Claude Code session. All environment code (Phases 0–4) must be complete before starting. Training may still be pending β€” that's fine, it runs onsite.
3
+
4
+ ---
5
+
6
+ All environment code is complete. Now package everything for HuggingFace Spaces deployment and build the demo infrastructure that will carry 30% of the judging score.
7
+
8
+ ---
9
+
10
+ ## Step 1 β€” `openenv.yaml`
11
+
12
+ Create the OpenEnv manifest in the project root:
13
+
14
+ ```yaml
15
+ name: viral-script-debugging-engine
16
+ version: "1.0.0"
17
+ description: >
18
+ A multi-agent RL environment where an LLM Arbitrator learns to improve
19
+ short-form video scripts through adversarial debate. Trains with GRPO via
20
+ HuggingFace TRL + Unsloth. Hits Theme 1 (Multi-Agent) and Theme 4
21
+ (Self-Improvement) simultaneously.
22
+ themes:
23
+ - multi_agent_interactions
24
+ - self_improvement
25
+ author: "Team Name"
26
+ python_requires: ">=3.10"
27
+ entry_point: environment.env:ViralScriptEnv
28
+ reset_method: reset
29
+ step_method: step
30
+ state_method: state
31
+ reward_method: reward
32
+ tools:
33
+ - name: reset
34
+ description: "Start a new script improvement episode"
35
+ - name: step
36
+ description: "Execute one debate round: Critic attacks, Defender responds, Arbitrator acts, Rewriter executes"
37
+ - name: state
38
+ description: "Get current environment state including script, debate history, and reward components"
39
+ dependencies:
40
+ - anthropic>=0.40.0
41
+ - sentence-transformers>=2.7.0
42
+ - unsloth
43
+ - trl>=0.12.0
44
+ - numpy>=1.26.0
45
+ - pydantic>=2.0.0
46
+ - fastapi>=0.110.0
47
+ - uvicorn>=0.29.0
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Step 2 β€” `app.py` (FastAPI server for HF Spaces)
53
+
54
+ ```python
55
+ """
56
+ FastAPI wrapper exposing ViralScriptEnv as an OpenEnv-compliant HTTP server.
57
+ Deployed to HuggingFace Spaces on port 7860.
58
+ """
59
+ from fastapi import FastAPI, HTTPException
60
+ from fastapi.middleware.cors import CORSMiddleware
61
+ from pydantic import BaseModel
62
+ from environment.env import ViralScriptEnv
63
+ import uvicorn
64
+
65
+ app = FastAPI(
66
+ title="Viral Script Debugging Engine",
67
+ description="Multi-agent RL environment for improving short-form video scripts",
68
+ version="1.0.0",
69
+ )
70
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
71
+
72
+ _envs: dict = {} # per-session env instances keyed by session_id
73
+
74
+ class ResetRequest(BaseModel):
75
+ session_id: str
76
+ difficulty: str = "easy"
77
+ options: dict = {}
78
+
79
+ class StepRequest(BaseModel):
80
+ session_id: str
81
+ action: dict
82
+
83
+ @app.post("/reset")
84
+ def reset(req: ResetRequest):
85
+ env = ViralScriptEnv(difficulty=req.difficulty)
86
+ obs, info = env.reset(options=req.options)
87
+ _envs[req.session_id] = env
88
+ return {"observation": obs, "info": info}
89
+
90
+ @app.post("/step")
91
+ def step(req: StepRequest):
92
+ env = _envs.get(req.session_id)
93
+ if not env:
94
+ raise HTTPException(404, f"Session {req.session_id} not found. Call /reset first.")
95
+ obs, reward, terminated, truncated, info = env.step(req.action)
96
+ return {"observation": obs, "reward": reward, "terminated": terminated, "truncated": truncated, "info": info}
97
+
98
+ @app.get("/state/{session_id}")
99
+ def state(session_id: str):
100
+ env = _envs.get(session_id)
101
+ if not env:
102
+ raise HTTPException(404, "Session not found")
103
+ return env.state()
104
+
105
+ @app.get("/health")
106
+ def health():
107
+ return {"status": "ok", "environment": "ViralScriptDebugEngine", "version": "1.0.0"}
108
+
109
+ if __name__ == "__main__":
110
+ uvicorn.run(app, host="0.0.0.0", port=7860)
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Step 3 β€” `Dockerfile`
116
+
117
+ ```dockerfile
118
+ FROM python:3.11-slim
119
+
120
+ WORKDIR /app
121
+ COPY requirements.txt .
122
+ RUN pip install --no-cache-dir -r requirements.txt
123
+
124
+ COPY . .
125
+
126
+ EXPOSE 7860
127
+
128
+ CMD ["python", "app.py"]
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Step 4 β€” `demo/run_demo.py`
134
+
135
+ The flagship demo script β€” what you run during the pitch. Must tell a 5-act story with `rich` terminal output.
136
+
137
+ ```
138
+ python demo/run_demo.py --script S03 --compare # base vs trained side-by-side
139
+ python demo/run_demo.py --interactive # human acts as Arbitrator
140
+ ```
141
+
142
+ **Act 1 β€” "The Raw Script"**
143
+ - Display original script in a `rich` Panel
144
+ - Show: region, platform, niche, known flaws
145
+
146
+ **Act 2 β€” "The Critic Attacks"**
147
+ - Run Critic on the script
148
+ - Display each `CritiqueClaim` as a numbered panel, colour-coded by severity (red=high, yellow=medium, green=low)
149
+ - 2-second pause between claims for dramatic effect
150
+
151
+ **Act 3 β€” "The Defender Responds"**
152
+ - Run Defender
153
+ - Display `core_strength` in a highlighted box: "WHAT WE MUST PROTECT"
154
+ - Show each `flagged_critic_claims` entry with "⚠ Defender flagged this as overcorrection"
155
+
156
+ **Act 4 β€” "The Arbitrator Decides"** (both shown when `--compare`)
157
+ - Grey panel: "Untrained Arbitrator chose: [action] β€” Reasoning: [reasoning]"
158
+ - Blue panel: "Trained Arbitrator chose: [action] β€” Reasoning: [reasoning]"
159
+ - Highlight the difference in reasoning
160
+
161
+ **Act 5 β€” "The Rewrite + Reward"**
162
+ - Show rewritten script as unified diff (coloured: green=added, red=removed)
163
+ - Show reward components as a progress-bar table:
164
+ ```
165
+ R1 Hook Strength β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘ 0.75
166
+ R2 Coherence β–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘ 0.60
167
+ R3 Cultural β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘ 0.85
168
+ R4 Resolution β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘ 0.70
169
+ R5 Preservation β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘ 0.75
170
+ ─────────────────────────────────
171
+ Total β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘ 0.73 (+34% vs baseline)
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Step 5 β€” `README.md`
177
+
178
+ Write the complete README with this exact structure:
179
+
180
+ ```markdown
181
+ # Viral Script Debugging Engine
182
+ ### Meta Γ— OpenEnv Hackathon 2026 | Theme 1: Multi-Agent Β· Theme 4: Self-Improvement
183
+
184
+ ## The Problem
185
+ [2 paragraphs: 95% of creators never break 10k. Existing tools are one-shot pipelines, not RL.]
186
+
187
+ ## What We Built
188
+ [2 paragraphs: the multi-agent RL loop. NOT a content generator. A reasoning system.]
189
+
190
+ ## How It Works
191
+ [Describe the 4-step loop: Critic β†’ Defender β†’ Arbitrator β†’ Rewriter. One episode = one trajectory.]
192
+
193
+ ## Environment API
194
+ [Code block showing reset(), step(), state(), reward() with example inputs/outputs]
195
+
196
+ ## Reward Functions
197
+ [Table of R1–R5: what each measures and how it's computed]
198
+
199
+ ## Anti-Gaming Protections
200
+ [Explain the two rules: catastrophic drop penalty and action diversity penalty]
201
+ [Show 2–3 real examples from training logs where penalties fired]
202
+
203
+ ## Self-Improvement Loop (Theme 4)
204
+ [Explain the Critic Escalation Engine and Difficulty Tracker]
205
+ ![Escalation chart](logs/escalation_chart.png)
206
+
207
+ ## Training
208
+ Model: Qwen2.5-7B-Instruct | Algorithm: GRPO via TRL + Unsloth
209
+ [Link to Colab notebook]
210
+
211
+ ## Results
212
+ ![Reward improvement](logs/training_vs_baseline.png)
213
+ [Table: per-reward improvement, baseline vs trained]
214
+
215
+ ## Why This Matters for Meta
216
+ [One paragraph: the Meta business case]
217
+
218
+ ## HuggingFace Space
219
+ [Link: huggingface.co/spaces/YOUR_TEAM/viral-script-debugging-engine]
220
+
221
+ ## References
222
+ [Links to mini-blog, video demo, Colab notebook]
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Step 6 β€” `notebooks/training_colab.ipynb`
228
+
229
+ Generate a Colab-ready notebook with these cells in order:
230
+
231
+ ```python
232
+ # Cell 1 β€” Install
233
+ !pip install unsloth trl anthropic sentence-transformers openenv pydantic rich
234
+
235
+ # Cell 2 β€” API key
236
+ import os
237
+ os.environ["ANTHROPIC_API_KEY"] = "YOUR_KEY_HERE"
238
+
239
+ # Cell 3 β€” Dry-run to validate pipeline
240
+ !python training/train_grpo.py --dry-run --steps 5
241
+
242
+ # Cell 4 β€” Full training
243
+ !python training/train_grpo.py --tier easy,medium --steps 200 --model unsloth/Qwen2.5-7B-Instruct-bnb-4bit
244
+
245
+ # Cell 5 β€” Evaluate and plot
246
+ !python training/eval_trained_model.py
247
+
248
+ # Cell 6 β€” Display reward curves inline
249
+ from IPython.display import Image
250
+ Image("logs/training_vs_baseline.png")
251
+
252
+ # Cell 7 β€” Run demo
253
+ !python demo/run_demo.py --script S03 --compare
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Step 7 β€” `scripts/submission_check.py`
259
+
260
+ ```
261
+ python scripts/submission_check.py
262
+ ```
263
+
264
+ Prints PASS or FAIL for each requirement:
265
+
266
+ - `openenv.yaml` exists and parses without error
267
+ - `app.py` starts without error (test with a 3-second subprocess timeout)
268
+ - README contains `huggingface.co/spaces` link
269
+ - `logs/baseline_reward_curves.png` exists
270
+ - `logs/training_vs_baseline.png` exists
271
+ - `logs/escalation_chart.png` exists
272
+ - `notebooks/training_colab.ipynb` exists
273
+ - README contains all required sections (The Problem, What We Built, Reward Functions, Anti-Gaming, Results)
274
+ - `requirements.txt` is complete
275
+ - All tests pass (`pytest` exit code 0)
276
+
277
+ Final output: `SUBMISSION READY βœ“` or `SUBMISSION INCOMPLETE β€” fix the above before submitting`
278
+
279
+ ---
280
+
281
+ ## Gate check
282
+
283
+ Run:
284
+ ```
285
+ python scripts/submission_check.py
286
+ ```
287
+
288
+ All 10 checks must print PASS. Fix any failures before considering this phase done.
289
+
290
+ Then run the demo end-to-end once to confirm it tells the full 5-act story without errors:
291
+ ```
292
+ python demo/run_demo.py --script S03 --compare
293
+ ```
prompts/phase-index.md ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claude Code Prompts β€” Index
2
+ ## Viral Script Debugging Engine Β· Meta Γ— OpenEnv Hackathon
3
+
4
+ ---
5
+
6
+ ## How to use these files
7
+
8
+ Each file is a standalone prompt. Open a **fresh Claude Code session** for each phase and paste the entire file contents. Do not trim or summarise β€” Claude Code needs the full context.
9
+
10
+ **Do not open the next phase until the gate check at the bottom of the current phase prints PASS.**
11
+
12
+ ---
13
+
14
+ ## Files
15
+
16
+ | File | Phase | What it builds | Gate command |
17
+ |---|---|---|---|
18
+ | `phase_0_critic_gate.md` | Phase 0 | Critic agent + evaluation harness + 10 test scripts | `python scripts/run_critic_gate.py --dry-run` |
19
+ | `phase_1_openenv_scaffold.md` | Phase 1 | OpenEnv env scaffold + R1/R2 rewards + Rewriter | `python scripts/run_dummy_episode.py --difficulty easy --steps 3 --verbose` |
20
+ | `phase_2_defender_rewards_baseline.md` | Phase 2 | Defender + R3/R4/R5 + anti-gaming logging + baseline curves | `python scripts/run_baseline.py` |
21
+ | `phase_3_curriculum_grpo_training.md` | Phase 3 | Curriculum datasets + GRPO training pipeline | `python training/train_grpo.py --dry-run` |
22
+ | `phase_4_escalation_engine.md` | Phase 4 | Difficulty Tracker + Critic Escalation Engine | `python scripts/run_escalation_demo.py --episodes 10 --verbose` |
23
+ | `phase_5_deployment_demo.md` | Phase 5 | FastAPI server + Dockerfile + demo script + README | `python scripts/submission_check.py` |
24
+
25
+ ---
26
+
27
+ ## Full file structure after all phases
28
+
29
+ ```
30
+ viral_script_engine/
31
+ β”œβ”€β”€ agents/
32
+ β”‚ β”œβ”€β”€ critic.py # Phase 0
33
+ β”‚ β”œβ”€β”€ defender.py # Phase 2
34
+ β”‚ β”œβ”€β”€ rewriter.py # Phase 1
35
+ β”‚ └── baseline_arbitrator.py # Phase 2
36
+ β”œβ”€β”€ data/
37
+ β”‚ β”œβ”€β”€ test_scripts/scripts.json # Phase 0
38
+ β”‚ β”œβ”€β”€ golden_fixtures/ # Phase 0
39
+ β”‚ β”œβ”€β”€ cultural_kb.json # Phase 2
40
+ β”‚ └── curriculum/ # Phase 3
41
+ β”‚ β”œβ”€β”€ easy_tier.jsonl
42
+ β”‚ β”œβ”€β”€ medium_tier.jsonl
43
+ β”‚ β”œβ”€β”€ hard_tier.jsonl
44
+ β”‚ └── synthetic_scripts.json
45
+ β”œβ”€β”€ environment/
46
+ β”‚ β”œβ”€β”€ env.py # Phase 1 (updated Phase 2, 4)
47
+ β”‚ β”œβ”€β”€ actions.py # Phase 1
48
+ β”‚ β”œβ”€β”€ observations.py # Phase 1
49
+ β”‚ └── episode_state.py # Phase 1
50
+ β”œβ”€β”€ escalation/
51
+ β”‚ β”œβ”€β”€ difficulty_tracker.py # Phase 4
52
+ β”‚ └── critic_escalation_engine.py # Phase 4
53
+ β”œβ”€β”€ evaluation/
54
+ β”‚ └── critic_evaluator.py # Phase 0
55
+ β”œβ”€β”€ rewards/
56
+ β”‚ β”œβ”€β”€ base.py # Phase 1
57
+ β”‚ β”œβ”€β”€ r1_hook_strength.py # Phase 1
58
+ β”‚ β”œβ”€β”€ r2_coherence.py # Phase 1
59
+ β”‚ β”œβ”€β”€ r3_cultural_alignment.py # Phase 2
60
+ β”‚ β”œβ”€β”€ r4_debate_resolution.py # Phase 2
61
+ β”‚ β”œβ”€β”€ r5_defender_preservation.py # Phase 2
62
+ β”‚ └── reward_aggregator.py # Phase 1 (updated Phase 2)
63
+ β”œβ”€β”€ training/
64
+ β”‚ β”œβ”€β”€ rollout_function.py # Phase 3
65
+ β”‚ β”œβ”€β”€ train_grpo.py # Phase 3
66
+ β”‚ β”œβ”€β”€ eval_trained_model.py # Phase 3
67
+ β”‚ └── reward_curves.py # Phase 3
68
+ β”œβ”€β”€ demo/
69
+ β”‚ └── run_demo.py # Phase 5
70
+ β”œβ”€β”€ scripts/
71
+ β”‚ β”œβ”€β”€ run_critic_gate.py # Phase 0
72
+ β”‚ β”œβ”€β”€ run_dummy_episode.py # Phase 1
73
+ β”‚ β”œβ”€β”€ run_baseline.py # Phase 2
74
+ β”‚ β”œβ”€β”€ run_escalation_demo.py # Phase 4
75
+ β”‚ └── submission_check.py # Phase 5
76
+ β”œβ”€β”€ tests/
77
+ β”‚ β”œβ”€β”€ test_critic.py # Phase 0
78
+ β”‚ β”œβ”€β”€ test_environment.py # Phase 1
79
+ β”‚ β”œβ”€β”€ test_rewards.py # Phase 1
80
+ β”‚ β”œβ”€β”€ test_phase2.py # Phase 2
81
+ β”‚ β”œβ”€β”€ test_training_pipeline.py # Phase 3
82
+ β”‚ └── test_escalation.py # Phase 4
83
+ β”œβ”€β”€ notebooks/
84
+ β”‚ └── training_colab.ipynb # Phase 5
85
+ β”œβ”€β”€ logs/ # generated at runtime
86
+ β”œβ”€β”€ outputs/ # training checkpoints
87
+ β”œβ”€β”€ app.py # Phase 5
88
+ β”œβ”€β”€ openenv.yaml # Phase 5
89
+ β”œβ”€β”€ Dockerfile # Phase 5
90
+ β”œβ”€β”€ requirements.txt # Phase 0
91
+ └── README.md # Phase 5
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Key constraints to keep in mind across all phases
97
+
98
+ - Use the **Anthropic Python SDK** only (not OpenAI)
99
+ - All models/dataclasses use **Pydantic** for validation
100
+ - LLM calls only in: CriticAgent, DefenderAgent, RewriterAgent, BaselineArbitratorAgent, CriticEscalationEngine
101
+ - Evaluators and reward scorers (R1, R3) are **purely rule-based β€” zero LLM calls**
102
+ - Store API key in `.env`, load with `python-dotenv`
103
+ - Use `rich` for all console output
104
+ - Mock all Anthropic API calls in tests β€” no real API calls in the test suite
105
+ - Model saving: always use `save_pretrained_merged`, never naive upcast from 4-bit
scripts/deploy.sh ADDED
File without changes
scripts/dev.sh ADDED
File without changes
scripts/test.sh ADDED
File without changes
session/context.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Context β€” Carry Over for Next Session
2
+
3
+ ## Purpose
4
+ Read this file at every session start after index.md and phase-log.md.
5
+ Contains only what Claude needs to resume without re-reading everything.
6
+ Overwrite when context changes. Keep it minimal and current.
7
+
8
+ ---
9
+
10
+ ## Current Phase
11
+ Phase: [number]
12
+ Prompt file: prompts/phase-X.md
13
+ Status: [in progress / complete / blocked]
14
+
15
+ ---
16
+
17
+ ## Currently Working On
18
+ Feature: [name]
19
+ File(s): [list]
20
+ Status: [what is done, what is not]
21
+
22
+ ---
23
+
24
+ ## Open Questions
25
+
26
+ [question that needs user input before proceeding]
27
+ [question that needs user input before proceeding]
28
+
29
+
30
+ ---
31
+
32
+ ## Known Blockers
33
+
34
+ [what is blocked and why]
35
+
36
+
37
+ ---
38
+
39
+ ## Last Commit Message
40
+ [most recent commit message generated]
41
+
42
+ ---
43
+
44
+ ## Do Not Forget
45
+
46
+ [critical thing to remember for next session]
47
+ [critical thing to remember for next session]
48
+
49
+
50
+ ---
51
+
52
+ ## Rules for This File
53
+ - Keep this file under 30 lines always
54
+ - Overwrite at end of every session
55
+ - Only include what is immediately needed to resume
56
+ - Do not include explanations β€” only facts and state
session/phase-log.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase Log β€” Full History
2
+
3
+ ## Purpose
4
+ Read this file at every session start to know overall progress.
5
+ One line per phase or significant milestone. Never overwrite β€” only append.
6
+ This is the only file that keeps permanent history across all sessions.
7
+
8
+ ---
9
+
10
+ ## Format
11
+ [YYYY-MM-DD] [Phase X] [status] β€” [one liner of what happened]
12
+
13
+ ## Status Tags
14
+ STARTED β€” phase work has begun
15
+ PARTIAL β€” some features done, phase not complete
16
+ COMPLETE β€” all features done, all tests passing
17
+ BLOCKED β€” cannot proceed, reason in line
18
+ ROLLED BACK β€” changes reverted, reason in line
19
+
20
+ ---
21
+
22
+ ## Log
23
+ [YYYY-MM-DD] [Phase 1] STARTED β€” project scaffolding begun
24
+
25
+ ---
26
+
27
+ ## Rules for This File
28
+ - Never delete or overwrite any line
29
+ - Append only β€” one line per session or milestone
30
+ - Keep each line under 15 words after the date and phase tag
31
+ - This file is the single source of truth for project history
session/summary.md ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Session Summary β€” Last Session Record
2
+
3
+ ## Purpose
4
+ Read this file only if context.md is unclear or incomplete.
5
+ Overwrite this file at the end of every session.
6
+ One session = one summary. Previous summaries live in phase-log.md.
7
+
8
+ ---
9
+
10
+ ## Last Session
11
+
12
+ ### Date
13
+ [YYYY-MM-DD]
14
+
15
+ ### Phase
16
+ [Phase number and name]
17
+
18
+ ### What Was Done
19
+
20
+ [one liner]
21
+ [one liner]
22
+ [one liner]
23
+
24
+
25
+ ### What Was NOT Done (carry over)
26
+
27
+ [one liner]
28
+ [one liner]
29
+
30
+
31
+ ### Errors Encountered
32
+
33
+ [file:function] β€” [reason] β€” [how it was fixed]
34
+
35
+
36
+ ### Tests Status
37
+ Total: 0 | Passed: 0 | Failed: 0
38
+
39
+ ### Commit Messages Generated
40
+
41
+ [commit message]
42
+ [commit message]
43
+
44
+
45
+ ### Notes for Next Session
46
+
47
+ [one liner]
48
+ [one liner]
49
+
50
+
51
+ ---
52
+
53
+ ## Rules for This File
54
+ - Overwrite at end of every session β€” do not append
55
+ - Keep every section to one liners only
56
+ - Move key notes to context.md if needed next session
57
+ - Full phase history lives in phase-log.md not here