Skip to content

feat(connect): Phase 16 — commit-id resolution improvements (first-16-bytes-of-SHA format) - #37

Merged
onokonem merged 33 commits into
mainfrom
feat/phase-16-commit-id-resolution
Jul 7, 2026
Merged

feat(connect): Phase 16 — commit-id resolution improvements (first-16-bytes-of-SHA format)#37
onokonem merged 33 commits into
mainfrom
feat/phase-16-commit-id-resolution

Conversation

@onokonem

@onokonem onokonem commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 16 of v1.3 (Diagnostic Logging). Three changes bundled into one phase:

  1. Format change (SC-1)commitUUID now derives the 32-char dashless id from the first 14 bytes of the git SHA + UUID version/variant bits (positions 6 and 8) instead of SHA-256 of the SHA. Deterministic function of the git SHA; round-trips for both full 40-char and short (7-byte / 14-byte) inputs. Signature changed to (string, error) with strict 40-hex-char input contract.

  2. Resolution path (SC-2)probeCommitID runs unconditionally on cache miss (single-source deployments keep the resolveForeignCommitID fast path). Already on the branch; this phase preserves it.

  3. Failure mode (SC-3) — the 400 response for an unresolvable commit id names the id in both the wire body and the structured log line. Message updated to \"unknown commit id: re-run buf mod update / buf dep update\". New internalError helper emits error_class=internal warn + 500 for the (string, error) contract violations.

Wire-format impact

Hard cutover. Existing buf.lock entries referencing the old SHA-256-derived UUIDs are rejected after upgrade (look like unknown commit ids). Clients must re-run buf mod update or buf dep update. See CHANGELOG.md.

Files

  • internal/connect/commits_helpers.go — rewritten commitUUID (D-01/D-02/D-03)
  • internal/connect/commits.go — 5 call sites wired to (string, error); internalError helper; 400 message (D-04/D-06/D-12)
  • internal/connect/{commits_helpers,uuid_format,api}_test.go — updated + new tests
  • CHANGELOG.md — format cutover entry (D-11)
  • .planning/phases/16-* — plan/summary/verification artifacts

Verification

  • go build ./...
  • go test ./... ✓ (all packages)
  • E2E with real buf CLI (v1.30.1 + v1.69.0): TestAllBufVersionsModUpdate, TestAllBufVersionsDepUpdate, TestSmokeBufModUpdate, TestNewProtocolBufDepUpdate all PASS against real GitHub. The new 32-char format mints correctly end-to-end.
  • 2 e2e tests (TestNewProtocolBufModUpdate, TestOldProtocolBufModUpdateTwice) failed only due to net/http: TLS handshake timeout reaching raw.githubusercontent.com — network issue, not code.
  • Verifier: 13/13 must-haves verified → .planning/phases/16-commit-id-resolution-improvements/16-VERIFICATION.md

Notes

🤖 Generated with Claude Code

onokonem and others added 30 commits July 6, 2026 15:23
buf v1.69.0 (and later) validates every commit id it receives with
uuidutil.FromDashless, which:
  1. Asserts length == 32
  2. Calls uuid.Parse, which validates version and variant bits

Before this fix, ServeHTTP/ServeGraph/ServeDownload returned the raw
40-char git SHA, which buf v1.69.0 rejected with:
    "Failure: expected dashless uuid to be of length 32 but was 40"

buf v1.30.1 happened to accept the raw SHA, so the existing smoke
test (TestSmokeBufModUpdate, which only runs v1.30.1) missed the bug
on the v1.69.0 path.

The fix is a deterministic SHA-256-derived UUIDv4-shaped id, computed
from the git SHA. Determinism is load-bearing: buf.lock entries stay
valid across proxy restarts, so a buf client that pinned a commit id
in a previous session will find it again on the next. SHA-256 of the
40-char input is overkill for non-security id-minting but lets us
reuse the stdlib without pulling google/uuid.

Files:
- internal/connect/commits_helpers.go: new commitUUID() helper
- internal/connect/commits.go: use commitUUID() in GetCommits,
  GetGraph, Download (response + files cache key), and
  registerResolved (commitMap keying + SHA alias for foreign-id path)
