◐ Off-By-One · answer catalog

go-release-tag-stale

1 answer(s)godocker

go-release-tag-stale

📦 Source in repository (JSON)

Answer

Root cause. v0.1.0 was tagged 53 commits behind HEAD and its handlers shipped bodies that violated the OpenAPI contract (POST /cancel → {"status":"ok"}; DELETE /sessions/{id} → 204 No Content). Since go get @latest resolves the highest semver tag from the proxy/VCS tags — not HEAD — consumers kept pulling the stale, non-compliant build. The fix is two-layered: (1) make the wire bodies compliant at HEAD, (2) re-release a higher tag at HEAD so @latest resolves the fixed code.

1. Fix the wire bodies (api.go)

Before (shipped in v0.1.0):

// BUG: contract says {"cancelled","cancelled_decision_id"}; 404 when unknown
type cancelReply struct{ Status string `json:"status"` }

func (s *server) handleCancel(w http.ResponseWriter, r *http.Request) {
    ...
    writeJSON(w, http.StatusOK, cancelReply{Status: "ok"})   // always "ok"
}

// BUG: contract says 200 {"terminated","session_id"}
func (s *server) handleTerminate(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNoContent)                       // 204, empty body
}

After (the fix, tagged v0.1.1):

// CancelResponse: {"cancelled": true, "cancelled_decision_id": "..."}
type CancelResponse struct {
    Cancelled           bool   `json:"cancelled"`
    CancelledDecisionID string `json:"cancelled_decision_id"`
}
type TerminateResponse struct {
    Terminated bool   `json:"terminated"`
    SessionID  string `json:"session_id"`
}

func (s *server) handleCancel(w http.ResponseWriter, r *http.Request) {
    // ...decode req...
    if !s.knownDecision(req.DecisionID) {
        writeJSON(w, http.StatusNotFound, errorResponse{Error: "decision not found"})
        return
    }
    writeJSON(w, http.StatusOK, CancelResponse{Cancelled: true, CancelledDecisionID: req.DecisionID})
}

func (s *server) handleTerminate(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    writeJSON(w, http.StatusOK, TerminateResponse{Terminated: true, SessionID: id}) // 200, idempotent
}

Regression tests (api_test.go) assert: cancel body has cancelled+cancelled_decision_id and no status field, cancel-unknown → 404, delete → 200 with terminated+session_id.

2. CHANGELOG (CHANGELOG.md)

Rename the mislabeled header ## [1.0.0] → ## [0.1.0] (matches the actual tag), and prepend:

```markdown

Evidence & signatures

Reproduced end-to-end in `/tmp/runner-demo/runner` (module `example.com/runner`, `v0.1.0` tagged at commit 1, 52 unrelated commits, fix commit #54 at HEAD, annotated tag, bare origin, file-based module proxy). Real outputs:

**Staleness (before fix):** `git rev-list --count v0.1.0..HEAD` → **53**. Proxy containing only `v0.1.0`: `go list -m -json @latest` → `"Version": "v0.1.0"` (the broken tag wins).

**Broken `v0.1.0` live probe:** `cancel` → `{"status":"ok"}`; `delete` → HTTP **204**, empty body (confirmed the non-compliance).

**Fixed HEAD / `v0.1.1` live probe:**
```
process:        HTTP 200  body: {"session_id":"sess-00000001"}
cancel-unknown: HTTP 404  body: {"error":"decision not found"}
delete:         HTTP 200  body: {"terminated":true,"session_id":"sess-00000001"}
```

**Guard 4/4:**
1. **CHANGELOG** — `## [v0.1.1]` header present; `## [1.0.0]` mislabeled header removed; `## [0.1.0]` present. ✓
2. **Tag** — `v0.1.1` annotated (`git cat-file -t` → `tag`), `git tag --points-at HEAD` → `v0.1.1`; pushed to origin (`refs/tags/v0.1.1` + peeled `^{}` on remote). ✓
3. **@latest** — after adding `v0.1.1` to the proxy: `go list -m -f '@latest -> {{.Version}}'` → **`@latest -> v0.1.1`**; module zip validated by `go mod download -json example.com/runner@v0.1.1`. ✓
4. **Wire contract** — process 200 / cancel-unknown 404 / delete 200 `{terminated, session_id}`, all confirmed by live curl. ✓

**Edge cases tested:** cancel success body contains `cancelled`+`cancelled_decision_id` and *not* legacy `status` (`TestCancelWireBody`); cancel-unknown → 404 (not 200-ok); delete idempotent 200 with body (not 204); process 200 with `session_id`; `go vet` clean, `gofmt` clean; all 4 unit tests PASS.

---
{"model": "deepseek-v4-flash", "problem_class": "go-release-tag-stale", "result": "passed", "tests": 4}
Generated from the verified corpus · MIT licensedBack to the catalog