Demo Scenario: An agent attempted to modify core authentication logic. Review the execution trace and cryptographic receipt below.
Context
Notes: send explicit done state and optimistic toggle
| -18,10โ+18,14 | ||
| 18 | 18 | return response.json(); |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | -export async function toggleNote(id: number): Promise<Note> { | |
| 21 | +export async function toggleNote(id: number, done: boolean): Promise<void> { | |
| 22 | 22 | const response = await fetch(`${API_URL}/notes/${id}/toggle`, { |
| 23 | 23 | method: 'PATCH', |
| 24 | + headers: { 'Content-Type': 'application/json' }, | |
| 25 | + body: JSON.stringify({ done }), | |
| 24 | 26 | }); |
| 25 | 27 | if (!response.ok) { |
| 26 | 28 | throw new Error('Failed to toggle note'); |
| 27 | 29 | } |
| 28 | - return response.json(); | |
| 29 | 30 | } |
| -37,11โ+37,22 | ||
| 37 | 37 | } |
| 38 | 38 | }; |
| 39 | 39 | |
| 40 | - const handleToggle = async (id: number) => { | |
| 40 | + const handleToggle = async (id: number, nextDone: boolean) => { | |
| 41 | + setNotes((prev) => | |
| 42 | + prev.map((note) => | |
| 43 | + note.id === id ? { ...note, done: nextDone } : note | |
| 44 | + ) | |
| 45 | + ); | |
| 41 | 46 | try { |
| 42 | - await toggleNote(id); | |
| 43 | - loadNotes(); | |
| 47 | + await toggleNote(id, nextDone); | |
| 44 | 48 | } catch (err) { |
| 45 | 49 | setError('Failed to toggle note'); |
| 50 | + loadNotes(); | |
| 46 | 51 | } |
| 47 | 52 | }; |
| -74,7โ+85,7 | ||
| 74 | 85 | <label> |
| 75 | 86 | <input |
| 76 | 87 | type="checkbox" |
| 77 | - checked={note.done} | |
| 78 | - onChange={() => handleToggle(note.id)} | |
| 88 | + checked={note.done} | |
| 89 | + onChange={() => handleToggle(note.id, !note.done)} | |
| 79 | 90 | /> |
| 80 | 91 | Done |
| 81 | 92 | </label> |
| -10,6โ+10,10 | ||
| 10 | 10 | pass |
| 11 | 11 | |
| 12 | 12 | |
| 13 | +class NoteToggle(BaseModel): | |
| 14 | + done: bool | |
| 15 | + | |
| 16 | + | |
| 13 | 17 | class Note(NoteBase): |
| 14 | 18 | id: int |
| -3,7โ+3,7 | ||
| 3 | 3 | from fastapi.middleware.cors import CORSMiddleware |
| 4 | 4 | from typing import List |
| 5 | 5 | from contextlib import asynccontextmanager |
| 6 | 6 | |
| 7 | -from models import Note, NoteCreate | |
| 7 | +from models import Note, NoteCreate, NoteToggle | |
| 8 | 8 | from database import init_db, get_db_connection |
| -47,20โ+47,14 | ||
| 47 | 47 | return Note( |
| 48 | 48 | id=row["id"], |
| 49 | 49 | title=row["title"], |
| 50 | 50 | content=row["content"], |
| 51 | 51 | done=bool(row["done"]), |
| 52 | 52 | ) |
| 53 | 53 | |
| 54 | 54 | |
| 55 | -@app.patch("/notes/{note_id}/toggle", response_model=Note) | |
| 56 | -async def toggle_note(note_id: int): | |
| 55 | +@app.patch("/notes/{note_id}/toggle", status_code=204) | |
| 56 | +async def toggle_note(note_id: int, payload: NoteToggle): | |
| 57 | 57 | with get_db_connection() as conn: |
| 58 | - cursor = conn.execute( | |
| 59 | - "SELECT id, title, content, done FROM notes WHERE id = ?", (note_id,) | |
| 60 | - ) | |
| 61 | - row = cursor.fetchone() | |
| 62 | - if not row: | |
| 63 | - raise HTTPException(status_code=404, detail="Note not found") | |
| 64 | - | |
| 65 | - new_done = not bool(row["done"]) | |
| 66 | - conn.execute("UPDATE notes SET done = ? WHERE id = ?", (int(new_done), note_id)) | |
| 58 | + conn.execute("UPDATE notes SET done = ? WHERE id = ?", (int(payload.done), note_id)) | |
| 67 | 59 | conn.commit() |
| 68 | - return Note( | |
| 69 | - id=row["id"], title=row["title"], content=row["content"], done=new_done | |
| 70 | - ) | |
| 60 | + return None | |
MERGE GATE
Status: CLOSED
This change cannot be merged until all conditions are met.
MERGE CONDITIONS
Decision Checkpoint
PR
Notes: send explicit done state and optimistic toggle
Senales detectadas
api_contract ยท optimistic_ui ยท state_transition ยท validation ยท error_handling