- internal/connect/commits_helpers_test.go: unit tests for the UUID
  format (length, hex, version, variant, determinism, distinctness)
- internal/connect/uuid_format_test.go: 400-error regression tests
  that lock in the wire format the buf client parses
- e2e/all_versions_test.go: matrix test that runs every cached buf
  version with buf mod update and buf dep update (skipping the latter
  for v1.30.x, which does not have the command). AvailableBufVersions
  discovers the versions dynamically so adding a new binary extends
  the matrix.
- e2e/testutil/{bufbin,config}.go: accept either EASYP_GH_TOKEN
  (current) or EASYP_GITHUB_TOKEN (legacy) so tests do not silently
  skip when only one of the two names is set.

Co-Authored-By: Claude <noreply@anthropic.com>
- Drop SHA-256 path; reuse first 14 SHA bytes (positions 0..5, 7, 9..15)
- Stamp UUID version-4 (0x40) at byte 6 and RFC 4122 variant (0x80) at byte 8
- Change signature to (string, error) with strict 40-lowercase-hex input contract
- Add preResolveForTest fixture (test-only) that pads short SHA to 40 chars
- Call sites in commits.go are updated in plan 16-02 (known build break)

- Drops "crypto/sha256" import, adds "errors"
- Inverse: take bytes 0..5, 7, 9..15 of id to recover first 14 SHA bytes
Documents the hard cutover in 16-01/16-02: the proxy now mints
the first 14 bytes of the git SHA combined with UUID version/variant
bits (D-01/D-02) instead of SHA-256. Existing buf.lock entries are
invalidated; operators are pointed at buf mod update / buf dep update
as the recovery action (D-10/D-11/D-12).

Co-Authored-By: Claude <noreply@anthropic.com>
…; add 4 new cases

- Update existing TestCommitUUIDFormat/Determinism/Distinct to call new 2-return signature
- Replace empty/leading-zero tests (subsumed by InvalidInput/KnownSHA)
- Add TestCommitUUID_KnownSHA: table-driven, 5 SHAs lock in exact UUID bytes
- Add TestCommitUUID_InvalidInput: empty, 39, 41, non-hex, mixed non-hex, 1 char
- Add TestCommitUUID_InverseRecovery: 4 SHAs, recover sha[0..13] from id bytes
- Add TestPreResolveForTest: empty/short/exact-40/over-40 + anti-collision assertion
- Update 3 commitUUID call sites in uuid_format_test.go to 2-return signature
  (D-13 explicitly required both test files be updated; plan files_modified
  listed only commits_helpers_test.go — deviation per Rule 1)
- Documents Tasks 1 (commitUUID rewrite, preResolveForTest) and 2 (test updates)
- Records deviation: uuid_format_test.go call sites updated (D-13 scope was incomplete in files_modified)
- Documents known build break in commits.go (5 call sites, fix is in plan 16-02 per plan split)
- Records standalone verification of helper logic (full test build cannot run until commits.go is fixed)
…ernalError helper

- Add internalError helper (D-04): slog warn with error_class=internal,
  commit_id, upstream_error via h.hlog(r); http.Error(w, 'internal error', 500)
- ServeHTTP / ServeGraph: rename local err to cidErr to avoid loop shadowing
- ServeDownload: reuse existing err var for the helper error check
- computeB4Digest: propagate helper error to caller; both callers
  (ServeHTTP, ServeGraph) now turn the resulting error into 500 via
  internalError instead of 502 via upstreamError
- registerResolved: background path logs with h.api.log + context.Background
- probeCommitID call site in ServeDownload is preserved (D-07/D-08)
… hex

Production:
- commits.go:582: change 'unknown commit id: must call
  CommitService/GetCommits first' to 'unknown commit id: re-run buf mod
  update / buf dep update' (D-12). Surrounding slog attrs and 400 status
  unchanged.
- commits.go:399: update the comment that quoted the old 400 message to
  reference the new text verbatim so it does not drift from the code.

Tests:
- api_test.go + uuid_format_test.go: add a Contains assertion for the
  D-12 substring in the existing 400-body checks so a future regression
  that drops 're-run buf mod update / buf dep update' fails loudly.
