Enhanced code review and frontend validation with intelligent triggers: Code Review Agent Enhancement: - Added Sequential Thinking MCP integration for complex issues - Triggers on 2+ rejections or 3+ critical issues - New escalation format with root cause analysis - Comprehensive solution strategies with trade-off evaluation - Educational feedback to break rejection cycles - Files: .claude/agents/code-review.md (+308 lines) - Docs: CODE_REVIEW_ST_ENHANCEMENT.md, CODE_REVIEW_ST_TESTING.md Frontend Design Skill Enhancement: - Automatic invocation for ANY UI change - Comprehensive validation checklist (200+ checkpoints) - 8 validation categories (visual, interactive, responsive, a11y, etc.) - 3 validation levels (quick, standard, comprehensive) - Integration with code review workflow - Files: .claude/skills/frontend-design/SKILL.md (+120 lines) - Docs: UI_VALIDATION_CHECKLIST.md (462 lines), AUTOMATIC_VALIDATION_ENHANCEMENT.md (587 lines) Settings Optimization: - Repaired .claude/settings.local.json (fixed m365 pattern) - Reduced permissions from 49 to 33 (33% reduction) - Removed duplicates, sorted alphabetically - Created SETTINGS_PERMISSIONS.md documentation Checkpoint Command Enhancement: - Dual checkpoint system (git + database) - Saves session context to API for cross-machine recall - Includes git metadata in database context - Files: .claude/commands/checkpoint.md (+139 lines) Decision Rationale: - Sequential Thinking MCP breaks rejection cycles by identifying root causes - Automatic frontend validation catches UI issues before code review - Dual checkpoints enable complete project memory across machines - Settings optimization improves maintainability Total: 1,200+ lines of documentation and enhancements Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
565 lines
23 KiB
Plaintext
565 lines
23 KiB
Plaintext
1→"""
|
|
2→Permissions Router
|
|
3→==================
|
|
4→
|
|
5→API endpoints for permission management within projects.
|
|
6→"""
|
|
7→
|
|
8→from datetime import datetime
|
|
9→from typing import Optional, List
|
|
10→
|
|
11→from fastapi import APIRouter, Depends, HTTPException, status
|
|
12→from pydantic import BaseModel, Field
|
|
13→from sqlalchemy import select, and_
|
|
14→from sqlalchemy.ext.asyncio import AsyncSession
|
|
15→
|
|
16→from ..database import get_db
|
|
17→from ..models import Project, Permission, PermissionRequest
|
|
18→
|
|
19→router = APIRouter(prefix="/api/projects", tags=["permissions"])
|
|
20→
|
|
21→
|
|
22→# ============================================================================
|
|
23→# Pydantic Schemas
|
|
24→# ============================================================================
|
|
25→
|
|
26→class PermissionResponse(BaseModel):
|
|
27→ """Schema for permission response."""
|
|
28→ id: int
|
|
29→ project_id: int
|
|
30→ permission_type: str
|
|
31→ permission_value: str
|
|
32→ granted: bool
|
|
33→ created_at: datetime
|
|
34→
|
|
35→ class Config:
|
|
36→ from_attributes = True
|
|
37→
|
|
38→
|
|
39→class PermissionListResponse(BaseModel):
|
|
40→ """Schema for permission list response."""
|
|
41→ permissions: List[PermissionResponse]
|
|
42→ count: int
|
|
43→
|
|
44→
|
|
45→class PermissionCreate(BaseModel):
|
|
46→ """Schema for creating a permission."""
|
|
47→ permission_type: str = Field(..., min_length=1, max_length=50)
|
|
48→ permission_value: str = Field(..., min_length=1, max_length=500)
|
|
49→ granted: bool = True
|
|
50→
|
|
51→
|
|
52→class PermissionUpdate(BaseModel):
|
|
53→ """Schema for updating a permission."""
|
|
54→ granted: Optional[bool] = None
|
|
55→
|
|
56→
|
|
57→class PermissionRequestResponse(BaseModel):
|
|
58→ """Schema for permission request response."""
|
|
59→ id: int
|
|
60→ project_id: int
|
|
61→ feature_id: Optional[int]
|
|
62→ agent_id: str
|
|
63→ permission_type: str
|
|
64→ permission_value: str
|
|
65→ status: str
|
|
66→ requested_at: datetime
|
|
67→ resolved_at: Optional[datetime]
|
|
68→
|
|
69→ class Config:
|
|
70→ from_attributes = True
|
|
71→
|
|
72→
|
|
73→class PermissionRequestListResponse(BaseModel):
|
|
74→ """Schema for permission request list response."""
|
|
75→ requests: List[PermissionRequestResponse]
|
|
76→ count: int
|
|
77→
|
|
78→
|
|
79→class PermissionRequestCreate(BaseModel):
|
|
80→ """Schema for creating a permission request."""
|
|
81→ agent_id: str = Field(..., min_length=1, max_length=1) # "A" or "B"
|
|
82→ permission_type: str = Field(..., min_length=1, max_length=50)
|
|
83→ permission_value: str = Field(..., min_length=1, max_length=500)
|
|
84→ feature_id: Optional[int] = None
|
|
85→
|
|
86→
|
|
87→class PermissionImportItem(BaseModel):
|
|
88→ """Schema for a single permission in import."""
|
|
89→ permission_type: str = Field(..., min_length=1, max_length=50)
|
|
90→ permission_value: str = Field(..., min_length=1, max_length=500)
|
|
91→ granted: bool = True
|
|
92→
|
|
93→
|
|
94→class PermissionImportRequest(BaseModel):
|
|
95→ """Schema for importing permissions from configuration."""
|
|
96→ permissions: List[PermissionImportItem]
|
|
97→ replace_existing: bool = False # If true, delete all existing permissions first
|
|
98→
|
|
99→
|
|
100→class PermissionImportResponse(BaseModel):
|
|
101→ """Schema for import response."""
|
|
102→ imported: int
|
|
103→ skipped: int
|
|
104→ message: str
|
|
105→
|
|
106→
|
|
107→class PermissionExportItem(BaseModel):
|
|
108→ """Schema for a single permission in export."""
|
|
109→ permission_type: str
|
|
110→ permission_value: str
|
|
111→ granted: bool
|
|
112→
|
|
113→
|
|
114→class PermissionExportResponse(BaseModel):
|
|
115→ """Schema for export response."""
|
|
116→ project_name: str
|
|
117→ permissions: List[PermissionExportItem]
|
|
118→ count: int
|
|
119→
|
|
120→
|
|
121→# ============================================================================
|
|
122→# Permission Endpoints
|
|
123→# ============================================================================
|
|
124→
|
|
125→@router.get("/{name}/permissions", response_model=PermissionListResponse)
|
|
126→async def list_permissions(name: str, db: AsyncSession = Depends(get_db)):
|
|
127→ """List all permissions for a project."""
|
|
128→ # Verify project exists
|
|
129→ result = await db.execute(select(Project).where(Project.name == name))
|
|
130→ project = result.scalar_one_or_none()
|
|
131→
|
|
132→ if not project:
|
|
133→ raise HTTPException(
|
|
134→ status_code=status.HTTP_404_NOT_FOUND,
|
|
135→ detail=f"Project '{name}' not found"
|
|
136→ )
|
|
137→
|
|
138→ # Get permissions
|
|
139→ result = await db.execute(
|
|
140→ select(Permission)
|
|
141→ .where(Permission.project_id == project.id)
|
|
142→ .order_by(Permission.created_at.desc())
|
|
143→ )
|
|
144→ permissions = result.scalars().all()
|
|
145→
|
|
146→ return PermissionListResponse(
|
|
147→ permissions=[PermissionResponse.model_validate(p) for p in permissions],
|
|
148→ count=len(permissions)
|
|
149→ )
|
|
150→
|
|
151→
|
|
152→@router.post("/{name}/permissions", response_model=PermissionResponse, status_code=status.HTTP_201_CREATED)
|
|
153→async def create_permission(name: str, permission: PermissionCreate, db: AsyncSession = Depends(get_db)):
|
|
154→ """Create a new permission for a project."""
|
|
155→ # Verify project exists
|
|
156→ result = await db.execute(select(Project).where(Project.name == name))
|
|
157→ project = result.scalar_one_or_none()
|
|
158→
|
|
159→ if not project:
|
|
160→ raise HTTPException(
|
|
161→ status_code=status.HTTP_404_NOT_FOUND,
|
|
162→ detail=f"Project '{name}' not found"
|
|
163→ )
|
|
164→
|
|
165→ # Check if permission already exists
|
|
166→ result = await db.execute(
|
|
167→ select(Permission).where(and_(
|
|
168→ Permission.project_id == project.id,
|
|
169→ Permission.permission_type == permission.permission_type,
|
|
170→ Permission.permission_value == permission.permission_value
|
|
171→ ))
|
|
172→ )
|
|
173→ existing = result.scalar_one_or_none()
|
|
174→ if existing:
|
|
175→ raise HTTPException(
|
|
176→ status_code=status.HTTP_409_CONFLICT,
|
|
177→ detail=f"Permission already exists"
|
|
178→ )
|
|
179→
|
|
180→ # Create permission
|
|
181→ db_permission = Permission(
|
|
182→ project_id=project.id,
|
|
183→ permission_type=permission.permission_type,
|
|
184→ permission_value=permission.permission_value,
|
|
185→ granted=permission.granted,
|
|
186→ )
|
|
187→ db.add(db_permission)
|
|
188→ await db.commit()
|
|
189→ await db.refresh(db_permission)
|
|
190→
|
|
191→ return PermissionResponse.model_validate(db_permission)
|
|
192→
|
|
193→
|
|
194→# ============================================================================
|
|
195→# Permission Import/Export Endpoints (must be before /{permission_id} routes)
|
|
196→# ============================================================================
|
|
197→
|
|
198→@router.get("/{name}/permissions/export", response_model=PermissionExportResponse)
|
|
199→async def export_permissions(name: str, db: AsyncSession = Depends(get_db)):
|
|
200→ """Export permissions to a configuration format."""
|
|
201→ # Verify project exists
|
|
202→ result = await db.execute(select(Project).where(Project.name == name))
|
|
203→ project = result.scalar_one_or_none()
|
|
204→
|
|
205→ if not project:
|
|
206→ raise HTTPException(
|
|
207→ status_code=status.HTTP_404_NOT_FOUND,
|
|
208→ detail=f"Project '{name}' not found"
|
|
209→ )
|
|
210→
|
|
211→ # Get all permissions
|
|
212→ result = await db.execute(
|
|
213→ select(Permission)
|
|
214→ .where(Permission.project_id == project.id)
|
|
215→ .order_by(Permission.permission_type, Permission.permission_value)
|
|
216→ )
|
|
217→ permissions = result.scalars().all()
|
|
218→
|
|
219→ return PermissionExportResponse(
|
|
220→ project_name=name,
|
|
221→ permissions=[
|
|
222→ PermissionExportItem(
|
|
223→ permission_type=p.permission_type,
|
|
224→ permission_value=p.permission_value,
|
|
225→ granted=p.granted
|
|
226→ )
|
|
227→ for p in permissions
|
|
228→ ],
|
|
229→ count=len(permissions)
|
|
230→ )
|
|
231→
|
|
232→
|
|
233→@router.post("/{name}/permissions/import", response_model=PermissionImportResponse)
|
|
234→async def import_permissions(name: str, import_data: PermissionImportRequest, db: AsyncSession = Depends(get_db)):
|
|
235→ """Import permissions from a configuration file."""
|
|
236→ # Verify project exists
|
|
237→ result = await db.execute(select(Project).where(Project.name == name))
|
|
238→ project = result.scalar_one_or_none()
|
|
239→
|
|
240→ if not project:
|
|
241→ raise HTTPException(
|
|
242→ status_code=status.HTTP_404_NOT_FOUND,
|
|
243→ detail=f"Project '{name}' not found"
|
|
244→ )
|
|
245→
|
|
246→ imported = 0
|
|
247→ skipped = 0
|
|
248→
|
|
249→ # If replace_existing is true, delete all existing permissions first
|
|
250→ if import_data.replace_existing:
|
|
251→ existing_result = await db.execute(
|
|
252→ select(Permission).where(Permission.project_id == project.id)
|
|
253→ )
|
|
254→ existing_permissions = existing_result.scalars().all()
|
|
255→ for perm in existing_permissions:
|
|
256→ await db.delete(perm)
|
|
257→
|
|
258→ # Import each permission
|
|
259→ for perm in import_data.permissions:
|
|
260→ # Check if permission already exists (skip duplicates)
|
|
261→ result = await db.execute(
|
|
262→ select(Permission).where(and_(
|
|
263→ Permission.project_id == project.id,
|
|
264→ Permission.permission_type == perm.permission_type,
|
|
265→ Permission.permission_value == perm.permission_value
|
|
266→ ))
|
|
267→ )
|
|
268→ existing = result.scalar_one_or_none()
|
|
269→
|
|
270→ if existing:
|
|
271→ skipped += 1
|
|
272→ continue
|
|
273→
|
|
274→ # Create new permission
|
|
275→ db_permission = Permission(
|
|
276→ project_id=project.id,
|
|
277→ permission_type=perm.permission_type,
|
|
278→ permission_value=perm.permission_value,
|
|
279→ granted=perm.granted,
|
|
280→ )
|
|
281→ db.add(db_permission)
|
|
282→ imported += 1
|
|
283→
|
|
284→ await db.commit()
|
|
285→
|
|
286→ return PermissionImportResponse(
|
|
287→ imported=imported,
|
|
288→ skipped=skipped,
|
|
289→ message=f"Successfully imported {imported} permissions ({skipped} skipped as duplicates)"
|
|
290→ )
|
|
291→
|
|
292→
|
|
293→# ============================================================================
|
|
294→# Permission ID Endpoints
|
|
295→# ============================================================================
|
|
296→
|
|
297→@router.get("/{name}/permissions/{permission_id}", response_model=PermissionResponse)
|
|
298→async def get_permission(name: str, permission_id: int, db: AsyncSession = Depends(get_db)):
|
|
299→ """Get a specific permission by ID."""
|
|
300→ # Verify project exists
|
|
301→ result = await db.execute(select(Project).where(Project.name == name))
|
|
302→ project = result.scalar_one_or_none()
|
|
303→
|
|
304→ if not project:
|
|
305→ raise HTTPException(
|
|
306→ status_code=status.HTTP_404_NOT_FOUND,
|
|
307→ detail=f"Project '{name}' not found"
|
|
308→ )
|
|
309→
|
|
310→ # Get permission
|
|
311→ result = await db.execute(
|
|
312→ select(Permission).where(and_(
|
|
313→ Permission.id == permission_id,
|
|
314→ Permission.project_id == project.id
|
|
315→ ))
|
|
316→ )
|
|
317→ permission = result.scalar_one_or_none()
|
|
318→
|
|
319→ if not permission:
|
|
320→ raise HTTPException(
|
|
321→ status_code=status.HTTP_404_NOT_FOUND,
|
|
322→ detail=f"Permission {permission_id} not found in project '{name}'"
|
|
323→ )
|
|
324→
|
|
325→ return PermissionResponse.model_validate(permission)
|
|
326→
|
|
327→
|
|
328→@router.put("/{name}/permissions/{permission_id}", response_model=PermissionResponse)
|
|
329→async def update_permission(name: str, permission_id: int, permission_update: PermissionUpdate, db: AsyncSession = Depends(get_db)):
|
|
330→ """Update a permission."""
|
|
331→ # Verify project exists
|
|
332→ result = await db.execute(select(Project).where(Project.name == name))
|
|
333→ project = result.scalar_one_or_none()
|
|
334→
|
|
335→ if not project:
|
|
336→ raise HTTPException(
|
|
337→ status_code=status.HTTP_404_NOT_FOUND,
|
|
338→ detail=f"Project '{name}' not found"
|
|
339→ )
|
|
340→
|
|
341→ # Get permission
|
|
342→ result = await db.execute(
|
|
343→ select(Permission).where(and_(
|
|
344→ Permission.id == permission_id,
|
|
345→ Permission.project_id == project.id
|
|
346→ ))
|
|
347→ )
|
|
348→ permission = result.scalar_one_or_none()
|
|
349→
|
|
350→ if not permission:
|
|
351→ raise HTTPException(
|
|
352→ status_code=status.HTTP_404_NOT_FOUND,
|
|
353→ detail=f"Permission {permission_id} not found in project '{name}'"
|
|
354→ )
|
|
355→
|
|
356→ # Update granted status
|
|
357→ if permission_update.granted is not None:
|
|
358→ permission.granted = permission_update.granted
|
|
359→
|
|
360→ await db.commit()
|
|
361→ await db.refresh(permission)
|
|
362→
|
|
363→ return PermissionResponse.model_validate(permission)
|
|
364→
|
|
365→
|
|
366→@router.delete("/{name}/permissions/{permission_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
367→async def delete_permission(name: str, permission_id: int, db: AsyncSession = Depends(get_db)):
|
|
368→ """Delete a permission."""
|
|
369→ # Verify project exists
|
|
370→ result = await db.execute(select(Project).where(Project.name == name))
|
|
371→ project = result.scalar_one_or_none()
|
|
372→
|
|
373→ if not project:
|
|
374→ raise HTTPException(
|
|
375→ status_code=status.HTTP_404_NOT_FOUND,
|
|
376→ detail=f"Project '{name}' not found"
|
|
377→ )
|
|
378→
|
|
379→ # Get permission
|
|
380→ result = await db.execute(
|
|
381→ select(Permission).where(and_(
|
|
382→ Permission.id == permission_id,
|
|
383→ Permission.project_id == project.id
|
|
384→ ))
|
|
385→ )
|
|
386→ permission = result.scalar_one_or_none()
|
|
387→
|
|
388→ if not permission:
|
|
389→ raise HTTPException(
|
|
390→ status_code=status.HTTP_404_NOT_FOUND,
|
|
391→ detail=f"Permission {permission_id} not found in project '{name}'"
|
|
392→ )
|
|
393→
|
|
394→ await db.delete(permission)
|
|
395→ await db.commit()
|
|
396→
|
|
397→
|
|
398→# ============================================================================
|
|
399→# Permission Request Endpoints
|
|
400→# ============================================================================
|
|
401→
|
|
402→@router.get("/{name}/permission-requests", response_model=PermissionRequestListResponse)
|
|
403→async def list_permission_requests(
|
|
404→ name: str,
|
|
405→ status_filter: Optional[str] = None,
|
|
406→ db: AsyncSession = Depends(get_db)
|
|
407→):
|
|
408→ """List permission requests for a project."""
|
|
409→ # Verify project exists
|
|
410→ result = await db.execute(select(Project).where(Project.name == name))
|
|
411→ project = result.scalar_one_or_none()
|
|
412→
|
|
413→ if not project:
|
|
414→ raise HTTPException(
|
|
415→ status_code=status.HTTP_404_NOT_FOUND,
|
|
416→ detail=f"Project '{name}' not found"
|
|
417→ )
|
|
418→
|
|
419→ # Build query
|
|
420→ query = select(PermissionRequest).where(PermissionRequest.project_id == project.id)
|
|
421→
|
|
422→ if status_filter:
|
|
423→ query = query.where(PermissionRequest.status == status_filter)
|
|
424→
|
|
425→ query = query.order_by(PermissionRequest.requested_at.desc())
|
|
426→
|
|
427→ result = await db.execute(query)
|
|
428→ requests = result.scalars().all()
|
|
429→
|
|
430→ return PermissionRequestListResponse(
|
|
431→ requests=[PermissionRequestResponse.model_validate(r) for r in requests],
|
|
432→ count=len(requests)
|
|
433→ )
|
|
434→
|
|
435→
|
|
436→@router.post("/{name}/permission-requests", response_model=PermissionRequestResponse, status_code=status.HTTP_201_CREATED)
|
|
437→async def create_permission_request(name: str, request: PermissionRequestCreate, db: AsyncSession = Depends(get_db)):
|
|
438→ """Create a new permission request (typically called by agents)."""
|
|
439→ # Verify project exists
|
|
440→ result = await db.execute(select(Project).where(Project.name == name))
|
|
441→ project = result.scalar_one_or_none()
|
|
442→
|
|
443→ if not project:
|
|
444→ raise HTTPException(
|
|
445→ status_code=status.HTTP_404_NOT_FOUND,
|
|
446→ detail=f"Project '{name}' not found"
|
|
447→ )
|
|
448→
|
|
449→ # Create permission request
|
|
450→ db_request = PermissionRequest(
|
|
451→ project_id=project.id,
|
|
452→ feature_id=request.feature_id,
|
|
453→ agent_id=request.agent_id,
|
|
454→ permission_type=request.permission_type,
|
|
455→ permission_value=request.permission_value,
|
|
456→ status="pending",
|
|
457→ )
|
|
458→ db.add(db_request)
|
|
459→ await db.commit()
|
|
460→ await db.refresh(db_request)
|
|
461→
|
|
462→ return PermissionRequestResponse.model_validate(db_request)
|
|
463→
|
|
464→
|
|
465→@router.post("/{name}/permission-requests/{request_id}/grant", response_model=PermissionRequestResponse)
|
|
466→async def grant_permission_request(name: str, request_id: int, db: AsyncSession = Depends(get_db)):
|
|
467→ """Grant a pending permission request."""
|
|
468→ # Verify project exists
|
|
469→ result = await db.execute(select(Project).where(Project.name == name))
|
|
470→ project = result.scalar_one_or_none()
|
|
471→
|
|
472→ if not project:
|
|
473→ raise HTTPException(
|
|
474→ status_code=status.HTTP_404_NOT_FOUND,
|
|
475→ detail=f"Project '{name}' not found"
|
|
476→ )
|
|
477→
|
|
478→ # Get permission request
|
|
479→ result = await db.execute(
|
|
480→ select(PermissionRequest).where(and_(
|
|
481→ PermissionRequest.id == request_id,
|
|
482→ PermissionRequest.project_id == project.id
|
|
483→ ))
|
|
484→ )
|
|
485→ request = result.scalar_one_or_none()
|
|
486→
|
|
487→ if not request:
|
|
488→ raise HTTPException(
|
|
489→ status_code=status.HTTP_404_NOT_FOUND,
|
|
490→ detail=f"Permission request {request_id} not found"
|
|
491→ )
|
|
492→
|
|
493→ if request.status != "pending":
|
|
494→ raise HTTPException(
|
|
495→ status_code=status.HTTP_400_BAD_REQUEST,
|
|
496→ detail=f"Permission request is already {request.status}"
|
|
497→ )
|
|
498→
|
|
499→ # Update request status
|
|
500→ request.status = "granted"
|
|
501→ request.resolved_at = datetime.utcnow()
|
|
502→
|
|
503→ # Create the actual permission
|
|
504→ db_permission = Permission(
|
|
505→ project_id=project.id,
|
|
506→ permission_type=request.permission_type,
|
|
507→ permission_value=request.permission_value,
|
|
508→ granted=True,
|
|
509→ )
|
|
510→ db.add(db_permission)
|
|
511→
|
|
512→ await db.commit()
|
|
513→ await db.refresh(request)
|
|
514→
|
|
515→ return PermissionRequestResponse.model_validate(request)
|
|
516→
|
|
517→
|
|
518→@router.post("/{name}/permission-requests/{request_id}/deny", response_model=PermissionRequestResponse)
|
|
519→async def deny_permission_request(name: str, request_id: int, db: AsyncSession = Depends(get_db)):
|
|
520→ """Deny a pending permission request."""
|
|
521→ # Verify project exists
|
|
522→ result = await db.execute(select(Project).where(Project.name == name))
|
|
523→ project = result.scalar_one_or_none()
|
|
524→
|
|
525→ if not project:
|
|
526→ raise HTTPException(
|
|
527→ status_code=status.HTTP_404_NOT_FOUND,
|
|
528→ detail=f"Project '{name}' not found"
|
|
529→ )
|
|
530→
|
|
531→ # Get permission request
|
|
532→ result = await db.execute(
|
|
533→ select(PermissionRequest).where(and_(
|
|
534→ PermissionRequest.id == request_id,
|
|
535→ PermissionRequest.project_id == project.id
|
|
536→ ))
|
|
537→ )
|
|
538→ request = result.scalar_one_or_none()
|
|
539→
|
|
540→ if not request:
|
|
541→ raise HTTPException(
|
|
542→ status_code=status.HTTP_404_NOT_FOUND,
|
|
543→ detail=f"Permission request {request_id} not found"
|
|
544→ )
|
|
545→
|
|
546→ if request.status != "pending":
|
|
547→ raise HTTPException(
|
|
548→ status_code=status.HTTP_400_BAD_REQUEST,
|
|
549→ detail=f"Permission request is already {request.status}"
|
|
550→ )
|
|
551→
|
|
552→ # Update request status
|
|
553→ request.status = "denied"
|
|
554→ request.resolved_at = datetime.utcnow()
|
|
555→
|
|
556→ await db.commit()
|
|
557→ await db.refresh(request)
|
|
558→
|
|
559→ return PermissionRequestResponse.model_validate(request)
|
|
560→
|
|
|
|
<system-reminder>
|
|
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
|
</system-reminder>
|