Purpose: These are the two code-level bugs/missing features in Phase 1. The net/url bug causes compile errors for POST-only services. The cross-file unwrap PR enables real-world usage patterns where proto files are split across multiple files in the same package.
Output: Both fixes landed on main, all tests passing, golden files updated.
<execution_context> @/Users/sebastienmelki/.claude/get-shit-done/workflows/execute-plan.md @/Users/sebastienmelki/.claude/get-shit-done/templates/summary.md </execution_context>
Step 1: Add a fileNeedsURLImport method to Generator in internal/clientgen/generator.go. It mirrors the existing fileNeedsRequestBody pattern (lines 70-84). The function must return true if ANY method in the file uses:
- Path parameters (
httpConfig.PathParamshas entries) -- these generateurl.PathEscape()calls - Query parameters on GET/DELETE methods -- these generate
url.Values{}usage
Implementation pattern:
func (g *Generator) fileNeedsURLImport(file *protogen.File) bool {
for _, service := range file.Services {
for _, method := range service.Methods {
httpConfig := getMethodHTTPConfig(method)
// Path params use url.PathEscape
if httpConfig != nil && len(httpConfig.PathParams) > 0 {
return true
}
// Determine HTTP method
httpMethod := http.MethodPost
if httpConfig != nil && httpConfig.Method != "" {
httpMethod = httpConfig.Method
}
// GET/DELETE with query params use url.Values
if httpMethod == http.MethodGet || httpMethod == http.MethodDelete {
queryParams := getQueryParams(method.Input)
if len(queryParams) > 0 {
return true
}
}
}
}
return false
}Step 2: Modify generateClientFile (line 47) to call fileNeedsURLImport and pass the result to writeImports. Find the existing call g.writeImports(gf, needsBytes) and replace with:
needsURL := g.fileNeedsURLImport(file)
g.writeImports(gf, needsBytes, needsURL)Step 3: Modify writeImports signature to accept needsURL bool (line 94). Make the "net/url" line conditional:
func (g *Generator) writeImports(gf *protogen.GeneratedFile, needsBytes, needsURL bool) {
// ... existing code ...
gf.P(`"net/http"`)
if needsURL {
gf.P(`"net/url"`)
}
// ... rest of imports ...Step 4: Update golden files by running:
make build && UPDATE_GOLDEN=1 go test -run TestClientGenGoldenFiles ./internal/clientgen/Step 5: Verify the golden file diff:
backward_compat_client.pb.goshould LOSE the"net/url"import line (POST-only, no path/query params)query_params_client.pb.goshould KEEP"net/url"(has query params)http_verbs_comprehensive_client.pb.goshould KEEP"net/url"(has path params and query params)
Step 6 (runs AFTER verification passes): Close GitHub issue #105:
gh issue close 105 --comment "Fixed: net/url import is now conditional based on whether the service uses path parameters or query parameters. POST-only services no longer generate unused net/url imports. The fix adds a fileNeedsURLImport() function that checks for path params (url.PathEscape usage) and query params on GET/DELETE methods (url.Values usage)."Verify backward_compat golden file does NOT contain net/url
! grep -q '"net/url"' internal/clientgen/testdata/golden/backward_compat_client.pb.go
Verify other golden files DO contain net/url#
grep -q '"net/url"' internal/clientgen/testdata/golden/query_params_client.pb.go grep -q '"net/url"' internal/clientgen/testdata/golden/http_verbs_comprehensive_client.pb.go
Run full test suite#
./scripts/run_tests.sh --fast
</verify>
<done>
- `fileNeedsURLImport` function exists in `generator.go` and checks both path params and query params
- `writeImports` conditionally emits `"net/url"` based on the `needsURL` parameter
- `backward_compat_client.pb.go` golden file no longer contains `"net/url"`
- `query_params_client.pb.go` and `http_verbs_comprehensive_client.pb.go` golden files still contain `"net/url"`
- All tests pass (golden file tests, unit tests, full test suite)
- GitHub issue #105 is closed with explanatory comment (closed AFTER verification confirms the fix works)
</done>
</task>
<task type="auto">
<name>Task 2: Land PR #98 (cross-file unwrap resolution)</name>
<files>internal/httpgen/generator.go, internal/httpgen/unwrap.go, internal/httpgen/unwrap_test.go, internal/httpgen/testdata/proto/same_pkg_service.proto, internal/httpgen/testdata/proto/same_pkg_wrapper.proto</files>
<action>
Land PR #98 which adds cross-file unwrap resolution for proto files in the same Go package.
**Important context:** PR #98 is currently OPEN with merge conflicts (`"mergeable":"CONFLICTING"`). The branch is `unwrap_bug`. The PR also has a lint failure on `go fmt` check, though tests pass on both platforms.
**Step 1:** Fetch and check out the PR branch locally:
```bash
gh pr checkout 98
Step 2: Rebase onto current main to resolve conflicts:
git rebase mainIf conflicts arise, they are likely in the files recently changed on main (5 docs commits). Resolve them -- the PR's code changes should take priority for any code files; docs conflicts should incorporate both sides.
Step 3: Fix the lint issue by running:
go fmt ./...Commit any formatting fixes.
Step 4: Run the full test suite to verify everything works BEFORE merging:
make build && ./scripts/run_tests.sh --fastCRITICAL: Only proceed to Step 5 if ALL tests pass. If any test fails, debug and fix before merging.
Step 5: After tests pass, merge the PR into main:
gh pr merge 98 --squash --subject "fix(httpgen): resolve unwrap fields across files in same Go package"If gh pr merge fails due to CI not passing on the rebased branch, push the rebased branch first (git push --force-with-lease) and wait for CI, or merge locally:
git checkout main
git merge --squash unwrap_bug
git commit -m "fix(httpgen): resolve unwrap fields across files in same Go package (#98)"If merged locally, close the PR on GitHub so it doesn't remain OPEN:
gh pr close 98 --comment "Merged locally after resolving conflicts and passing tests"Run full test suite#
make build && ./scripts/run_tests.sh --fast
Verify cross-file unwrap test files exist#
ls internal/httpgen/testdata/proto/same_pkg_service.proto ls internal/httpgen/testdata/proto/same_pkg_wrapper.proto
Verify PR is merged or closed (CLOSED if merged locally)#
gh pr view 98 --json state -q '.state' | grep -E 'MERGED|CLOSED'
</verify>
<done>
- PR #98 is merged into main (state: MERGED, or CLOSED if merged locally)
- Cross-file unwrap test fixtures exist (`same_pkg_service.proto`, `same_pkg_wrapper.proto`)
- `GlobalUnwrapInfo` collects unwrap annotations from all proto files before code generation
- All tests pass (httpgen golden tests, unwrap tests, full suite)
</done>
</task>
</tasks>
<verification>
After both tasks complete:
1. `go build ./cmd/protoc-gen-go-client/` succeeds
2. `go build ./cmd/protoc-gen-go-http/` succeeds
3. `./scripts/run_tests.sh --fast` passes all tests
4. `backward_compat_client.pb.go` golden file does NOT contain `"net/url"`
5. PR #98 shows state MERGED or CLOSED on GitHub (CLOSED if merged locally)
6. Issue #105 shows state CLOSED on GitHub (closed by Task 1 after verification confirms the fix)
7. `git log --oneline -5` shows both fix commits on main
</verification>
<success_criteria>
- The net/url import is conditional: only emitted when path params or query params are used
- Cross-file unwrap resolution works for proto files in the same Go package
- All golden file tests pass without manual intervention
- Full test suite passes with 85%+ coverage
- PR #98 merged, issue #105 closed
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundation-quick-wins/01-01-SUMMARY.md`
</output>