- (deviation, Rule 1) api_test.go: pad the 4 short hex commit literals
  in test mocks from 6/8 chars to 40 chars (e.g. 'deadbeef' ->
  'deadbeef00000000000000000000000000000000'). The new commitUUID helper
  rejects non-40-char input as a contract violation (D-03), so the old
  short fixtures would have made 6 tests 500 with 'internal error'
  instead of running the assertion. Padded literals preserve the test
  intent (same prefix, same mockSource owner/module) while satisfying
  the strict input contract.
- internalError helper added in commits.go; 5 commitUUID call sites
  wired to the new (string, error) signature with structured 500 handling
- 400 message at commits.go:582 updated to D-12 text: 'unknown commit id:
  re-run buf mod update / buf dep update'
- probeCommitID contract preserved (D-07/D-08)
- Test files: D-12 substring assertion added; short mock commits padded
  to 40 hex chars to satisfy the new commitUUID strict-input contract
- All internal/connect tests pass; go build ./... exits 0

Deviation (Rule 1): padded 4 mockProvider mock commits from 6/8 chars to
40 hex chars so they satisfy the new commitUUID D-03 contract. The plan's
files_modified list did not include fixture updates, but the strict
contract would have caused 6 tests to 500 with 'internal error' instead
of exercising their real assertions.
…helpers

Findings #1, #2, #5 of the PR #37 review:

- Remove internalError helper from commits.go; it bypassed the v1.3
  ERR-05 logging contract (dropped server/protocol/request_id/status,
  downgraded to LevelWarn for 5xx, hardcoded 'commitUUID failure' even
  when the cause was GetFiles upstream).
- Add errCommitUUIDContract sentinel; dispatch on errors.Is so digest
  failures route correctly: 500 (contract violation, logHandlerError)
  vs 502 (upstream, h.upstreamError).
- Thread already-minted cid into computeB4Digest so the redundant
  commitUUID(commit) re-derivation is replaced with a contract-violation
  guard wrapped via fmt.Errorf('%w: %v', errCommitUUIDContract, ...).
- All 5 former internalError call sites (160, 174-178, 351, 354-358,
  668) now call h.logHandlerError directly; the 2 digest-error branches
  at 174-178 and 354-358 dispatch on errors.Is.
Finding #3 of the PR #37 review:

- commitUUID now accepts exactly 40 or 64 lowercase hex characters.
  Bitbucket Server on SHA-256-enabled repos returns out.Commit as 64
  chars (bitbucket/getrepo.go:40); the old strict-40 check 500-ed
  every request to such a module.
- The byte-table reads sha[0:6], sha[6], sha[7:14] which are all
  within the 20-byte SHA-1 and 32-byte SHA-256 decoded buffers, so
  the function is byte-safe for both lengths. The UUID only reflects
  the first 14 decoded bytes regardless of input length.
- Updated the doc comment to call out the 40/64 contract and the
  Bitbucket driver.
