2025-09-15 - 2026-09-15
Overview
193 Pull requests merged by 1 user
Merged
#356 docs(auth): correct the claim that QA could never run Google sign-in
Merged
#355 chore(qa): read the Google credentials from the stack, like every other secret
Merged
#354 fix(auth): offer Google on the sign-up tab, not only on Log In (#345)
Merged
#353 fix(e2e): match an email template tab on its whole name, not a prefix
Merged
#352 feat(auth): offer Google sign-in on the login form (#345)
Merged
#351 feat(auth): the routes that assumed every customer has a password (#344)
Merged
#350 feat(auth): link a Google identity to an account that already exists (#343)
Merged
#349 feat(auth): create an account from a Google identity, then ask about consent (#342)
Merged
#348 fix(local): install dependencies when the lockfile changes, not only when node_modules is absent
Merged
#347 feat(auth): the Google sign-in round trip (#341)
Merged
#346 feat(auth): groundwork for signing in with Google (#340)
Merged
#339 fix(ci): the cleanup script forced TLS onto a plaintext endpoint (#324)
Merged
#338 feat(admin): move a customer's account to an address they can reach (#337)
Merged
#336 feat(passkeys): a password reset takes the passkeys with it (#42)
Merged
#335 feat(passkeys): offer passkey sign-in on the login form (#41)
Merged
#334 feat(passkeys): list, add and revoke from the account page (#40)
Merged
#333 feat(passkeys): authentication ceremony (#39)
Merged
#331 feat(passkeys): registration ceremony (#38)
Merged
#330 feat(passkeys): schema, dependency and per-environment Relying Party (#37)
Merged
#329 docs(ops): feature flags are not worth a manager here (#318)
Merged
#328 fix(admin): refetch Inventory when its tab becomes visible (#327)
Merged
#326 test(ci): record which Postgres actually answered (#154)
Merged
#325 chore(ci): a manually-run workflow to delete old Actions runs (#324)
Merged
#323 fix(tests): the error-handling suite would not compile, and nothing local said so (#307)
Merged
#322 fix(items): read the id strictly, and stop the error test needing a route that does not (#307)
Merged
#321 fix(tests): regenerate the schema mirror and name the controls #56 moved (#320)
Merged
#319 test(e2e): make the resend allowance failure say why (#257)
Merged
#317 feat(analytics): report consenting customers' activity to Brevo (#56)
Merged
#316 fix(admin): keep a modal's controls on screen on a phone (#314)
Merged
#315 docs(ops): the free tier cannot be applied to this hostname (#285)
Merged
#312 fix(tests): close the route guard's factory hole and two regex warnings (#307)
Merged
#310 docs(scripts): record the Node pin decision at the constant (#208)
Merged
#309 Feature/308 kysely dynamic queries
Merged
#306 Feature/305 kysely swap
Merged
#304 docs(db): weigh Kysely against the Drizzle decision (#297)
Merged
#303 Feature/301 rotate photos
Merged
#302 fix(uploads): apply the EXIF orientation before discarding it (#300)
Merged
#299 fix(lint): bring the backend test suites into scope (#298)
Merged
#296 Feature/293 remove backgrounds from inventory
Merged
#295 chore(sonarqube): keep interpolation out of query call sites (#294)
Merged
#292 Feature/260 email the upload link
Merged
#291 fix(scripts): make the database the e2e suite reads a recorded fact (#273)
Merged
#290 fix(admin): let a setting whose default is empty actually be cleared (#280)
Merged
#289 fix(admin): answer 404 for an unreadable id instead of 500 (#207)
Merged
#288 fix(ci): give the end-to-end backend a REMBG_URL so its controls render (#287)
Merged
#286 Feature/269 paginate catalogue
Merged
#284 Feature/281 background removal plan
Merged
#283 docs(intake): design background removal for submitted photos (#281)
Merged
#282 chore: clear the six code smells SonarQube reported (#181)
Merged
#279 docs(ops): setup notes for the rembg background-removal sidecar
Merged
#278 test(integration): say so when the database loses its schema (#154)
Merged
#277 feat(build): pass the commit to the image as a build arg (#248)
Merged
#276 Fix/271 anthropic workspace id
Merged
#275 chore(sonar): name the findings instead of counting them (#181)
Merged
#274 fix(scripts): refuse to reuse a backend that is serving the previous database (#257)
Merged
#270 test(e2e): give the suite a throwaway database of its own (#186)
Merged
#268 test(uploads): stop the cleanup assertions racing the cleanup (#228)
Merged
#267 Fix/197 restrict scan publishing
Merged
#266 Feature/227 submission ceiling
Merged
#265 Feature/224 notification impl
Merged
#263 Feature/224 intake notification
Merged
#262 chore(sonar): remove the rejected Tinqer spike, clear the lint debt, and report measures in CI (#261)
Merged
#259 build(qa): take the SMTP host, port and TLS flag from QA_ stack variables (#258)
Merged
#256 fix(e2e): match the modal OK button exactly so a random suffix cannot collide (#253)
Merged
#255 fix(e2e): restore the frontend build by typing the responses findOrFail searches (#254)
Merged
#251 Feature/223 drafting worker
Merged
#252 Fix/241 e2e isolation part2
Merged
#250 feat(intake): issue named upload links and accept photo submissions (#222)
Merged
#249 chore(ci): remove the registry spike workflow (#237)
Merged
#247 test(e2e): design, plan, and the find-or-fail helper (#241)
Merged
#246 test(e2e): skip the admin save happy-path test while #241 stands (#245)
Merged
#243 test(perf): stop hashing test passwords at production cost (#242)
Merged
#240 fix(test): correct the sold-filter tally assertion stranded by #188 (#239)
Merged
#238 spike(ci): probe whether the runner can build and push an image (#237)
Merged
#236 fix(build): stop the version stamp from breaking every Portainer deploy (#235)
Merged
#234 feat(admin): show the deployed commit and build time in the admin (#233)
Merged
#232 fix(uploads): ship the image backfill script in the container image (#231)
Merged
#229 fix(uploads): re-encode uploaded images to strip EXIF and cut stored bytes (#226)
Merged
#230 spike(db): evaluate Drizzle and Tinqer against the hardest query we have (#216)
Merged
#221 docs(intake): design and implementation plans for the intake pipeline (#220)
Merged
#214 fix(cart): say what the demo button does rather than what the shop does (#203)
Merged
#212 docs(security): put the SQL injection invariant where it is enforced (#202)
Merged
#211 fix(email): stop a demo purchase telling real customers an item sold (#206)
Merged
#210 docs(ops): stop the compose header contradicting itself, and name the four variables step 2 dropped (#204)
Merged
#209 fix(orders): mark a demo order in the history rather than leaving it to read as real (#205)
Merged
#201 fix(scripts): switch Node to a pinned version rather than asking nvm for latest (#198)
Merged
#200 fix(cart): say it is a demo where the customer can see it (#195)
Merged
#199 docs(ops): make the cutover runbook agree with the compose file about DEMO_MODE (#196)
Merged
#193 Feature/192 backup directories
Merged
#192 Feature/191 production demo mode interim
Merged
#191 docs(ops): name the crash loop the cutover runbook was most likely to cause (#175)
Merged
#189 Feature/188 filter dimensions
Merged
#187 feat(filters): show a tag's own colour on its active filter chip (#185)
Merged
#184 refactor: remove the duplicated blocks SonarQube found (#182)
Merged
#183 fix(security): stop refused uploads accumulating on the volume, and record the hotspot review (#180)
Merged
#179 fix(ci): stop the test summarisers failing the job on an unreadable results file (#178)
Merged
#177 docs(ops): write down how to cut production over to the committed compose file (#175)
Merged
#176 ci: fail at the end rather than part way through, so the scan still runs (#174)
Merged
#173 feat(security): stop the app origin serving anything it does not recognise, and make the uploads origin configurable (#103)
Merged
#172 test(perf): measure what concurrent hashing actually costs a bystander request (#163)
Merged
#171 Feature/169 admin filter flyout
Merged
#168 feat(admin): make the placeholder chips insert at the cursor (#143)
Merged
#167 refactor: turn on noUncheckedIndexedAccess in both workspaces (#101)
Merged
#165 refactor(backend): type the remaining query results (#159)
Merged
#166 docs(ci): correct the #154 hypothesis — the database is wiped before the tests start (#154)
Merged
#164 refactor(backend): type the admin item queries, and fix the stale status union (#159)
Merged
#162 refactor(backend): type the customer query results (#159)
Merged
#161 refactor(backend): type the cart and checkout query results (#159)
Merged
#160 refactor(backend): type the item query results (#159)
Merged
#158 refactor(frontend): triage the setState-in-effect sites (#99)
Merged
#157 refactor(frontend): declare props read-only, and drop the deprecated antd prop (#100)
Merged
#156 feat(ops): schedule database and uploads backups, and document the restore (#147)
Merged
#155 ci: let the summarisers summarise and the gate do the failing (#142)
Merged
#153 refactor(storefront): extract the catalogue machine from App.tsx as useCatalogue (#98)
Merged
#152 fix(cart): make the reservation countdown tick, and warn against the real hold (#97)
Merged
#151 Feature/137 convert account specs
Merged
#150 test(e2e): convert the auth specs onto page objects (#137)
Merged
#149 test(e2e): add page objects, fixtures and a typed test build (#137)
Merged
#148 fix(deploy): run the image QA reviewed rather than rebuilding production (#146)
Merged
#145 fix(deploy): commit production's compose and bring it under the drift guard (#118)
Merged
#144 feat(scripts): switch Node automatically, and add a test runner (#140)
Merged
#141 feat(admin): make the token lifetimes, cart hold and greeting configurable (#136)
Merged
#138 feat(admin): give the customer emails a tab of their own (#135)
Merged
#134 feat(admin): filter inventory by status directly, so Published and Unpublished are reachable (#132)
Merged
#133 feat(admin): use the item description's markdown editor for email templates (#131)
Merged
#130 feat: let a customer resend their own verification email (#110)
Merged
#129 feat: filter by Sold / Not sold / All on the storefront and the admin (#105)
Merged
#128 feat: tabs and a rendered preview for the email templates (#119)
Merged
#127 ci: fold the Tests workflow into SonarQube Analysis and delete it (#123)
Merged
#126 feat(scripts): a PowerShell script to start the local environment (#125)
Merged
#124 feat(frontend): give order history a page of its own (#121)
Merged
#122 docs: design for moving Order History onto its own page (#121)
Merged
#120 feat(frontend): let a customer change their own name, password and email (#111)
Merged
#114 ci: give linting its own workflow (#113)
Merged
#113 feat(backend): let a customer change their own name, password and email (#111)
Merged
#112 Feature/92 editable email templates
Merged
#109 Feature/106 customer first last name
Merged
#108 Feature/107 compose required env
Merged
#104 feat(backend): accept only real images in the inventory upload (#95)
Merged
#102 refactor: standardise antd imports and remove the avoidable any (#65)
Merged
#96 feat(backend): check the environment at boot instead of discovering it later (#64)
Merged
#94 Feature/63 admin gate
Merged
#93 feat: stage new items as pending until an admin publishes them (#90)
Merged
#91 feat(frontend): preview an inventory item as a customer sees it (#89)
Merged
#88 feat: let QA send real email, guarded by a recipient allowlist (#87)
Merged
#86 Feature/84 ipv6 rate limit key
Merged
#85 Feature/62 error boundary
Merged
#83 docs: design for React error boundaries (#62)
Merged
#82 Feature/81 technical debt
Merged
#80 chore(ci): declare sonar.projectVersion so the new-code period means something (#79)
Merged
#78 docs: require an issue behind every branch and PR (#77)
Merged
#75 feat(frontend): show the RD mark beside the storefront wordmark
Merged
#73 Feature/61 coverage import
Merged
#70 feat(frontend): add an RD monogram app icon
Merged
#69 fix(ci): make SonarQube actually analyse the frontend (#61)
Merged
#68 Feature/60 eslint
Merged
#66 Feature/59 wrap async routes
Merged
#58 Feature/50 auth modal routes
Merged
#57 48 my account modal
Merged
#55 Feature/favorites filter issue #35
Merged
#54 feat: tell favoriters when an item is withdrawn (#34)
Merged
#53 feat: favorite items and notify when a favorite is sold (#34)
Merged
#47 feat(admin): disable and re-enable customer accounts (#33)
Merged
#46 ci: run backend integration tests as a separate manual workflow
Merged
#45 ci: distinguish between missing secrets and variables in QA build
Merged
#44 ci: distinguish why the QA build daemon is unreachable (#25)
Merged
#43 feat: customer password reset via email round-trip (#32)
Merged
#30 fix(admin): theme, American English, and inventory/reservation tooling (#27)
Merged
#29 docs: make QA review a standing step in the change checklist
Merged
#28 fix: reset the header and return home when a customer logs out (#21)
Merged
#26 Fix/deploy migration safety
Merged
#24 Feature/categories and tags
Merged
#22 test: disambiguate the Email locator in auth.spec
Merged
#20 fix: add the missing /verify-email page
Merged
#19 fix: unset NODE_ENV in e2e job so devDependencies install
Merged
#17 refactor: delete superseded checkout routes and dedupe cart checkout
Merged
#18 chore: sync package-lock with declared dependencies
Merged
#16 fix: resolve SonarQube hotspots for upload size and X-Powered-By
Merged
#15 docs: update project context with deployment lessons and cart architecture
Merged
#14 fix: use node-pg-migrate instead of deleted init.sql in e2e workflow
Merged
#13 fix: remove stale single-item demo-checkout tests, add cart integration tests
Merged
#12 Fix/cart back navigation
Merged
#11 fix: cart badge not updating after account creation due to stale closure
Merged
#10 fix: TypeScript type mismatch between UspsValidationResult and local fallback object
Merged
#9 feat: add customer cart with expiry, shipping addresses with USPS validation, and multi-item PayPal checkout
Merged
#8 fix: Dockerfile
Merged
#7 chore: replace manual SQL migrations with node-pg-migrate
Merged
#6 fix: markdown editor not accepting input due to Form.Item prop injection conflict
Merged
#5 fix: add frontend package-lock.json
Merged
#4 fix: bound admin image uploads and use a CSPRNG for stored filenames
Merged
#2 fix: sonarqube denial of service issue
Merged
#3 ci: fix Postgres service port conflict in CI
Merged
#1 fix: move scripts folder to correct location
1 Pull request proposed by 1 user
Proposed
#357 docs(plans): record the Google sign-in plan where the other plans live
154 Issues closed from 1 user
Closed
#332 feat(auth): sign in with Google, and decide whether Apple follows
Closed
#345 Google 6: the button, and the domain cutover
Closed
#344 Google 5: life without a password
Closed
#343 Google 4: linking a Google identity to an existing account
Closed
#342 Google 3: new accounts, and the consent problem
Closed
#341 Google 2: the OAuth round trip
Closed
#340 Google 1: groundwork — nullable password hash, identities table, config
Closed
#337 Admin: change a customer's email address, for the locked-out case
Closed
#42 Passkeys 6: account recovery and interaction with password reset
Closed
#41 Passkeys 5: login page integration and unsupported-browser fallback
Closed
#40 Passkeys 4: manage and revoke passkeys from the account page
Closed
#39 Passkeys 3: authentication ceremony
Closed
#38 Passkeys 2: registration ceremony
Closed
#37 Passkeys 1: schema, dependency, and per-environment RP configuration
Closed
#318 spike(ops): is a feature flag manager worth adopting, and is a free one viable here
Closed
#327 fix(admin): a published item does not appear in Inventory until the page is reloaded
Closed
#324 chore(ci): a manually-run workflow to delete old Actions runs
Closed
#307 chore(sonar): clean up after the rotation and Kysely branches
Closed
#257 e2e: the resend-verification allowance test fails intermittently in full runs but never in isolation
Closed
#320 fix(tests): #56 left main red — the schema mirror and three positional locators
Closed
#56 Implement the Brevo Tracker
Closed
#314 fix(admin): the item editor modal cannot be closed on a phone
Closed
#285 spike(ops): what Cloudflare's free tier would and would not do for us
Closed
#208 fix(scripts): the Node pin left three docs stale, and the alias check still fails open
Closed
#197 fix(ci): every pull request scan overwrites the dashboard's picture of main
Closed
#308 refactor(db): convert the two dynamic queries to Kysely and retire the S2077 hotspots
Closed
#297 spike(db): finish the Drizzle conversion of the dynamic queries, or reconsider the builder
Closed
#305 refactor(db): swap the query builder from Drizzle to Kysely
Closed
#301 feat(admin): rotate a photo from the review queue and the inventory editor
Closed
#300 fix(uploads): the EXIF strip rotates every photo that was taken in portrait
Closed
#298 fix(lint): backend test files are never linted
Closed
#293 feat(admin): remove an image's background from the inventory item editor
Closed
#294 chore(sonarqube): keep interpolation out of query call sites, and clear the last hotspot
Closed
#260 Upload links should require an email address and send the link to it on creation
Closed
#269 feat(storefront): paginate the catalogue instead of rendering all of it
Closed
#287 fix(ci): three background-removal e2e tests fail because CI never sets REMBG_URL
Closed
#273 fix(scripts): the e2e helper connects to a different database than the app under test
Closed
#280 fix(admin): a text setting that is meant to be empty cannot be cleared
Closed
#207 fix(admin): PUT /admin/items/:id returns 200 with an empty body for an id that does not exist
Closed
#217 feat(db): land the Drizzle schema, config and conventions
Closed
#223 feat(intake): draft a listing from the submitted photos
Closed
#227 feat(intake): global submission ceiling with an abuse alert
Closed
#248 feat(build): pass the commit to the image as a build arg
Closed
#186 Two e2e tests now fail every run locally: the storefront renders 1,662 items unpaginated
Closed
#181 Clear the five code smells, which are the 21 minutes of new technical debt
Closed
#272 fix(intake): Regenerate re-queues the draft but never wakes the worker
Closed
#271 fix(intake): identity-linked Anthropic keys need a workspace id, and drafting has no way to send one
Closed
#281 feat(intake): remove the background from submitted photos
Closed
#116 The e2e suite and the backend integration suite share one database, and the integration suite truncates it
Closed
#241 test(e2e): specs are not isolated — parallel runs interfere through one shared database
Closed
#228 test(uploads): upload cleanup assertions race the cleanup they assert on
Closed
#219 decide: whether generated Drizzle migrations replace node-pg-migrate
Closed
#218 feat(db): convert routes/adminCategories.ts to Drizzle, and measure the real per-site cost
Closed
#220 feat(intake): shared upload links, AI-drafted listings, and an admin review queue
Closed
#245 test(e2e): admin-save-failures happy-path test is skipped and needs restoring
Closed
#224 feat(intake): notify the admin with signed action links
Closed
#225 feat(intake): review queue for drafted items
Closed
#261 SonarQube cleanup: remove the rejected Tinqer spike, clear the lint debt, dedupe the extension map, and report measures in CI
Closed
#258 QA_SMTP_HOST, QA_SMTP_PORT and QA_SMTP_SECURE are set on the stack but nothing reads them
Closed
#253 e2e: name: 'OK' matches as a substring, so random test data can collide with the modal button
Closed
#254 The frontend build fails on main, breaking QA and production deploys (regression from #241)
Closed
#222 feat(intake): issue named upload links and accept photo submissions
Closed
#237 spike: can the runner build and push an image, and can Portainer pull it?
Closed
#242 test(perf): bcrypt cost 12 in tests makes the integration suite slow and timeout-flaky
Closed
#239 fix(test): sold-filter tally assertion has been failing since #188
Closed
#235 fix(build): COPY .git breaks the Portainer build — the version stamp stops deploys
Closed
#233 feat(admin): show the deployed commit and build time in the admin
Closed
#231 fix(uploads): the image backfill script does not ship in the container image
Closed
#226 fix(uploads): re-encode uploaded images to strip EXIF and cut stored bytes
Closed
#216 spike: evaluate Drizzle for type-safe queries and generated migrations
Closed
#203 fix(cart): the demo notice claims the shop is not taking payments, which is false when PayPal is configured
Closed
#202 docs(security): the S2077 justification never reached main, and belongs beside the invariant it describes
Closed
#206 fix(email): a demo purchase emails real customers to say an item sold, with no demo qualifier
Closed
#204 docs(ops): the compose header contradicts itself, and step 2 drops four interpolated variables
Closed
#205 fix(orders): a demo order is indistinguishable from a real one in order history
Closed
#198 fix(scripts): start-local.ps1 asks nvm for a version it cannot have, then blames the wrong thing
Closed
#195 fix(cart): production's demo checkout button is not labelled as a demo
Closed
#196 docs(ops): the cutover runbook contradicts the compose file about DEMO_MODE
Closed
#180 Review and clear the three security hotspots in routes/admin.ts
Closed
#190 Production is in demo mode and taking no money — restore real payments
Closed
#188 Make filtering one composable component both screens extend, rather than a shared drawer with per-screen flags
Closed
#185 Active filter chips render tags without their colour, the only place that does
Closed
#182 Remove the four duplicated blocks, one of which #139 introduced
Closed
#178 The test summarisers fail the job on an unreadable results file, which is the one thing they exist not to do
Closed
#175 There is no runbook for cutting production over to the committed compose file
Closed
#174 A failing integration suite takes the SonarQube scan down with it, and four other steps can do the same
Closed
#103 Serve user uploads from an origin of their own, not the app's
Closed
#163 Concurrent password hashing adds tail latency — measured, and not worth acting on at this traffic
Closed
#139 The storefront filter panel needs searchable multi-select for categories and tags
Closed
#169 Admin inventory filtering should use the same flyout and controls as the storefront
Closed
#143 Make the placeholder chips insert themselves at the cursor instead of only naming themselves
Closed
#101 Turn on noUncheckedIndexedAccess once #65 has typed the query results
Closed
#159 Query results are untyped, so strict: true stops at the database boundary
Closed
#99 Eight setState-in-effect sites: triage which are derived state and which are legitimate
Closed
#100 Seventeen components declare mutable props, and one antd prop is deprecated
Closed
#147 Nothing backs up the production database
Closed
#142 A failing e2e run is reported as a failure in the summary step, and the explicit gate never runs
Closed
#98 App.tsx holds the whole catalogue-fetching machine — extract it as a useCatalogue hook
Closed
#97 The cart's reservation countdown is frozen — it never ticks, and the expiry warning never appears
Closed
#137 Refactor the Playwright suite onto page objects, shared fixtures, and a typed test build
Closed
#118 The compose drift guard covers only ALWAYS_REQUIRED, and only QA — production is unguarded entirely
Closed
#117 QA publishes port 32751 on every interface, not just to the proxy
Closed
#146 Production rebuilds its own image rather than running the one QA tested, and the builds are not reproducible anyway
Closed
#140 start-local should switch Node itself, and there should be a script for running the test suites
Closed
#136 Make the email settings configurable: token lifetimes, cart hold, and the greeting
Closed
#135 Email templates are buried in Settings and squeezed into 720px — give them their own Emails tab
Closed
#132 Admin inventory needs to filter Published / Unpublished, so give it the status dimension directly
Closed
#131 Use the item description's markdown editor for the email templates too
Closed
#110 Let a customer resend their own verification email from My Account
Closed
#105 Filter by Sold / Not Sold / All, on the storefront and the admin inventory
Closed
#119 Email template editor: put the cards in tabs and show a rendered preview
Closed
#123 tests.yml duplicates the suites sonarqube.yml already runs: fold its reporting in and delete it
Closed
#125 A script to start the local environment for testing and review
Closed
#121 Move Order History out of the My Account modal onto its own page
Closed
#92 Make the password-reset email editable from Admin → Settings
Closed
#111 A customer cannot change their own details — PUT /api/customers/me has no caller
Closed
#115 Give linting its own workflow instead of a job inside tests.yml
Closed
#106 Capture first and last name at registration, so emails can greet informally
Closed
#95 The inventory image upload accepts any file type, in the picker and on the server
Closed
#107 QA cannot start: UPLOADS_DIR became required in #64 but the compose file never set it
Closed
#65 Import convention and avoidable any are inconsistent across the codebase
Closed
#64 No boot-time validation of required environment variables
Closed
#63 The admin API has no application-layer authorization — it depends entirely on one proxy regex
Closed
#90 Add a Pending status so items are staged, not published the moment they are created
Closed
#89 Preview an inventory item as a customer sees it, from the admin panel
Closed
#84 IPv6 clients can bypass the password-reset rate limit, and express-rate-limit says so at every boot
Closed
#87 QA cannot send email at all, so the four mail flows have never been regression tested
Closed
#62 No React error boundary — one render error blanks the entire storefront
Closed
#61 SonarQube analyses the code but imports no test coverage, so its quality gate is measuring nothing
Closed
#60 No ESLint anywhere, so the React and SonarJS rules that would catch these problems never run
Closed
#72 Keep the coverage pipeline honest: a frontend unit suite, and guards against silently-empty coverage
Closed
#71 CI authenticates to SonarQube as admin rather than a restricted analysis account
Closed
#81 Clear the 85 minutes of technical debt — four of the fourteen "smells" are real React defects
Closed
#79 new_coverage is 71.2% against an 80% gate — and "new code" is silently the entire codebase
Closed
#76 Show the RD monogram beside the wordmark in the storefront header
Closed
#74 Review and disposition the 3 SQL-injection hotspots in admin.ts — they block the quality gate
Closed
#77 Documented branch convention still permits branches with no issue behind them
Closed
#67 SonarQube silently skips the entire frontend, and the scan still reports success
Closed
#59 30 async route handlers still bypass the error middleware
Closed
#52 Most pages outside the storefront are navigational dead ends
Closed
#36 Passkey (WebAuthn) login for customers
Closed
#50 Sign-in and registration exist twice, and neither route leads back to the shop
Closed
#51 My Account should open as a modal and return the customer where they were
Closed
#49 Pages reached from an email link or bookmark have no site chrome
Closed
#48 My Account is a navigational dead end
Closed
#35 Filter the storefront by favorited items
Closed
#31 Initial Build UI issues and clean-up
Closed
#34 Favorite items, with notification when a favorite sells
Closed
#32 Customer password reset via email round-trip
Closed
#33 Admin: disable a customer account
Closed
#21 Home Page and Customer Reset when logging out.
Closed
#23 Categories and tags
Closed
#25 On-demand QA environment stack
Closed
#27 Admin Category and Tag UI issues
156 Issues created by 1 user
Opened
#21 Home Page and Customer Reset when logging out.
Opened
#23 Categories and tags
Opened
#25 On-demand QA environment stack
Opened
#27 Admin Category and Tag UI issues
Opened
#31 Initial Build UI issues and clean-up
Opened
#32 Customer password reset via email round-trip
Opened
#33 Admin: disable a customer account
Opened
#34 Favorite items, with notification when a favorite sells
Opened
#35 Filter the storefront by favorited items
Opened
#36 Passkey (WebAuthn) login for customers
Opened
#37 Passkeys 1: schema, dependency, and per-environment RP configuration
Opened
#38 Passkeys 2: registration ceremony
Opened
#39 Passkeys 3: authentication ceremony
Opened
#40 Passkeys 4: manage and revoke passkeys from the account page
Opened
#41 Passkeys 5: login page integration and unsupported-browser fallback
Opened
#42 Passkeys 6: account recovery and interaction with password reset
Opened
#48 My Account is a navigational dead end
Opened
#49 Pages reached from an email link or bookmark have no site chrome
Opened
#50 Sign-in and registration exist twice, and neither route leads back to the shop
Opened
#51 My Account should open as a modal and return the customer where they were
Opened
#52 Most pages outside the storefront are navigational dead ends
Opened
#56 Implement the Brevo Tracker
Opened
#59 30 async route handlers still bypass the error middleware
Opened
#60 No ESLint anywhere, so the React and SonarJS rules that would catch these problems never run
Opened
#61 SonarQube analyses the code but imports no test coverage, so its quality gate is measuring nothing
Opened
#62 No React error boundary — one render error blanks the entire storefront
Opened
#63 The admin API has no application-layer authorization — it depends entirely on one proxy regex
Opened
#64 No boot-time validation of required environment variables
Opened
#65 Import convention and avoidable any are inconsistent across the codebase
Opened
#67 SonarQube silently skips the entire frontend, and the scan still reports success
Opened
#71 CI authenticates to SonarQube as admin rather than a restricted analysis account
Opened
#72 Keep the coverage pipeline honest: a frontend unit suite, and guards against silently-empty coverage
Opened
#74 Review and disposition the 3 SQL-injection hotspots in admin.ts — they block the quality gate
Opened
#76 Show the RD monogram beside the wordmark in the storefront header
Opened
#77 Documented branch convention still permits branches with no issue behind them
Opened
#79 new_coverage is 71.2% against an 80% gate — and "new code" is silently the entire codebase
Opened
#81 Clear the 85 minutes of technical debt — four of the fourteen "smells" are real React defects
Opened
#84 IPv6 clients can bypass the password-reset rate limit, and express-rate-limit says so at every boot
Opened
#87 QA cannot send email at all, so the four mail flows have never been regression tested
Opened
#89 Preview an inventory item as a customer sees it, from the admin panel
Opened
#90 Add a Pending status so items are staged, not published the moment they are created
Opened
#92 Make the password-reset email editable from Admin → Settings
Opened
#95 The inventory image upload accepts any file type, in the picker and on the server
Opened
#97 The cart's reservation countdown is frozen — it never ticks, and the expiry warning never appears
Opened
#98 App.tsx holds the whole catalogue-fetching machine — extract it as a useCatalogue hook
Opened
#99 Eight setState-in-effect sites: triage which are derived state and which are legitimate
Opened
#100 Seventeen components declare mutable props, and one antd prop is deprecated
Opened
#101 Turn on noUncheckedIndexedAccess once #65 has typed the query results
Opened
#103 Serve user uploads from an origin of their own, not the app's
Opened
#105 Filter by Sold / Not Sold / All, on the storefront and the admin inventory
Opened
#106 Capture first and last name at registration, so emails can greet informally
Opened
#107 QA cannot start: UPLOADS_DIR became required in #64 but the compose file never set it
Opened
#110 Let a customer resend their own verification email from My Account
Opened
#111 A customer cannot change their own details — PUT /api/customers/me has no caller
Opened
#115 Give linting its own workflow instead of a job inside tests.yml
Opened
#116 The e2e suite and the backend integration suite share one database, and the integration suite truncates it
Opened
#117 QA publishes port 32751 on every interface, not just to the proxy
Opened
#118 The compose drift guard covers only ALWAYS_REQUIRED, and only QA — production is unguarded entirely
Opened
#119 Email template editor: put the cards in tabs and show a rendered preview
Opened
#121 Move Order History out of the My Account modal onto its own page
Opened
#123 tests.yml duplicates the suites sonarqube.yml already runs: fold its reporting in and delete it
Opened
#125 A script to start the local environment for testing and review
Opened
#131 Use the item description's markdown editor for the email templates too
Opened
#132 Admin inventory needs to filter Published / Unpublished, so give it the status dimension directly
Opened
#135 Email templates are buried in Settings and squeezed into 720px — give them their own Emails tab
Opened
#136 Make the email settings configurable: token lifetimes, cart hold, and the greeting
Opened
#137 Refactor the Playwright suite onto page objects, shared fixtures, and a typed test build
Opened
#139 The storefront filter panel needs searchable multi-select for categories and tags
Opened
#140 start-local should switch Node itself, and there should be a script for running the test suites
Opened
#142 A failing e2e run is reported as a failure in the summary step, and the explicit gate never runs
Opened
#143 Make the placeholder chips insert themselves at the cursor instead of only naming themselves
Opened
#146 Production rebuilds its own image rather than running the one QA tested, and the builds are not reproducible anyway
Opened
#147 Nothing backs up the production database
Opened
#154 The integration suite's Postgres service loses its schema mid-run in CI, failing 12 of 17 suites
Opened
#159 Query results are untyped, so strict: true stops at the database boundary
Opened
#163 Concurrent password hashing adds tail latency — measured, and not worth acting on at this traffic
Opened
#169 Admin inventory filtering should use the same flyout and controls as the storefront
Opened
#174 A failing integration suite takes the SonarQube scan down with it, and four other steps can do the same
Opened
#175 There is no runbook for cutting production over to the committed compose file
Opened
#178 The test summarisers fail the job on an unreadable results file, which is the one thing they exist not to do
Opened
#180 Review and clear the three security hotspots in routes/admin.ts
Opened
#181 Clear the five code smells, which are the 21 minutes of new technical debt
Opened
#182 Remove the four duplicated blocks, one of which #139 introduced
Opened
#185 Active filter chips render tags without their colour, the only place that does
Opened
#186 Two e2e tests now fail every run locally: the storefront renders 1,662 items unpaginated
Opened
#188 Make filtering one composable component both screens extend, rather than a shared drawer with per-screen flags
Opened
#190 Production is in demo mode and taking no money — restore real payments
Opened
#195 fix(cart): production's demo checkout button is not labelled as a demo
Opened
#196 docs(ops): the cutover runbook contradicts the compose file about DEMO_MODE
Opened
#197 fix(ci): every pull request scan overwrites the dashboard's picture of main
Opened
#198 fix(scripts): start-local.ps1 asks nvm for a version it cannot have, then blames the wrong thing
Opened
#202 docs(security): the S2077 justification never reached main, and belongs beside the invariant it describes
Opened
#203 fix(cart): the demo notice claims the shop is not taking payments, which is false when PayPal is configured
Opened
#204 docs(ops): the compose header contradicts itself, and step 2 drops four interpolated variables
Opened
#205 fix(orders): a demo order is indistinguishable from a real one in order history
Opened
#206 fix(email): a demo purchase emails real customers to say an item sold, with no demo qualifier
Opened
#207 fix(admin): PUT /admin/items/:id returns 200 with an empty body for an id that does not exist
Opened
#208 fix(scripts): the Node pin left three docs stale, and the alias check still fails open
Opened
#216 spike: evaluate Drizzle for type-safe queries and generated migrations
Opened
#217 feat(db): land the Drizzle schema, config and conventions
Opened
#218 feat(db): convert routes/adminCategories.ts to Drizzle, and measure the real per-site cost
Opened
#219 decide: whether generated Drizzle migrations replace node-pg-migrate
Opened
#220 feat(intake): shared upload links, AI-drafted listings, and an admin review queue
Opened
#222 feat(intake): issue named upload links and accept photo submissions
Opened
#223 feat(intake): draft a listing from the submitted photos
Opened
#224 feat(intake): notify the admin with signed action links
Opened
#225 feat(intake): review queue for drafted items
Opened
#226 fix(uploads): re-encode uploaded images to strip EXIF and cut stored bytes
Opened
#227 feat(intake): global submission ceiling with an abuse alert
Opened
#228 test(uploads): upload cleanup assertions race the cleanup they assert on
Opened
#231 fix(uploads): the image backfill script does not ship in the container image
Opened
#233 feat(admin): show the deployed commit and build time in the admin
Opened
#235 fix(build): COPY .git breaks the Portainer build — the version stamp stops deploys
Opened
#237 spike: can the runner build and push an image, and can Portainer pull it?
Opened
#239 fix(test): sold-filter tally assertion has been failing since #188
Opened
#241 test(e2e): specs are not isolated — parallel runs interfere through one shared database
Opened
#242 test(perf): bcrypt cost 12 in tests makes the integration suite slow and timeout-flaky
Opened
#245 test(e2e): admin-save-failures happy-path test is skipped and needs restoring
Opened
#248 feat(build): pass the commit to the image as a build arg
Opened
#253 e2e: name: 'OK' matches as a substring, so random test data can collide with the modal button
Opened
#254 The frontend build fails on main, breaking QA and production deploys (regression from #241)
Opened
#257 e2e: the resend-verification allowance test fails intermittently in full runs but never in isolation
Opened
#258 QA_SMTP_HOST, QA_SMTP_PORT and QA_SMTP_SECURE are set on the stack but nothing reads them
Opened
#260 Upload links should require an email address and send the link to it on creation
Opened
#261 SonarQube cleanup: remove the rejected Tinqer spike, clear the lint debt, dedupe the extension map, and report measures in CI
Opened
#269 feat(storefront): paginate the catalogue instead of rendering all of it
Opened
#271 fix(intake): identity-linked Anthropic keys need a workspace id, and drafting has no way to send one
Opened
#272 fix(intake): Regenerate re-queues the draft but never wakes the worker
Opened
#273 fix(scripts): the e2e helper connects to a different database than the app under test
Opened
#280 fix(admin): a text setting that is meant to be empty cannot be cleared
Opened
#281 feat(intake): remove the background from submitted photos
Opened
#285 spike(ops): what Cloudflare's free tier would and would not do for us
Opened
#287 fix(ci): three background-removal e2e tests fail because CI never sets REMBG_URL
Opened
#293 feat(admin): remove an image's background from the inventory item editor
Opened
#294 chore(sonarqube): keep interpolation out of query call sites, and clear the last hotspot
Opened
#297 spike(db): finish the Drizzle conversion of the dynamic queries, or reconsider the builder
Opened
#298 fix(lint): backend test files are never linted
Opened
#300 fix(uploads): the EXIF strip rotates every photo that was taken in portrait
Opened
#301 feat(admin): rotate a photo from the review queue and the inventory editor
Opened
#305 refactor(db): swap the query builder from Drizzle to Kysely
Opened
#307 chore(sonar): clean up after the rotation and Kysely branches
Opened
#308 refactor(db): convert the two dynamic queries to Kysely and retire the S2077 hotspots
Opened
#313 chore(ops): move to a domain we control and proxy the origin through Cloudflare
Opened
#314 fix(admin): the item editor modal cannot be closed on a phone
Opened
#318 spike(ops): is a feature flag manager worth adopting, and is a free one viable here
Opened
#320 fix(tests): #56 left main red — the schema mirror and three positional locators
Opened
#324 chore(ci): a manually-run workflow to delete old Actions runs
Opened
#327 fix(admin): a published item does not appear in Inventory until the page is reloaded
Opened
#332 feat(auth): sign in with Google, and decide whether Apple follows
Opened
#337 Admin: change a customer's email address, for the locked-out case
Opened
#340 Google 1: groundwork — nullable password hash, identities table, config
Opened
#341 Google 2: the OAuth round trip
Opened
#342 Google 3: new accounts, and the consent problem
Opened
#343 Google 4: linking a Google identity to an existing account
Opened
#344 Google 5: life without a password
Opened
#345 Google 6: the button, and the domain cutover