-
-
Notifications
You must be signed in to change notification settings - Fork 35
tsk-gq43l6 [OPEN] Lists S1: backend stores+routes+scopes (#2140) #2144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| from tinyagentos.projects.element_store import ProjectElementStore | ||
| from tinyagentos.projects.invite_store import ProjectInviteStore | ||
| from tinyagentos.projects.lists_store import ProjectListsStore, ProjectListEntriesStore | ||
| from tinyagentos.projects.project_store import ProjectStore | ||
| from tinyagentos.projects.routines_store import ProjectRoutinesStore | ||
| from tinyagentos.projects.task_store import ProjectTaskStore | ||
|
|
||
| __all__ = [ | ||
| "ProjectElementStore", | ||
| "ProjectInviteStore", | ||
| "ProjectListsStore", | ||
| "ProjectListEntriesStore", | ||
| "ProjectStore", | ||
| "ProjectRoutinesStore", | ||
| "ProjectTaskStore", | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,182 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tinyagentos.base_store import BaseStore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tinyagentos.projects.ids import new_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ProjectListEntriesStore(BaseStore): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win Implement or stop importing
📍 Affects 3 files
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SCHEMA = """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE TABLE IF NOT EXISTS project_list_entries ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id TEXT PRIMARY KEY, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_id TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project_id TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| original_text TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category TEXT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status TEXT NOT NULL DEFAULT 'new', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done INTEGER NOT NULL DEFAULT 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| author_kind TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| author_id TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| edited_by TEXT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position INTEGER NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created_at REAL NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updated_at REAL NOT NULL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE INDEX IF NOT EXISTS idx_entries_list ON project_list_entries(list_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE INDEX IF NOT EXISTS idx_entries_project ON project_list_entries(project_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE INDEX IF NOT EXISTS idx_entries_list_project ON project_list_entries(list_id, project_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CREATE INDEX IF NOT EXISTS idx_entries_status ON project_list_entries(project_id, status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MIGRATIONS = [] # No migrations needed per spec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def add_entry( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_id: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project_id: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| original_text: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| author_kind: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| author_id: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: int | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> dict: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry_id = new_id("lle") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| now = time.time() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Store immutability: original_text cannot be changed after creation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # It is stored verbatim as the first written text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "INSERT INTO project_list_entries ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id, list_id, project_id, text, original_text, category, status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done, author_kind, author_id, position, created_at, updated_at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
ruff check tinyagentos/projects/lists_store.pyRepository: jaylfc/taOS Length of output: 20372 Fix The current line 50 multi-line string uses a normal double-quoted string, which leaves the 🧰 Tools🪛 OpenGrep (1.25.0)[ERROR] 49-59: SQL query built via f-string passed to execute()/executemany(). Use parameterized queries with placeholders instead. (coderabbit.sql-injection.python-fstring-execute) 🪛 Ruff (0.15.21)[warning] 50-50: missing closing quote in string literal (invalid-syntax) [warning] 51-51: Expected (invalid-syntax) [warning] 53-53: Simple statements must be separated by newlines or semicolons (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: missing closing quote in string literal (invalid-syntax) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry_id, list_id, project_id, text, original_text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category, "new", 0, author_kind, author_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position if position is not None else 0, now, now, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Append entries at the next list position. When Proposed fix ) -> dict:
entry_id = new_id("lle")
now = time.time()
+ if position is None:
+ position = await self._get_next_position(project_id, list_id)
# Store immutability: original_text cannot be changed after creation
@@
- position if position is not None else 0, now, now,
+ position, now, now,📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.15.21)[warning] 50-50: missing closing quote in string literal (invalid-syntax) [warning] 51-51: Expected (invalid-syntax) [warning] 53-53: Simple statements must be separated by newlines or semicolons (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: Expected an expression or a ')' (invalid-syntax) [warning] 53-53: Got unexpected token ? (invalid-syntax) [warning] 53-53: missing closing quote in string literal (invalid-syntax) [warning] 54-54: Unexpected indentation (invalid-syntax) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.commit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await self.get_entry(entry_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_entry(self, entry_id: str) -> dict | None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SELECT * FROM project_list_entries WHERE id = ?", (entry_id,) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) as cur: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| row = await cur.fetchone() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if row is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys = [d[0] for d in cur.description] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return dict(zip(keys, row)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def list_entries( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project_id: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_id: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> list[dict]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where_clause = "project_id = ?" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params = [project_id] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if list_id is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where_clause += " AND list_id = ?" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(list_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if status is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where_clause += " AND status = ?" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(status) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if category is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where_clause += " AND category = ?" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(category) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"SELECT * FROM project_list_entries WHERE {where_clause} ORDER BY position ASC, created_at ASC", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) as cur: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rows = await cur.fetchall() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys = [d[0] for d in cur.description] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [dict(zip(keys, r)) for r in rows] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def update_entry( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entry_id: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done: int | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: int | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| edited_by: str | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> dict | None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Enforce immutability: original_text cannot be changed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Only text, category, status, done, position can be updated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if text is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("text = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if category is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("category = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(category) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if status is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("status = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(status) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if done is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("done = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(done) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if position is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("position = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(position) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if edited_by is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("edited_by = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(edited_by) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not sets: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await self.get_entry(entry_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sets.append("updated_at = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(time.time()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.append(entry_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Before updating, verify that original_text cannot be modified | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| existing = await self.get_entry(entry_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if existing is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"UPDATE project_list_entries SET {', '.join(sets)} WHERE id = ?", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.commit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await self.get_entry(entry_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def delete_entry(self, entry_id: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor = await self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "DELETE FROM project_list_entries WHERE id = ?", (entry_id,) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.commit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cursor.rowcount == 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def reorder_entries(self, project_id: str, entries: list[dict]) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for entry in entries: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "UPDATE project_list_entries SET position = ?, updated_at = ? WHERE id = ? AND project_id = ?", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (entry["position"], time.time(), entry["id"], project_id), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._db.commit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _get_next_position(self, project_id: str, list_id: str | None = None) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with self._db.execute( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SELECT MAX(position) + 1 FROM project_list_entries WHERE project_id = ? AND list_id = ?", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (project_id, list_id or ""), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) as cur: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+176
to
+180
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. Next position computed incorrectly ProjectListEntriesStore._get_next_position converts list_id=None into an empty string, but list_id is stored as a real non-empty identifier, so the query will not match existing rows and will return 0. Any caller using this helper with list_id omitted will repeatedly assign position 0 and break ordering semantics. Agent Prompt
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| row = await cur.fetchone() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (row[0] if row[0] is not None else 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,11 @@ | |
| # scope keeps an existing approval meaning exactly what it meant when it was | ||
| # given, and makes authoring an explicit per-agent opt-in. | ||
| "project_tasks_create", | ||
| # Lists access: idea capture with agent triage (statuses, checkboxes, agent tidy) | ||
| # Least-privilege read + write on the agent's OWN project only, bound by the | ||
| # token's project_id claim. | ||
| "lists_read", | ||
| "lists_write", | ||
|
Comment on lines
+65
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. _allowed_scopes missing lists scopes lists_read and lists_write were added to VALID_SCOPES but not to _ALLOWED_SCOPES in agent_registry.py, so the two scope sets are no longer synchronized. This likely breaks the equality check/test and can prevent minting/granting the new scopes consistently. Agent Prompt
|
||
| # Canvas access: read and write on a specific project's canvas. Like | ||
| # project_tasks, a project_id is required so the token is bound to the | ||
| # operator-validated project rather than whatever the unauthenticated agent | ||
|
|
@@ -194,7 +199,13 @@ async def _retire_scope_request_notification(request: Request, request_id: str) | |
| # project_tasks_create globally. One definition, referenced everywhere. | ||
| _CANVAS_SCOPES = {"canvas_read", "canvas_write"} | ||
| _FILES_SCOPES = {"files_read", "files_write"} | ||
| _PROJECT_SCOPES = {"project_tasks", "project_tasks_create"} | _CANVAS_SCOPES | _FILES_SCOPES | ||
| _LISTS_SCOPES = {"lists_read", "lists_write"} | ||
| _PROJECT_SCOPES = { | ||
| "project_tasks", | ||
| "project_tasks_create", | ||
| "lists_read", | ||
| "lists_write", | ||
| } | _CANVAS_SCOPES | _FILES_SCOPES | _LISTS_SCOPES | ||
|
Comment on lines
+202
to
+208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3. Scope-set test will fail _PROJECT_SCOPES now includes lists_read/lists_write, but tests/test_agent_scope_requests.py asserts an exact set without those entries, so CI will fail. This blocks verification of the PR until the expected scope set is updated. Agent Prompt
|
||
|
|
||
|
|
||
| def _get_approve_lock(request: Request, request_id: str) -> asyncio.Lock: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2. Missing projectlistsstore class
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools