Skip to content

tsk-gq43l6 [OPEN] Lists S1: backend stores+routes+scopes (#2140) - #2144

Closed
jaylfc wants to merge 1 commit into
devfrom
exec/tsk-gq43l6
Closed

tsk-gq43l6 [OPEN] Lists S1: backend stores+routes+scopes (#2140)#2144
jaylfc wants to merge 1 commit into
devfrom
exec/tsk-gq43l6

Conversation

@jaylfc

@jaylfc jaylfc commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Autonomous build of board card tsk-gq43l6.

Files:
tinyagentos/app.py | 7 ++
tinyagentos/projects/init.py | 16 +++
tinyagentos/projects/lists_store.py | 182 ++++++++++++++++++++++++++++++
tinyagentos/routes/agent_auth_requests.py | 13 ++-
4 files changed, 217 insertions(+), 1 deletion(-)

Summary by CodeRabbit

  • New Features
    • Added persistent project list entries with support for creating, viewing, editing, deleting, marking complete, and reordering items.
    • Added filtering of list entries by list, status, and category.
  • Permissions
    • Added lists_read and lists_write access scopes for external agents.
    • List access permissions are now explicitly tied to the relevant project.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds SQLite-backed project list entry persistence, wires the new stores into application startup and state, exports project store classes, and adds project-scoped lists_read and lists_write authorization scopes.

Changes

Project lists

Layer / File(s) Summary
List entry persistence
tinyagentos/projects/lists_store.py
Adds the ProjectListEntriesStore, its table schema, and entry CRUD, filtering, updating, deletion, reordering, and position calculation methods.
Application store wiring
tinyagentos/projects/__init__.py, tinyagentos/app.py
Exports project store classes and constructs, initializes, and exposes the new list stores through app.state.
List authorization scopes
tinyagentos/routes/agent_auth_requests.py
Adds lists_read and lists_write to valid scopes and project-bound scope enforcement.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • jaylfc/taOS#719: Modifies the external-agent consent flow that processes the newly added list scopes.
  • jaylfc/taOS#1824: Updates project-bound agent scope validation in the same authorization module.
  • jaylfc/taOS#1838: Extends consent scope validation and project-binding logic in the same route module.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main backend change: list stores, routes, and scopes for Lists S1.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch exec/tsk-gq43l6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Jul 26, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

@jaylfc

jaylfc commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

nemotron-ultra-kilo review

VERDICT: Code adds project lists feature with stores and auth scopes; has SQL injection risks, race conditions, missing constraints, and no tests.

  • tinyagentos/projects/lists_store.py:98 SQL injection via f-string interpolation in list_entries - where_clause built by string concatenation before parameterization; use parameterized query builder or validate/filter inputs

  • tinyagentos/projects/lists_store.py:139 SQL injection via f-string in update_entry - sets joined into query string; column names are hardcoded but pattern is unsafe

  • tinyagentos/projects/lists_store.py:174 Race condition in _get_next_position - MAX(position) + 1 not atomic under concurrency; use INSERT ... RETURNING or advisory lock

  • tinyagentos/projects/lists_store.py:162 reorder_entries lacks transaction - multiple UPDATEs commit individually on failure; wrap in explicit transaction

  • tinyagentos/projects/lists_store.py:30 Schema missing foreign keys - list_id and project_id should reference parent tables for referential integrity

  • tinyagentos/projects/lists_store.py:30 No cascade/delete rules - orphaned entries on list/project deletion

  • tinyagentos/projects/lists_store.py:117 update_entry doesn't verify project_id matches - could update entry from different project; add AND project_id = ? to WHERE

  • tinyagentos/projects/lists_store.py:1-182 No test file added for new store - missing unit/integration tests for all CRUD operations, edge cases, concurrency

  • tinyagentos/routes/agent_auth_requests.py:72 New scopes lists_read/lists_write added but no corresponding route handlers visible in diff - verify endpoints exist and enforce project_id binding
    VERDICT: Code adds project lists feature with stores and auth scopes; has SQL injection risks, race conditions, missing constraints, and no tests.

  • tinyagentos/projects/lists_store.py:98 SQL injection via f-string interpolation in list_entries - where_clause built by string concatenation before parameterization; use parameterized query builder or validate/filter inputs

  • tinyagentos/projects/lists_store.py:139 SQL injection via f-string in update_entry - sets joined into query string; column names are hardcoded but pattern is unsafe

  • tinyagentos/projects/lists_store.py:174 Race condition in _get_next_position - MAX(position) + 1 not atomic under concurrency; use INSERT ... RETURNING or advisory lock

  • tinyagentos/projects/lists_store.py:162 reorder_entries lacks transaction - multiple UPDATEs commit individually on failure; wrap in explicit transaction

  • tinyagentos/projects/lists_store.py:30 Schema missing foreign keys - list_id and project_id should reference parent tables for referential integrity

  • tinyagentos/projects/lists_store.py:30 No cascade/delete rules - orphaned entries on list/project deletion

  • tinyagentos/projects/lists_store.py:117 update_entry doesn't verify project_id matches - could update entry from different project; add AND project_id = ? to WHERE

  • tinyagentos/projects/lists_store.py:1-182 No test file added for new store - missing unit/integration tests for all CRUD operations, edge cases, concurrency

  • tinyagentos/routes/agent_auth_requests.py:72 New scopes lists_read/lists_write added but no corresponding route handlers visible in diff - verify endpoints exist and enforce project_id binding

Automated first-pass review by the nemotron-ultra-kilo lane. The lead still reviews before merge.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add project list stores and lists_* agent scopes

✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Add SQLite-backed stores for per-project list entries.
• Wire list stores into FastAPI startup and app state.
• Extend agent consent scopes with project-bound lists_read/lists_write.
Diagram

graph TD
A["FastAPI app"] --> B["Startup lifespan"] --> C["List stores"]
C --> D["ProjectListsStore"] --> E[("project_lists.db")]
C --> F["ListEntriesStore"] --> G[("project_list_entries.db")]
A --> H["Agent auth routes"] --> I["lists_* scopes"] --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Single SQLite DB for lists + entries
  • ➕ Fewer DB files to initialize/manage
  • ➕ Easier to keep list and entry operations transactionally consistent
  • ➖ Less isolation; schema changes may become more coupled over time
  • ➖ May require careful migrations if already deployed
2. Co-locate list tables in existing projects.db
  • ➕ Keeps all project-related persistence in one place
  • ➕ Reduces app wiring and operational surface area
  • ➖ Requires migration/upgrade discipline for existing installs
  • ➖ Higher risk of contention/accidental coupling with other project tables
3. Model lists as a view over existing tasks/routines
  • ➕ Avoids introducing new persistence primitives and scopes
  • ➕ Leverages existing lifecycle/status machinery
  • ➖ Semantics mismatch (lists vs task workflow) can leak into UX and APIs
  • ➖ Harder to evolve independently (e.g., per-list ordering, categories)

Recommendation: The current direction (dedicated list stores and explicit lists_* scopes) is a sensible least-privilege foundation for a lists feature. To make it robust, ensure the stores being wired (e.g., ProjectListsStore) are actually implemented/exported, and align ID generation (new_id prefixes) and schema expectations before building routes on top.

Files changed (4) +217 / -1

Enhancement (2) +189 / -0
app.pyInitialize and expose list stores via app lifespan/app.state +7/-0

Initialize and expose list stores via app lifespan/app.state

• Imports the new list stores, creates per-feature SQLite DB instances, and initializes them during the FastAPI lifespan. Exposes the stores on app.state so routes can access them consistently.

tinyagentos/app.py

lists_store.pyAdd SQLite store for project list entries +182/-0

Add SQLite store for project list entries

• Introduces ProjectListEntriesStore with schema for project_list_entries plus CRUD, filtering, and reorder APIs. Enforces original_text immutability by only allowing updates to mutable fields and maintaining updated_at timestamps.

tinyagentos/projects/lists_store.py

Refactor (1) +16 / -0
__init__.pyExport project store types from projects package +16/-0

Export project store types from projects package

• Adds a package-level __init__ that re-exports commonly used project store classes, including the new list stores, to provide a stable import surface.

tinyagentos/projects/init.py

Other (1) +12 / -1
agent_auth_requests.pyAdd lists_read/lists_write to valid scopes and project-bound scope set +12/-1

Add lists_read/lists_write to valid scopes and project-bound scope set

• Extends VALID_SCOPES to include lists_read and lists_write for the external-agent consent loop. Updates the project-binding scope set so list scopes are treated as requiring a project_id-bound grant.

tinyagentos/routes/agent_auth_requests.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tinyagentos/projects/lists_store.py`:
- Line 8: Resolve the missing ProjectListsStore contract: in
tinyagentos/projects/lists_store.py:8-8, implement ProjectListsStore alongside
ProjectListEntriesStore or remove its usage; in
tinyagentos/projects/__init__.py:3-3, export only symbols actually defined by
lists_store; and in tinyagentos/app.py:401-401, construct only implemented
stores or use the implemented ProjectListsStore.
- Around line 49-53: Update add_entry() so the multiline SQL passed to
self._db.execute uses a properly closed triple-quoted string, preserving the
existing INSERT statement and bind parameters while eliminating parser errors.
- Around line 42-57: When creating an entry in the method containing the INSERT,
replace the omitted-position fallback of 0 with the result of the existing
_get_next_position() helper. Preserve explicitly supplied positions while
ensuring new entries append after the current list contents.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ce07306e-cbd2-46da-aa1a-f5e5ea17df85

📥 Commits

Reviewing files that changed from the base of the PR and between 13f0afa and a24e2e8.

📒 Files selected for processing (4)
  • tinyagentos/app.py
  • tinyagentos/projects/__init__.py
  • tinyagentos/projects/lists_store.py
  • tinyagentos/routes/agent_auth_requests.py

from tinyagentos.base_store import BaseStore
from tinyagentos.projects.ids import new_id

class ProjectListEntriesStore(BaseStore):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Implement or stop importing ProjectListsStore. The new module defines ProjectListEntriesStore only, but both package export and app startup import ProjectListsStore; after the syntax error is fixed, this raises ImportError and prevents application startup.

  • tinyagentos/projects/lists_store.py#L8-L8: add the ProjectListsStore implementation expected by this PR.
  • tinyagentos/projects/__init__.py#L3-L3: export only symbols actually provided by tinyagentos.projects.lists_store.
  • tinyagentos/app.py#L401-L401: construct only implemented stores, or use the newly added ProjectListsStore.
📍 Affects 3 files
  • tinyagentos/projects/lists_store.py#L8-L8 (this comment)
  • tinyagentos/projects/__init__.py#L3-L3
  • tinyagentos/app.py#L401-L401
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tinyagentos/projects/lists_store.py` at line 8, Resolve the missing
ProjectListsStore contract: in tinyagentos/projects/lists_store.py:8-8,
implement ProjectListsStore alongside ProjectListEntriesStore or remove its
usage; in tinyagentos/projects/__init__.py:3-3, export only symbols actually
defined by lists_store; and in tinyagentos/app.py:401-401, construct only
implemented stores or use the implemented ProjectListsStore.

Comment on lines +42 to +57
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 position is omitted, every entry is stored at position 0; after any reorder, new entries can appear before existing entries. Use the existing _get_next_position() helper instead.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
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,
position: int | None = None,
) -> 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
# 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
entry_id, list_id, project_id, text, original_text,
category, "new", 0, author_kind, author_id,
position, now, now,
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 50-50: missing closing quote in string literal

(invalid-syntax)


[warning] 51-51: Expected ,, found name

(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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tinyagentos/projects/lists_store.py` around lines 42 - 57, When creating an
entry in the method containing the INSERT, replace the omitted-position fallback
of 0 with the result of the existing _get_next_position() helper. Preserve
explicitly supplied positions while ensuring new entries append after the
current list contents.

Comment on lines +49 to +53
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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.py

Repository: jaylfc/taOS

Length of output: 20372


Fix add_entry() so it can be parsed.

The current line 50 multi-line string uses a normal double-quoted string, which leaves the ruff/Python parser with multiple invalid-string and cascade parsing errors instead of a concise “missing closing quote” for just this SQL statement. Wrap the SQL in a triple-quoted string, or keep it on one line and pass the bind tuple as the second argument to execute().

🧰 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 ,, found name

(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 Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tinyagentos/projects/lists_store.py` around lines 49 - 53, Update add_entry()
so the multiline SQL passed to self._db.execute uses a properly closed
triple-quoted string, preserving the existing INSERT statement and bind
parameters while eliminating parser errors.

Source: Linters/SAST tools

@jaylfc

jaylfc commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Reviewed. Not mergeable yet - three blockers, one of them fatal.

1. The module does not parse. tinyagentos/projects/lists_store.py line 50 has an unterminated string literal: the multi-line INSERT INTO project_list_entries ( SQL opens with a single " instead of """. Reproduced locally with the exact CI command (python -m compileall), which is why the lint check is red. Nothing in this PR can import, so the pending test jobs were never going to pass either.

2. No routes. The card scope was stores + routes + scopes, and the diff touches app.py, projects/__init__.py, lists_store.py and agent_auth_requests.py - there is no route module and no endpoint. The stores are unreachable.

3. No tests. The card's acceptance criteria required them; the diff adds none.

What is good, and should be kept as-is: the schema is thoughtful. original_text alongside text preserves the user's original wording under agent tidying, which is exactly the requirement from #2140, and status/done/position/author_kind cover the rest of the spec. MIGRATIONS = [] is correct for a brand-new table.

Splitting the remainder so each piece is small enough to land cleanly:

  • Fix-forward on this branch (do NOT open a new PR): make it compile, plus unit tests for the store.
  • A separate follow-up card for routes + scopes + middleware allowlist, as its own PR.

Leaving this open for the fix-forward.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (1)

Context used
✅ Compliance rules (platform): 35 rules

Grey Divider


Action required

1. _ALLOWED_SCOPES missing lists scopes 📜 Skill insight ≡ Correctness
Description
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.
Code

tinyagentos/routes/agent_auth_requests.py[R65-69]

+    # 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",
Relevance

⭐⭐⭐ High

Scope lists must stay in sync; mismatch likely breaks auth/scopes checks, so they usually fix
promptly.

PR-#2122

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The compliance rule requires _ALLOWED_SCOPES and VALID_SCOPES to remain synchronized when adding
new scopes. The PR adds lists_read/lists_write to VALID_SCOPES, but _ALLOWED_SCOPES in
agent_registry.py does not include them.

tinyagentos/routes/agent_auth_requests.py[43-81]
tinyagentos/routes/agent_registry.py[87-100]
Skill: taos-development-skill

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR adds new scopes (`lists_read`, `lists_write`) to the consent-flow `VALID_SCOPES`, but `tinyagentos/routes/agent_registry.py`'s `_ALLOWED_SCOPES` is not updated, violating the requirement that these sets stay synchronized.

## Issue Context
A test asserts these scope sets are equal. If they drift, registry minting/validation paths may reject scopes that the consent flow considers valid.

## Fix Focus Areas
- tinyagentos/routes/agent_registry.py[87-100]
- tinyagentos/routes/agent_auth_requests.py[43-81]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing ProjectListsStore class 🐞 Bug ≡ Correctness
Description
tinyagentos/app.py imports and instantiates ProjectListsStore, but
tinyagentos/projects/lists_store.py defines only ProjectListEntriesStore, causing ImportError during
backend startup/probe. This prevents the FastAPI app from initializing stores and serving requests.
Code

tinyagentos/app.py[R401-405]

+    from tinyagentos.projects.lists_store import ProjectListsStore, ProjectListEntriesStore
    project_store = ProjectStore(data_dir / "projects.db")
    project_invite_store = ProjectInviteStore(data_dir / "project_invites.db")
+    project_lists_store = ProjectListsStore(data_dir / "project_lists.db")
+    project_list_entries_store = ProjectListEntriesStore(data_dir / "project_list_entries.db")
Relevance

⭐⭐⭐ High

ImportError/crash on startup is a hard blocker; team historically accepts ImportError-hardening
fixes.

PR-#419

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
App wiring imports/constructs ProjectListsStore, but the new module only defines
ProjectListEntriesStore, so the import cannot succeed.

tinyagentos/app.py[394-406]
tinyagentos/projects/lists_store.py[1-12]
tinyagentos/projects/init.py[1-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ProjectListsStore` is imported/instantiated during app setup but is not defined in `tinyagentos/projects/lists_store.py`, which will raise `ImportError` and crash startup.

## Issue Context
`tinyagentos/app.py` now wires both `ProjectListsStore` and `ProjectListEntriesStore` into `app.state` and calls `.init()` during lifespan.

## Fix Focus Areas
- tinyagentos/projects/lists_store.py[1-182]
- tinyagentos/app.py[401-405]
- tinyagentos/projects/__init__.py[1-16]

## Suggested fix
- Either implement a `ProjectListsStore` class (with its own `SCHEMA` for lists metadata and the API expected by routes), or stop importing/instantiating/exporting it until it exists.
- Ensure the store(s) you keep are initialized in lifespan and exposed via `app.state` consistently with how other stores are wired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Scope-set test will fail 🐞 Bug ☼ Reliability
Description
_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.
Code

tinyagentos/routes/agent_auth_requests.py[R202-208]

+_LISTS_SCOPES = {"lists_read", "lists_write"}
+_PROJECT_SCOPES = {
+    "project_tasks",
+    "project_tasks_create",
+    "lists_read",
+    "lists_write",
+} | _CANVAS_SCOPES | _FILES_SCOPES | _LISTS_SCOPES
Relevance

⭐⭐⭐ High

CI/test break from new scopes requires updating expected sets; test-maintenance fixes are commonly
accepted.

PR-#234
PR-#449

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds lists scopes to _PROJECT_SCOPES, while the existing test asserts _PROJECT_SCOPES
equals a set that does not include them.

tinyagentos/routes/agent_auth_requests.py[195-209]
tests/test_agent_scope_requests.py[554-580]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR expands `_PROJECT_SCOPES` to include `lists_read` and `lists_write`, but the unit test still asserts the previous exact set contents.

## Issue Context
`tests/test_agent_scope_requests.py::test_project_scope_set_is_a_single_definition` compares `_PROJECT_SCOPES` to a hard-coded set.

## Fix Focus Areas
- tinyagentos/routes/agent_auth_requests.py[200-209]
- tests/test_agent_scope_requests.py[554-580]

## Suggested fix
- Update the expected set in the test to include `lists_read` and `lists_write`.
- Keep the alias identity assertions (`is`) intact so drift is still caught.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Next position computed incorrectly 🐞 Bug ≡ Correctness
Description
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.
Code

tinyagentos/projects/lists_store.py[R176-180]

+    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:
Relevance

⭐⭐⭐ High

Deterministic ordering bug from incorrect SQL parameter binding; similar store correctness guards
are typically accepted.

PR-#260

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper binds "" when list_id is None, but list_id is NOT NULL and entries are inserted with a
real list_id, so the MAX() query will not see existing rows in that case.

tinyagentos/projects/lists_store.py[10-24]
tinyagentos/projects/lists_store.py[176-182]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`_get_next_position()` substitutes `None` list_id with `""`, which won’t match normal stored `list_id` values, causing the computed next position to reset to 0.

## Issue Context
Schema enforces `list_id TEXT NOT NULL`, and `add_entry()` inserts the provided `list_id` verbatim.

## Fix Focus Areas
- tinyagentos/projects/lists_store.py[10-24]
- tinyagentos/projects/lists_store.py[176-182]

## Suggested fix
- Make `list_id` a required parameter for `_get_next_position(project_id, list_id)` and remove the `or ""` fallback; or
- If `None` is intended to mean “across all lists in project”, change the SQL to omit the `list_id = ?` predicate when `list_id is None`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +65 to +69
# 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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. _allowed_scopes missing lists scopes 📜 Skill insight ≡ Correctness

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
## Issue description
The PR adds new scopes (`lists_read`, `lists_write`) to the consent-flow `VALID_SCOPES`, but `tinyagentos/routes/agent_registry.py`'s `_ALLOWED_SCOPES` is not updated, violating the requirement that these sets stay synchronized.

## Issue Context
A test asserts these scope sets are equal. If they drift, registry minting/validation paths may reject scopes that the consent flow considers valid.

## Fix Focus Areas
- tinyagentos/routes/agent_registry.py[87-100]
- tinyagentos/routes/agent_auth_requests.py[43-81]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread tinyagentos/app.py
Comment on lines +401 to +405
from tinyagentos.projects.lists_store import ProjectListsStore, ProjectListEntriesStore
project_store = ProjectStore(data_dir / "projects.db")
project_invite_store = ProjectInviteStore(data_dir / "project_invites.db")
project_lists_store = ProjectListsStore(data_dir / "project_lists.db")
project_list_entries_store = ProjectListEntriesStore(data_dir / "project_list_entries.db")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Missing projectlistsstore class 🐞 Bug ≡ Correctness

tinyagentos/app.py imports and instantiates ProjectListsStore, but
tinyagentos/projects/lists_store.py defines only ProjectListEntriesStore, causing ImportError during
backend startup/probe. This prevents the FastAPI app from initializing stores and serving requests.
Agent Prompt
## Issue description
`ProjectListsStore` is imported/instantiated during app setup but is not defined in `tinyagentos/projects/lists_store.py`, which will raise `ImportError` and crash startup.

## Issue Context
`tinyagentos/app.py` now wires both `ProjectListsStore` and `ProjectListEntriesStore` into `app.state` and calls `.init()` during lifespan.

## Fix Focus Areas
- tinyagentos/projects/lists_store.py[1-182]
- tinyagentos/app.py[401-405]
- tinyagentos/projects/__init__.py[1-16]

## Suggested fix
- Either implement a `ProjectListsStore` class (with its own `SCHEMA` for lists metadata and the API expected by routes), or stop importing/instantiating/exporting it until it exists.
- Ensure the store(s) you keep are initialized in lifespan and exposed via `app.state` consistently with how other stores are wired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +202 to +208
_LISTS_SCOPES = {"lists_read", "lists_write"}
_PROJECT_SCOPES = {
"project_tasks",
"project_tasks_create",
"lists_read",
"lists_write",
} | _CANVAS_SCOPES | _FILES_SCOPES | _LISTS_SCOPES

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Scope-set test will fail 🐞 Bug ☼ Reliability

_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
## Issue description
The PR expands `_PROJECT_SCOPES` to include `lists_read` and `lists_write`, but the unit test still asserts the previous exact set contents.

## Issue Context
`tests/test_agent_scope_requests.py::test_project_scope_set_is_a_single_definition` compares `_PROJECT_SCOPES` to a hard-coded set.

## Fix Focus Areas
- tinyagentos/routes/agent_auth_requests.py[200-209]
- tests/test_agent_scope_requests.py[554-580]

## Suggested fix
- Update the expected set in the test to include `lists_read` and `lists_write`.
- Keep the alias identity assertions (`is`) intact so drift is still caught.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +176 to +180
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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Next position computed incorrectly 🐞 Bug ≡ Correctness

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
## Issue description
`_get_next_position()` substitutes `None` list_id with `""`, which won’t match normal stored `list_id` values, causing the computed next position to reset to 0.

## Issue Context
Schema enforces `list_id TEXT NOT NULL`, and `add_entry()` inserts the provided `list_id` verbatim.

## Fix Focus Areas
- tinyagentos/projects/lists_store.py[10-24]
- tinyagentos/projects/lists_store.py[176-182]

## Suggested fix
- Make `list_id` a required parameter for `_get_next_position(project_id, list_id)` and remove the `or ""` fallback; or
- If `None` is intended to mean “across all lists in project”, change the SQL to omit the `list_id = ?` predicate when `list_id is None`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@jaylfc

jaylfc commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

nemotron-ultra-orB review

VERDICT: Code has correctness and security issues that need fixing.

  • tinyagentos/projects/lists_store.py:70-71 - SQL injection vulnerability: f"SELECT * FROM project_list_entries WHERE {where_clause} ORDER BY position ASC, created_at ASC" uses f-string interpolation for WHERE clause with user-controlled status and category parameters. While params are passed separately, the WHERE clause itself is built via string concatenation which is vulnerable if status/category contain SQL fragments. Use parameterized queries throughout or validate against allowlist.
  • tinyagentos/projects/lists_store.py:134 - SQL injection in f"UPDATE project_list_entries SET {', '.join(sets)} WHERE id = ?" - the sets list is built from user-controlled field names (though currently hardcoded, the pattern is unsafe). Use parameterized column names or allowlist validation.
  • tinyagentos/projects/lists_store.py:151 - SQL injection in reorder_entries: UPDATE project_list_entries SET position = ?, updated_at = ? WHERE id = ? AND project_id = ? uses parameterized query correctly here, but the loop executes N queries instead of batch update - inefficient for reordering many items.
  • tinyagentos/projects/lists_store.py:164 - SQL injection: f"SELECT MAX(position) + 1 FROM project_list_entries WHERE project_id = ? AND list_id = ?" - list_id or "" interpolated directly into SQL string. Use parameterized query with COALESCE or conditional logic.
  • tinyagentos/projects/lists_store.py:70 - list_id or "" in SQL is problematic: if list_id is None, it becomes empty string "" which won't match NULL values in DB. Use IS NULL/IS NOT NULL logic or COALESCE(list_id, '') = ?.
  • tinyagentos/projects/lists_store.py:56 - original_text immutability comment says "cannot be changed after creation" but update_entry doesn't enforce this at DB level. Should add CHECK constraint or trigger, or at minimum verify in update_entry that original_text isn't being modified (currently only enforced by not including it in updateable fields, but no DB constraint).
  • tinyagentos/projects/lists_store.py:56 - position if position is not None else 0 - race condition: concurrent inserts to same list/project will get same position. Use MAX(position) + 1 in transaction or DB-generated sequence.
  • tinyagentos/routes/agent_auth_requests.py:199 - _LISTS_SCOPES defined but _PROJECT_SCOPES union includes it twice (once in _LISTS_SCOPES var, once explicitly in union). Redundant but not harmful.
  • Missing tests: No test file for lists_store.py - new store with 6 public methods needs unit tests for CRUD, reorder, immutability enforcement, and SQL injection resistance.
  • tinyagentos/app.py:403 - Imports ProjectListsStore, ProjectListEntriesStore but the module is lists_store.py (plural "lists" vs singular "list" in class names). Classes are ProjectListsStore and ProjectListEntriesStore - naming inconsistency with module name (plural vs singular).
  • tinyagentos/projects/init.py - Exports ProjectListsStore and ProjectListEntriesStore but the store classes are only ProjectListEntriesStore in lists_store.py. ProjectListsStore class doesn't exist in the new file - missing class definition or missing import.

Automated first-pass review by the nemotron-ultra-orB lane. The lead still reviews before merge.

@jaylfc

jaylfc commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

nemotron-super review

VERDICT: Blocking issue found

  • tinyagentos/projects/init.py:3

Automated first-pass review by the nemotron-super lane. The lead still reviews before merge.

@jaylfc

jaylfc commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

Closing per Jay's decision (dec-gqor2k: close both, re-card clean). Red on BOTH test (3.12) and lint since 2026-07-26, and holding one of six fleet throttle slots - which is what starved PR throughput. Re-carded fresh so a lane redoes it against current dev rather than diagnosing a stale red build. No work is lost: the card carries the same scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant