Skip to content

feat(exthttp): centralize extension index ETag via Revision/BumpRevision#158

Merged
joshiste merged 2 commits into
mainfrom
feat/index-revision-etag
Jul 10, 2026
Merged

feat(exthttp): centralize extension index ETag via Revision/BumpRevision#158
joshiste merged 2 commits into
mainfrom
feat/index-revision-etag

Conversation

@joshiste

@joshiste joshiste commented Jul 10, 2026

Copy link
Copy Markdown
Member

What

Adds exthttp.Revision(), exthttp.BumpRevision(), and exthttp.RegisterRevisionedHandler so the extension index ETag becomes an SDK guarantee instead of per-extension main.go boilerplate.

  • Revision() — current revision string. Seeded at package init with a startup nonce (time.Now().UnixNano(), base36) so it differs across restarts, plus an atomic counter.
  • BumpRevision() — advances the revision; kit SDKs call this from their Register*/Clear* functions.
  • RegisterRevisionedHandler[T](path, getter) — registers a handler that serves the getter's result tagged with Revision() as its ETag (conditional GET → 304 on matching If-None-Match), replacing the hand-rolled startedAt + IfNoneMatchHandler wiring.

Why

Previously each extension froze a startedAt timestamp at process start and wrapped / with IfNoneMatchHandler. That made index caching copy-pasted boilerplate (an extension that forgot the wrapper silently lost caching) and boot-scoped rather than registration-scoped. The revision reflects the registered description set, so the agent's index-response cache invalidates on registration changes without needing a restart.

Compatibility

The ETag value stays an unquoted string, byte-identical to the historical startedAt, so it round-trips through the agent's If-None-Match/ETag handling unchanged — no agent-side coordination required. IfNoneMatchHandler is unchanged and stays public for back-compat.

Follow-ups

The *-kit SDKs (action/discovery/advice/preflight) will bump this dependency and add BumpRevision() calls once this is tagged; extensions then swap their boilerplate to RegisterRevisionedHandler.

The extension index ETag was previously hand-rolled in every extension's
main.go as a process-start timestamp, so index caching was per-extension
boilerplate and only invalidated on restart. Provide Revision()/BumpRevision()
and RegisterIndexHandler so the index ETag is an SDK guarantee that reflects
registration state: seeded with a startup nonce and bumped whenever a kit
registers or clears a describable element. The value stays an unquoted string
so it round-trips through the agent's If-None-Match handling unchanged.
@claude

claude Bot commented Jul 10, 2026

Copy link
Copy Markdown

Claude finished @joshiste's task in 1m 56s —— View job


Review complete

Todo list

  • Check previously unresolved review threads for fixes (none existed)
  • Review new changes (correctness, security, perf, concurrency, typos)
  • Post final summary

Summary

Reviewed exthttp/revision.go, exthttp/revision_test.go, and the CHANGELOG.md entry (diff origin/main...HEAD).

Correctness / concurrency

  • revisionCounter is an atomic.Uint64, so concurrent BumpRevision()/Revision() calls from multiple goroutines (e.g. concurrent kit registrations) are race-free.
  • revisionSeed is computed once at package init time and never mutated afterward, so no synchronization is needed for it.
  • Revision() reading + BumpRevision() writing the counter concurrently is safe via the atomic load/add pair — no lost updates, no data race.
  • RegisterRevisionedHandler wires Revision directly as the etagFn passed to the existing IfNoneMatchHandler, which is unchanged — the ETag/304 semantics are preserved exactly as before (verified against exthttp/exthttp.go:148-159).

Compatibility

  • The ETag stays an unquoted plain string (seed-counter), consistent with the PR's stated goal of matching the historical startedAt wire format byte-for-byte.

Tests

  • The three new tests cover: revision changes on BumpRevision, revision stability without a bump, and the full 200→ETag→304 round trip via IfNoneMatchHandler. These look correct and exercise the intended behavior.
  • Note: I was not able to execute go build/go test in this sandboxed environment (the tool run required approval that wasn't available), so this review is based on static code reading rather than an actual test run. The code compiles cleanly by inspection and the logic checks out.

No issues found — no bugs, no security concerns, no race conditions, and no typos affecting correctness. The change is small, self-contained, and consistent with the stated goal of centralizing the index ETag/revision logic in the SDK.

…andler

The function registers a handler tagged with the current revision as its ETag;
it has no notion of an index. Name it for that responsibility instead of one
use site.
@joshiste joshiste merged commit 3acc1e3 into main Jul 10, 2026
5 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant