SAMPLE: a fictional business. Not a client of Adwaizer.
Software fix: written scope, failing test, fix summary and handover for Ferngate Cycle Hire (sample)
Prepared by: Adwaizer AI Consulting Inc. · Engagement: Software fix or small feature, Starter tier (one fix in one repository, 5 working days) · Report version: sample, October 2026
What this page is. This is everything a client receives on a software fix order: the written scope on day 1, the failing test on day 2, the fix summary on days 3-4 and the handover note, acceptance checklist and Upwork milestone text on day 5. Ferngate Cycle Hire, its repository ferngate/booking-api and every file, test, commit, pull request and number below are invented to show the format and depth; none of them exist. In a real order every row points to the client's own repository, pull request and test run.
How to read the evidence. "Evidence" is what the client can open and check: a file and line in their repository, a test name, a command and its output. Nothing here was run against a real application.
1. Written scope (day 1, agreed in writing before any build)
Ferngate Cycle Hire is a fictional bike rental shop. Its booking API is a Python 3.12 FastAPI app with a PostgreSQL database and a pytest suite. The owner reported one bug through Upwork messages, with two screenshots of the booking page.
Run before any change. We cloned the repository at main @ 4be1c07 (fictional), followed the README (pip install -r requirements-dev.txt, pytest) and recorded today's result: 96 passed, 0 failed, 2 skipped (the two skips need a live payment sandbox and are skipped in the client's CI too).
| Item | Agreed |
|---|---|
| The one bug | A bike that is returned at a given time cannot be booked by the next customer from that same time. Example from the client: bike #14 booked 10:00-13:00, then a request for 13:00-16:00 on the same day is refused with "This bike is not available for the times you chose." |
| Expected behaviour (client's words) | "If a bike comes back at 1, someone else should be able to take it at 1. Back-to-back bookings are how we fill the weekend." |
| What is seen | HTTP 409 from POST /bookings with {"detail": "bike_unavailable"} for any booking that starts exactly when another ends, or ends exactly when another starts |
| Files likely touched | app/bookings/availability.py (the overlap check); a new test in tests/bookings/test_availability.py |
| Out of scope | The buffer time between bookings for cleaning (the client asked about it; it is a new feature, quoted separately if wanted); the booking page front end; the payment flow; any database migration; the two skipped payment tests |
| Acceptance checks | The four checks in section 4.2 |
Risk and issue log (day 1).
| # | Risk or issue | Owner | Status |
|---|---|---|---|
| R1 | Bookings are stored in local time without a time zone; a fix that compares times differently could shift bookings around the daylight-saving change | Adwaizer | Closed on day 3: the fix keeps the same comparison type and the existing DST tests still pass |
| R2 | The repository's .env.example holds a placeholder payment key only; no real key was found in the repository or its history | Adwaizer | Closed: nothing to rotate |
| I1 | The client's CI runs on pushes to main only, so a pull request does not show a CI run | Client | Open: the test run is shown in the pull request description instead (section 3.3) |
The scope above was sent through Upwork messages on day 1 and the client replied "Yes, go ahead" the same day. No build started before that reply.
2. The failing test (day 2)
The bug reproduces on the client's current code in their own framework (pytest). The test books bike #14 for 10:00-13:00 and then asks for the same bike from 13:00 to 16:00. It also checks the case the client did not mention but the same line causes: a booking that ends exactly when an existing one starts.
# tests/bookings/test_availability.py
from datetime import datetime
import pytest
from app.bookings.availability import is_available
from tests.factories import make_booking
@pytest.mark.parametrize(
"start, end",
[
("2026-10-24T13:00", "2026-10-24T16:00"), # starts when the earlier booking ends
("2026-10-24T07:00", "2026-10-24T10:00"), # ends when the earlier booking starts
],
)
def test_back_to_back_booking_is_available(db, start, end):
make_booking(db, bike_id=14, start="2026-10-24T10:00", end="2026-10-24T13:00")
assert is_available(
db,
bike_id=14,
start=datetime.fromisoformat(start),
end=datetime.fromisoformat(end),
)
def test_overlapping_booking_is_still_refused(db):
make_booking(db, bike_id=14, start="2026-10-24T10:00", end="2026-10-24T13:00")
assert not is_available(
db,
bike_id=14,
start=datetime.fromisoformat("2026-10-24T12:59"),
end=datetime.fromisoformat("2026-10-24T16:00"),
)
Result on the client's current code (pytest tests/bookings/test_availability.py):
FAILED tests/bookings/test_availability.py::test_back_to_back_booking_is_available[2026-10-24T13:00-2026-10-24T16:00] - assert False
FAILED tests/bookings/test_availability.py::test_back_to_back_booking_is_available[2026-10-24T07:00-2026-10-24T10:00] - assert False
PASSED tests/bookings/test_availability.py::test_overlapping_booking_is_still_refused
2 failed, 1 passed
The two back-to-back cases fail, which is the client's bug. The overlap case passes today and must keep passing after the fix, so a real double booking is still refused.
Root cause. app/bookings/availability.py, line 31 (fictional) treats two bookings as overlapping when existing.start <= requested.end and requested.start <= existing.end. With <=, a booking ending at 13:00 and one starting at 13:00 count as overlapping. Booking times are half-open (a bike returned at 13:00 is free at 13:00), so the comparison needs <.
Gabriel read the failing test on day 2 and confirmed with the client that it shows their bug.
3. Fix summary (days 3-4)
3.1 The change in words
One comparison changed in one function: in is_available, two bookings now overlap only when existing.start < requested.end and requested.start < existing.end. Nothing else in the booking logic changed. The SQL query that loads candidate bookings used the same <= filter, so it was changed to < in the same way; without that the query would still return the back-to-back booking and the function would refuse it.
| File | Change |
|---|---|
app/bookings/availability.py | The overlap test and the candidate query use < instead of <= (2 lines), plus a one-line comment that booking times are half-open |
tests/bookings/test_availability.py (new) | The three tests from section 2 |
Pull request: ferngate/booking-api#41 (fictional), from the branch fix/back-to-back-bookings. Diff: +46 / -2 lines in 2 files. No dependency, migration, setting or environment variable changed.
3.2 Considered and left alone
- A cleaning buffer between bookings. The client mentioned wanting 15 minutes between hires. That changes the rule rather than fixing it, so it is out of scope and listed in the handover note as a possible next order.
- Time zones. Bookings are stored without a time zone (R1). The fix does not change that; changing it would touch every booking row.
3.3 Checks run on the change
| Check | Result |
|---|---|
| New tests on the old code | 2 failed, 1 passed (section 2) |
| New tests with the change | 3 passed |
Full suite (pytest) | 99 passed, 0 failed, 2 skipped (96 existing + 3 new; the same 2 payment-sandbox skips as on day 1) |
Lint and typecheck of the changed files (ruff check, mypy app/bookings/availability.py) | 0 errors |
| Separate reviewer session against the written scope | Approved; only the two scoped files changed; asked for the half-open comment, which was added |
Secret scan of the commits (gitleaks) | No leaks found |
| Gabriel's read of the reviewed pull request | Approved for handover |
4. Handover note and acceptance checklist (day 5)
4.1 Handover note
What changed and why. A bike returned at a given time can now be booked from that same time. The availability check treated a booking ending at 13:00 and one starting at 13:00 as a clash; it now treats booking times as "from the start, up to but not including the end", which is how the shop already works at the counter. A real overlap (even one minute) is still refused.
How to run the new test.
pip install -r requirements-dev.txt
pytest tests/bookings/test_availability.py
Expect 3 passed. To see the bug on your old code, check out main before the pull request, keep the new test file, and run the same command: the two back-to-back cases fail.
What to check in the app after you merge. On your staging site, book any bike for 10:00-13:00, then book the same bike for 13:00-16:00. The second booking is accepted. Then try 12:30-16:00: it is refused.
Found and left alone.
| Item | Why it matters | Suggested next step |
|---|---|---|
| Cleaning buffer between bookings | You asked about it; it is a new rule, not this fix | A separate small-feature order if you want it |
| Booking times stored without a time zone | A risk around the daylight-saving change if you ever open a second location in another zone | Nothing needed today; worth a look before any multi-location work |
CI runs on main only | A pull request shows no test run until it is merged | Add pull_request to the trigger in your CI file (a one-line change you can make) |
4.2 Acceptance checklist (for the client)
- The new test fails on your old code and passes with the change (
pytest tests/bookings/test_availability.py). - Your existing test suite still passes (
pytest: 99 passed, 2 skipped, as shown in the pull request). - On staging, a back-to-back booking for the same bike is accepted and an overlapping one is refused, as described in the handover note.
- No file changed outside
app/bookings/availability.py, except the new test file.
A failed check is fixed within 5 working days at no charge. You merge the pull request and deploy it; we never merge to your main branch or deploy.
5. Upwork milestone submission text
Hi, the fix for back-to-back bookings is ready for your review.
- Pull request: ferngate/booking-api#41 (2 files: the availability check and a new test file).
- What changed: a bike returned at a given time can now be booked from that same time; overlapping bookings are still refused.
- Proof: the new test fails on your current code and passes with the fix. Your full suite passes (99 passed, 2 skipped, the same 2 skips as before).
- The handover note in the pull request explains how to run the test, what to check on staging, and three things we noticed and left alone (the cleaning buffer you mentioned is one of them).
Please go through the 4-item acceptance checklist in the pull request. If anything does not check out, tell me here and we fix it within 5 working days at no charge. Once you are happy, please approve this milestone and merge the pull request yourself; we do not merge or deploy.
Thank you.