audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs: - Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md - 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer). - Scanner docs from scratch (was zero): architecture, data model, API ref, payment flow, operations runbook + repo README. - Doc-sync updates across API reference, data models, flows, design system. - Secret Rotation Runbook (08 - Operations) for the exposed credentials. - Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js. Issues remain status:open intentionally — the code fixes are uncommitted-then-committed working-tree changes per repo and aren't "resolved" until merged/deployed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ related_models: ["[[SellerOffer]]", "[[PurchaseRequest]]", "[[Notification]]"]
|
||||
related_apis: ["POST /api/marketplace/purchase-requests/:id/offers", "GET /api/marketplace/purchase-requests/:id/offers", "PATCH /api/marketplace/offers/:id"]
|
||||
---
|
||||
|
||||
> **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md))
|
||||
> **Last updated:** 2026-05-30 — updated for offer-management page, `withdrawOffer` action, edit-while-pending, `getSellerOffers` API (commits 240a668–e7d1375)
|
||||
|
||||
# Seller Offer Flow
|
||||
|
||||
@@ -90,24 +90,22 @@ The valid `SellerOffer` statuses are `pending | accepted | rejected | withdrawn`
|
||||
- Notifications: `notifyOfferAccepted` to the winning seller, generic rejection notifications to the others (`SellerOfferService.acceptOffer` does the same in the manual path).
|
||||
- Socket events notify the winner and reject/close competing offers.
|
||||
|
||||
### Withdrawal
|
||||
### Edit / withdrawal while awaiting buyer acceptance
|
||||
|
||||
17. ⚠️ **`POST /api/marketplace/offers/:id/withdraw` does NOT exist as an HTTP route.** The `SellerOfferService.withdrawOffer()` service method exists but is dead code — it is not wired to any controller endpoint.
|
||||
17. While a request is in `received_offers` status (buyer has not yet accepted), the seller may **edit** their pending offer or **withdraw** it entirely from the request-detail step-2 card (`step-2-waiting-for-payment.tsx`).
|
||||
|
||||
The only supported HTTP way to withdraw an offer is:
|
||||
- **Edit**: toggles `mode` to `'edit'` inside `Step2WaitingForPayment`, re-mounts `Step1SendProposal` pre-populated with the existing offer values. On save, calls `PATCH /api/marketplace/offers/:id` (via `updateOffer` action, which now correctly uses `PATCH` instead of the old `PUT`).
|
||||
- **Withdraw**: opens a `ConfirmDialog`, then calls `withdrawOffer(offerId)` in `src/actions/marketplace.ts` which uses `PUT /api/marketplace/offers/:id/status` with `{ status: 'withdrawn' }`.
|
||||
|
||||
```
|
||||
PUT /api/marketplace/offers/:id
|
||||
Body: { status: 'withdrawn' }
|
||||
```
|
||||
`canManageOffer` is only `true` when `requestDetails?.status === 'received_offers'`; once the buyer accepts and the status advances, both buttons are hidden.
|
||||
|
||||
Note also that the frontend page `/dashboard/seller/marketplace/offers` (a "My Offers" listing) **does not exist**. Withdrawal must be triggered from the individual request detail page.
|
||||
The DB filter `{ status: 'pending' }` inside `SellerOfferService.withdrawOffer` means withdrawal is impossible once `accepted` or `rejected`.
|
||||
|
||||
The DB filter `{ status: 'pending' }` inside `withdrawOffer` means withdrawal is impossible once `accepted` or `rejected`.
|
||||
> ⚠️ `POST /api/marketplace/offers/:id/withdraw` still does **not** exist as an HTTP route. Always use `PUT /api/marketplace/offers/:id/status` with `{ status: 'withdrawn' }`.
|
||||
|
||||
### Offer update — method mismatch
|
||||
### Offer update — method mismatch resolved
|
||||
|
||||
> ⚠️ **Known mismatch**: The frontend sends `PUT /marketplace/offers/:id` to update an offer, but the backend route is registered as `PATCH /api/marketplace/offers/:id` (`marketplaceControllerRoutes.ts`). Depending on whether a proxy or middleware normalises the method, one of these may fail. Verify end-to-end and align to a single method.
|
||||
> ✅ **Fixed (commit 240a668)**: The frontend `updateOffer` action now sends `PATCH /api/marketplace/offers/:id`, matching the backend. The `acceptOffer` action was also corrected from `PUT` to `PATCH`.
|
||||
|
||||
## Sequence diagram
|
||||
|
||||
@@ -157,10 +155,10 @@ sequenceDiagram
|
||||
| `POST` | `/api/marketplace/purchase-requests/:id/offers` | Create offer | `purchaseRequestId` is a path param |
|
||||
| `GET` | `/api/marketplace/purchase-requests/:id/offers` | Buyer view of offers on a request | |
|
||||
| `GET` | `/api/marketplace/offers/:id` | Single offer details | |
|
||||
| `PATCH` | `/api/marketplace/offers/:id` | Update price / ETA / notes (seller, while pending) | ⚠️ Frontend sends `PUT`; backend registers `PATCH` — method mismatch |
|
||||
| `PATCH` | `/api/marketplace/offers/:id` | Update price / ETA / notes (seller, while pending) | Fixed: frontend now sends `PATCH` |
|
||||
| `POST` | `/api/marketplace/offers/:id/accept` | Manual acceptance (when not via webhook) | |
|
||||
| ~~`GET /api/marketplace/offers/seller/:sellerId`~~ | — | ~~Seller's own offer history~~ | ⚠️ NOT IMPLEMENTED — `getOffersBySeller()` service method exists but has no HTTP route |
|
||||
| ~~`POST /api/marketplace/offers/:id/withdraw`~~ | — | ~~Seller withdraws~~ | ⚠️ NOT IMPLEMENTED — use `PATCH /api/marketplace/offers/:id` with `{ status: 'withdrawn' }` instead |
|
||||
| `GET` | `/api/marketplace/offers/seller/:sellerId` | All offers by this seller (used by Offer Management page) | Implemented via `getSellerOffers` frontend action (commit 240a668) |
|
||||
| `PUT` | `/api/marketplace/offers/:id/status` | Status mutation — use `{ status: 'withdrawn' }` to withdraw | The only HTTP withdraw path; `POST /api/marketplace/offers/:id/withdraw` does **not** exist |
|
||||
|
||||
## Database writes
|
||||
|
||||
@@ -211,6 +209,9 @@ sequenceDiagram
|
||||
- Backend: `backend/src/services/marketplace/marketplaceController.ts`
|
||||
- Backend: `backend/src/models/SellerOffer.ts`
|
||||
- Backend: `backend/src/services/payment/paymentCoordinator.ts` (payment-state cascade)
|
||||
- Frontend: `frontend/src/sections/request/components/seller-steps/step-1-send-proposal.tsx`
|
||||
- Frontend: `frontend/src/sections/request/components/seller-steps/step-1-send-proposal.tsx` — proposal form (also re-used for edit)
|
||||
- Frontend: `frontend/src/sections/request/components/seller-steps/step-2-waiting-for-payment.tsx` — awaiting-buyer card with edit/withdraw actions
|
||||
- Frontend: `frontend/src/sections/request/components/buyer-steps/step-3-select-and-pay.tsx`
|
||||
- Frontend: `frontend/src/app/dashboard/seller/marketplace/`
|
||||
- Frontend: `frontend/src/app/dashboard/seller/marketplace/` — seller marketplace browse
|
||||
- Frontend: `frontend/src/app/dashboard/seller/marketplace/offers/page.tsx` — Offer Management page (all offers, status filter, withdraw)
|
||||
- Frontend: `frontend/src/actions/marketplace.ts` — `withdrawOffer`, `getSellerOffers` actions
|
||||
|
||||
Reference in New Issue
Block a user