Changeset 9577c79 for docs/P4-Prototype/PrototypeImplementationAIUsage.md
- Timestamp:
- 09/16/26 23:37:15 (13 days ago)
- Branches:
- main
- Children:
- 8b447ef
- Parents:
- df05838
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
docs/P4-Prototype/PrototypeImplementationAIUsage.md
rdf05838 r9577c79 39 39 ## Summary of AI involvement 40 40 41 | | Session 1 — 2026-04-21 | Session 2 — 2026-08-06/07 | 42 |---|---|---| 43 | **What I brought** | My existing Go backend (Chi HTTP handlers) and a half-finished frontend | The CLI prototype as it stood after session 1 | 44 | **What the AI did** | Rewrote the backend as a CLI covering UC0001–UC0007, wrote the market bot | Reviewed the code, found and fixed three bugs, improved the holding upsert | 45 | **What I decided** | To delete the frontend, to build a CLI rather than a web app, to keep the market simulator | To ask for a code review pass rather than documentation alone | 41 | | Session 1 — 2026-04-21 | Session 2 — 2026-08-06/07 | Session 3 — 2026-09-16 | 42 |---|---|---|---| 43 | **What I brought** | My existing Go backend (Chi HTTP handlers) and a half-finished frontend | The CLI prototype as it stood after session 1 | A design review: the sell path had no way to reserve crypto committed to an order | 44 | **What the AI did** | Rewrote the backend as a CLI covering UC0001–UC0007, wrote the market bot | Reviewed the code, found and fixed three bugs, improved the holding upsert | Added `holdings.reserved_quantity`, changed the sell path to reserve-then-settle, gave `Orders.status` a real lifecycle, tested it live including under real concurrency | 45 | **What I decided** | To delete the frontend, to build a CLI rather than a web app, to keep the market simulator | To ask for a code review pass rather than documentation alone | To keep reserve+settle in one transaction rather than split it across two, since there is no cancel-order use case to recover a stuck reservation | 46 46 47 47 The prototype was built in session 1 and worked. What session 2 added was a … … 116 116 > presentation. You will be asked how the buy transaction works, and the answer 117 117 > has to be yours. 118 119 ### Session 3 — 2026-09-16 120 121 Prompted by a design review I did myself, logged in full in 122 [ERModelAIUsage](../P1-ConceptualModel/ERModelAIUsage.md#session-3--2026-09-16). 123 The report: a user who owns 2 BTC and places a sell order for 0.5 BTC has that 124 crypto immediately removed from `quantity`, but nothing in the model recorded 125 that a *pending* order had already committed part of a position before it 126 settled — `holdings` had `quantity` and `avg_price` only, no equivalent of the 127 `available_balance`/`invested_balance` split already used for cash. 128 129 **Bug fixed** 130 131 `server/trade.go`'s sell path checked `held < qty` directly against 132 `holdings.quantity`. This happened to be safe against concurrent double-sells 133 only because the whole operation — order, holding check, holding update, 134 balance update, ledger, trade — runs inside one transaction with a 135 `SELECT ... FOR UPDATE` lock. It was not safe against the actual scenario 136 described: nothing distinguished "owned" from "owned, but already promised to 137 this order," which matters the moment an order can legitimately sit `open` 138 across more than one transaction — exactly what a limit-order matcher would 139 need, and what `orders.status` already implied was coming. 140 141 **What changed** 142 143 1. `holdings.reserved_quantity numeric(20,4) NOT NULL DEFAULT 0 CHECK 144 (reserved_quantity >= 0 AND reserved_quantity <= quantity)` added to 145 `schema_creation.sql`. 146 2. `v_portfolio` gained `reserved_quantity` and derived `available_quantity`. 147 3. `trade.go`'s sell path now: locks the holding, computes 148 `available := quantity - reserved_quantity`, rejects if `available < qty` 149 (previously: rejects if `quantity < qty`), reserves 150 (`reserved_quantity += qty`), then settles (`quantity -= qty; 151 reserved_quantity -= qty`) — two statements instead of one, kept inside the 152 same transaction rather than split into two commits, which would risk an 153 order stuck `open` with reserved crypto and no cancel command to free it. 154 4. Both `PlaceOrder` branches now insert the order as `status='open'` and 155 `UPDATE ... SET status='executed', executed_at=now()` at the end, instead 156 of inserting `'executed'` directly — `Orders.status` is now a real 157 lifecycle rather than a label written once. 158 5. `portfolio.go` gained `Reserved`/`Available` columns, reading 159 `v_portfolio.reserved_quantity`/`available_quantity`, so the new field is 160 something a Trader can actually see. 161 6. The error message on the sell path changed from 162 `"Insufficient holding: trying to sell X, hold Y"` to 163 `"Insufficient holding: trying to sell X, available Y (of Z held, W 164 reserved)"`, since "how much you hold" is no longer the only number that 165 matters. 166 167 **Test evidence** 168 169 Verified against PostgreSQL 16 (`bp_database`, `localhost:5433`): 170 171 ``` 172 $ eduberza sell 0.5 BTC (Alice: 2.0000 BTC held, 0.0000 reserved) 173 Order executed: sell 0.5000 BTC @ 67140.000000 (notional 33570.0000 USD) 174 # holdings.quantity: 2.0000 -> 1.5000, reserved_quantity: 0.0000 (unchanged net of reserve+release) 175 176 $ eduberza sell 1 ETH (Bob: no holdings row at all) 177 Insufficient holding: trying to sell 1.0000, available 0.0000 (of 0.0000 held, 0.0000 reserved) 178 179 # Two concurrent processes, Alice at 1.5 BTC / 0 reserved, each selling 1.0 BTC: 180 === process A === Insufficient holding: trying to sell 1.0000, available 0.5000 (of 0.5000 held, 0.0000 reserved) 181 === process B === Order executed: sell 1.0000 BTC @ 67140.000000 (notional 67140.0000 USD) 182 # final holding: quantity 0.5000, reserved_quantity 0.0000 — exactly one sell went through 183 184 # Reserve visible mid-transaction, in one psql session (BEGIN; ...; COMMIT;): 185 before: quantity 2.0000, reserved_quantity 0.0000, available 2.0000 186 after reserve: quantity 2.0000, reserved_quantity 0.5000, available 1.5000 187 after settle: quantity 1.5000, reserved_quantity 0.0000, available 1.5000 188 189 # The CHECK constraint holds even without going through trade.go: 190 UPDATE holdings SET reserved_quantity = quantity + 1 WHERE ...; 191 ERROR: new row for relation "holdings" violates check constraint "holdings_check" 192 ``` 193 194 Full transcripts are on 195 [UseCase0005Implementation](UseCase0005Implementation.md). The rejected-order 196 rollback guarantee from session 2 was re-checked too: after both the 197 insufficient-funds and insufficient-holding failure paths, the affected user 198 still has zero new rows in `orders`. 199 200 **What I decided:** to keep reserve and settle inside a single transaction — 201 splitting them into two, so an order genuinely sits `open` and reserved 202 between two commits, is what a real limit-order matcher will eventually need, 203 but building that now would add a way for an order to get stuck without also 204 building a way to cancel it, which is out of scope for this review. `go build 205 ./...` was run after every change; all seven use cases were re-exercised 206 manually.
Note:
See TracChangeset
for help on using the changeset viewer.