- New TestCommitUUID_SHA256_KnownSHA covers 4 cases: all-zero,
  all-ones, 14-byte-prefix-matching-existing-40-char-fixture (the
  load-bearing regression-guard for 'function actually consumes
  SHA-256 bytes, not just the first 40 chars of a string'), and
  deadbeef. The 14-byte-prefix case is the structural invariant:
  same first 14 bytes in -> same UUID out.
- TestCommitUUID_InvalidInput extended with 63-char and 65-char
  inputs (off-by-one boundaries around the new 64-char length).
Finding #4 of the PR #37 review:

- The 400 wire body at commits.go:582 changed from
  're-run buf mod update / buf dep update' to
  're-resolve via buf mod update / buf dep update'. The original text
  overfit to the stale-lockfile miss class — for a foreign id
  (resolveForeignCommitID miss class) the prescribed command is a
  no-op and operators would chase a re-run that cannot help.
- 'unknown commit id:' prefix preserved, so the substring assertions
  at api_test.go:590 and uuid_format_test.go:209 continue to pass
  unchanged.
- Inline comment in ServeGraph (commits.go:399) updated to quote the
  new wire body so future readers see the live text.
- Both test assertions updated: api_test.go:597 and
  uuid_format_test.go:215 now look for the new substring. Error
  message text in the t.Errorf lines updated to match.
Finding #7 of the PR #37 review:

- preResolveForTest has 5 callers, all in _test.go files. The only
  thing keeping it out of production was its name. Moved the
  function (with its 5-line doc comment) from commits_helpers.go to
  commits_helpers_test.go where Go's build constraints exclude it.
- The strings import in commits_helpers.go is retained: it remains
  used by strings.SplitN in parseModuleRefByID (line 288).
- No caller changes — package-internal calls resolve transparently
  across the .go / _test.go split.
- All 5 TestPreResolveForTest subtests continue to pass.
- STATE.md: phase 17 marked complete, plan 17-01 finished
  (4/4 tasks, 5/5 success criteria). 9/9 milestone plans now
  complete (100%). Velocity updated.
- ROADMAP.md: Phase 17 checkbox flipped to [x]; Progress table
  row updated to 1/1 Complete 2026-07-07; plan bullet at
  line 149 marked [x].
Pre-existing MD060 warnings on the By-Phase table from the column
widths going out of sync when the new row was added. Re-pad the
column separators so all rows have matching widths. The Deferred
Items table MD060 warning predates this commit (it was already
present on the main branch) and is left as-is to keep this change
narrowly scoped to the post-Phase-17 update.
…ssed

All 5 ROADMAP success criteria (SC-1 through SC-5) verified via
structural greps and the existing + new unit tests. Single deferred
item (T-17-DEFER, 32-char hex probe/cache path) carried forward per
plan question #3 — not blocking.
@onokonem

onokonem commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Phase 17 — PR #37 review findings addressed

Pushed 4 fix commits + 3 doc commits (7 total) to this branch on top of the existing Phase 16 work. All four in-scope review findings from review.md are resolved; the 5 ROADMAP success criteria (SC-1 through SC-5) are verified. Ready for re-review.

Findings addressed

# Finding Resolution
#1 computeB4Digest failures mislabeled as commitUUID failures, returned as 500 not 502 Added errCommitUUIDContract sentinel; errors.Is dispatch in both digest-error sites routes 500 (contract violation) vs 502 (upstream). commitUUID re-check inside computeB4Digest wraps via fmt.Errorf("%w: %v", errCommitUUIDContract, uidErr).
#2 internalError helper reimplements logHandlerError, bypasses ERR-05 Helper deleted. All 5 former call sites (3 contract-violation + 2 digest-error) flow through logHandlerError/upstreamError with full server/protocol/request_id/error/status/error_class attributes.
#3 Strict 40-char commitUUID contract breaks Bitbucket SHA-256 repos Length check relaxed to len != 40 && len != 64. Byte table reads sha[0:6], sha[6], sha[7:14] which are in-bounds for both 20-byte SHA-1 and 32-byte SHA-256 decoded buffers.
#4 400 message overfits to one cause, misleads for foreign ids Wire body changed from re-run to re-resolve via. The "unknown commit id" prefix is preserved (existing bytes.Contains / strings.Contains assertions continue to pass).
#5 Redundant commitUUID re-derivation inside computeB4Digest (cleanup) Threaded the caller-minted cid into computeB4Digest as a 4th parameter; eliminated the redundant re-derivation. Contract-violation guard now uses the explicit wrap.
#7 preResolveForTest ships in a production source file Moved to commits_helpers_test.go with its doc comment. strings import in commits_helpers.go retained (still used by strings.SplitN).

Commits added on top of the existing PR #37 baseline

704d35f docs(17): verification report — 13/13 must-haves verified, status: passed
5e15b9d docs(17): align table column padding in STATE.md (MD060 cosmetic)
a3d409b docs(17): update STATE.md and ROADMAP.md after phase completion
c278871 docs(17-01): complete plan - 4 tasks, 5 success criteria satisfied
01ed747 fix(17-01): move preResolveForTest from production source to test file
3ff1786 fix(17-01): soften 400 not-found message to cover foreign-id misses
1c1a404 fix(17-01): accept 64-char SHA-256 in commitUUID for Bitbucket compat
d355bac fix(17-01): route computeB4Digest errors through upstream/logHandler helpers

Files modified

  • internal/connect/commits.gointernalError removed; errCommitUUIDContract added; 5 call sites refactored; computeB4Digest 4-arg signature; 400 wire body softened
  • internal/connect/commits_helpers.go — length check expanded to 40/64 hex; preResolveForTest deleted
  • internal/connect/commits_helpers_test.goTestCommitUUID_SHA256_KnownSHA (4 cases) added; TestCommitUUID_InvalidInput extended with 63/65-char cases; preResolveForTest moved in
  • internal/connect/api_test.go — D-12 substring assertion updated to re-resolve via
  • internal/connect/uuid_format_test.go — D-12 substring assertion updated to re-resolve via

Verification

  • go build ./...
  • go vet ./...
  • go test ./internal/connect/ -count=1 ✓ (all pass)
  • go test ./... -count=1 ✓ (all packages pass — connect, artifactory, filter, multisource, reqid)
  • TestCommitUUID_SHA256_KnownSHA — 4/4 subtests pass
  • TestCommitUUID_InvalidInput — 8/8 subtests pass (incl. 63/65-char)
  • TestPreResolveForTest — 5/5 subtests pass in new location
  • TestBadRequest_OnUnknownCommitID and TestServeDownload_UnknownCommitID_ReturnsBadRequest — pass with the new re-resolve via text
  • 13/13 must-haves verified.planning/phases/17-fix-pr-37-review-findings/17-VERIFICATION.md
  • Score: 13/13
  • Gaps: 0
  • Deferred: T-17-DEFER (32-char hex path on probe/cache contract; no known provider returns 32-char hex shas; low real-world impact). Tracked in 17-VERIFICATION.md for a future phase.

Diff impact on PR #37

Net change vs the original PR #37 baseline (661f1d5704d35f):

  • +50 / -24 in commits.go (helper deletion, sentinel, 5 call-site refactor, signature change, wire body)
  • +10 / -5 in commits_helpers.go (length check, error messages, doc comment, function deletion)
  • +70 / -5 in commits_helpers_test.go (new test + extended test + function move)
  • +3 / -3 in api_test.go and uuid_format_test.go (assertion updates)
  • +103 in 17-VERIFICATION.md (new verification report)
  • +20 / -20 in .planning/{STATE,ROADMAP}.md (post-phase tracking)
  • +2 in .planning/STATE.md (table alignment)

Notes for the reviewer

  • The internalError helper described in the original PR feat(connect): Phase 16 — commit-id resolution improvements (first-16-bytes-of-SHA format) #37 body is gone. The 5 call sites it served are now split: 3 contract-violation sites call h.logHandlerError directly with slog.String("commit_id", meta.Commit, ...); 2 digest-error sites dispatch on errors.Is(err, errCommitUUIDContract) and route to either logHandlerError (500) or upstreamError (502) depending on the cause.
  • The 400 wire body text is a substring-compatible change (re-runre-resolve via). Wire protocol is unchanged.
  • The byte-table in commitUUID is unchanged; the 64-char SHA-256 support is a pure length-check expansion.
  • The registerResolved warn-and-return at commits.go:901-911 is intentionally untouched (logs without an HTTP request; the h.api.log + context.Background() shape is correct for that path).
  • Finding A tutorial is missing #6 (32-char hex on probe/cache contract) is documented as T-17-DEFER in 17-VERIFICATION.md. No code change in this phase per the plan's <questions> decision.

Full plan, research, summary, and verification artifacts in .planning/phases/17-fix-pr-37-review-findings/. 🤖 Generated with Claude Code

onokonem added 2 commits July 7, 2026 10:30
Phase 17 status: shipped. The 7 new commits (4 fix + 3 doc) are
pushed to feat/phase-16-commit-id-resolution alongside the existing
PR #37 baseline. Posted a summary comment on PR #37 listing the
findings addressed, the commits, the verification results, and the
files modified. Awaiting re-review on PR #37.
Two new MD034 no-bare-urls warnings fired on the URLs I added in
the previous commit (lines 31 and 94). Wrapping both in <> so the
markdown linter is happy. Renders identically; the link is still
clickable.
@onokonem
onokonem merged commit 8069e17 into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant