A small, fully-typed FastAPI notes service backed by a real SQLAlchemy/SQLite database, used as the substrate for tickets centered on search-query correctness, SQL LIKE-wildcard escaping, PATCH semantics, and a race-safe uniqueness check under real concurrency.
Nobody has started this yet — be first.
Tags at exactly the max length are rejected
A user tagging a note with a maximally-long tag (right at the documented 50-character limit) gets a 422 validation error for no visible reason -- the tag "looks fine," is under the length shown anywhere in the docs, and yet the API refuses it. It's a small paper cut, but it's the kind of inconsistency that erodes trust in `/docs` as an accurate contract (the README's whole pitch is that the schema is trustworthy). Low priority since it only bites at one exact length, but it's a one-line fix and worth clearing out.
About this project
A Notes API built with FastAPI, SQLAlchemy, and SQLite: create, search, filter, update, and delete notes, backed by an actual database instead of an in-memory dict. The project's whole pitch is that FastAPI's auto-generated page is only as trustworthy as the models behind it -- every route here declares a real ( / ), and every POST/PATCH body is a Pydantic model ( / ) with real field constraints, so shows accurate example payloads, accurate schemas, and genuinely useful 422 error shapes instead of a wall of guessed JSON. Tags live on the row itself as a single delimited string rather than a join table -- a deliberate, documented trade-off at this scale, and one that has real, testable consequences for how tag matching has to work.
Search silently requires a match in title AND content, not either
Search is the primary way a user with more than a handful of notes finds anything again. Right now, GET /notes?q=... only returns a note if the search term happens to appear in **both** the title and the content -- which real notes almost never satisfy (a title is a short label; content is the actual text). In practice this means search returns far fewer results than it should, or none at all, for completely reasonable queries. To a user this looks like their notes vanished. This is the kind of regression that generates "where did my note go?!" support tickets and makes people stop trusting the search box -- high priority even though the fix is small.
/docsresponse_modelNoteOutlist[NoteOut]NoteCreateNoteUpdate/docsNoteFour engineering problems sit at the center of the ticket set: query correctness in the q search filter (an AND where the documented and previously-tested contract requires an OR), SQL LIKE-wildcard escaping (a real, pre-existing defect where % and _ inside a user's search term are interpreted as SQL wildcards instead of literal characters -- parameterization is correct here, but "safe from injection" and "produces the search semantics you intended" turn out to be two different properties), PATCH semantics (exclude_unset=True correctly separates "field not sent" from "field sent empty", but the tags branch throws that distinction away by checking truthiness instead of key presence), and a duplicate-title race where the only correct fix is a real database-level unique constraint plus IntegrityError-to-409 translation -- a check-then-act query is a textbook TOCTOU bug that "works" in every manual test and fails only under real concurrent load.
Seven tickets are deliberately worked into this otherwise fully tested, working codebase: four injected bugs, one pre-existing defect surfaced during a real audit of the untouched code (kept as its own ticket precisely because it is a genuine, unstaged example of the same class of mistake, not a manufactured one), and two missing enhancements whose routes, request/response schemas, and query parameters are already wired up with only the implementation itself left as a stub. Every ticket has its own dedicated pytest file, runnable individually or as a full suite via practicetickets/run_tickets.sh, that goes green exactly when the fix is correct.