Reference: original Django application at b74e8ed. Functional fixes are outside
this exercise's agreed scope. The contract captures include database effects.
| Trigger | Existing behavior | Treatment |
|---|---|---|
| Create a post with a missing tag after an existing tag | 500 after persisting the post and earlier tag associations | Preserve and test partial state |
| Find an email shared by multiple users | Unhandled multiple-record error, 500 | Preserve, do not add uniqueness |
| Read a post | Increments view_count and modifies updated_at |
Preserve both effects |
| Concurrent reads of the same post | Read/modify/save can lose increments and overwrite other concurrent changes | Document race; do not introduce an atomic increment |
| Fetch an unpublished post by ID | Returns the draft and comments | Preserve; list/search still filter drafts |
| List/search/tag endpoints | Return all matches, without pagination | Preserve response scope and measure full payloads |
| Post title longer than 255 characters | Input schema permits it; database rejects the insert | Capture the failure instead of adding new validation |
None of these is presented as newly discovered author intent. They are observable
consequences of the supplied code. Runtime captures live in fixtures/contract/.
The Gunicorn reference also emitted unexpected body bytes after a HEAD 405 response,
which Go's HTTP client logged as an unsolicited response. The contract checks its
405 status and Allow header. It does not reproduce invalid connection framing or
compare a HEAD entity body, which the HTTP client does not expose. Transport framing
is explicitly outside the comparison fields in methodology.md.