Skip to content

fix: decouple context for activities - #2754

Merged
kmendell merged 1 commit into
breaking/v2.0.0from
fix/decouple-context
May 28, 2026
Merged

fix: decouple context for activities#2754
kmendell merged 1 commit into
breaking/v2.0.0from
fix/decouple-context

Conversation

@kmendell

@kmendell kmendell commented May 28, 2026

Copy link
Copy Markdown
Member

Checklist

  • This PR is not opened from my fork’s main branch

What This PR Implements

Fixes:

Changes Made

Testing Done

  • Development environment started: ./scripts/development/dev.sh start
  • Frontend verified at http://localhost:3000
  • Backend verified at http://localhost:3552
  • Manual testing completed (describe):
  • No linting errors (e.g., just lint all)
  • Backend tests pass: just test backend

AI Tool Used (if applicable)

AI Tool:
Assistance Level:
What AI helped with:
I reviewed and edited all AI-generated output:
I ran all required tests and manually verified changes:

Additional Context

Disclaimer Greptiles Reviews use AI, make sure to check over its work.

To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike

To have Greptile Re-Review the changes, mention greptileai.

Greptile Summary

This PR decouples activity tracking from HTTP request context by introducing an ActivityRuntimeContext utility and an ActivityAppContext wrapper that propagate the app lifecycle context through handler registration, replacing per-request context.WithoutCancel calls.

  • ActivityRuntimeContext selects the app lifecycle context (from bootstrap.go, marked via WithAppLifecycleContext) when available, so background activity goroutines — particularly Writer.drainMessagesInternal — now exit cleanly on app shutdown instead of leaking indefinitely under the old never-done context.WithoutCancel context.
  • A missing completeImageUpdateActivityInternal call on g.Wait() failure in ImageUpdateService.CheckMultipleImages is fixed, preventing activities from being permanently stuck in a running state after a batch error.

Confidence Score: 5/5

Safe to merge; the decoupling design is sound and closes a goroutine-lifetime bug in the activity writer.

The goroutine-lifetime fix in the writer drain loop is correct: using the app lifecycle context instead of a never-done context.WithoutCancel means the background goroutine exits on app shutdown. The ActivityRuntimeContext helper is well-tested and the fallback chain is sensible. The one concern — that returning the app context directly drops request-scoped tracing/logging values — is a deliberate design trade-off for the decoupling goal, but worth tracking if the team later adds distributed tracing via context propagation.

backend/pkg/utils/activity_context.go — the direct return of appCtx loses request-scoped observability values; worth revisiting if OpenTelemetry or similar context-propagated tracing is added.

Fix All in Codex Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
backend/pkg/utils/activity_context.go:22-25
**Request-scoped context values are silently dropped**

When `appCtx != nil`, the function returns it directly, discarding all values carried by `requestCtx` (distributed-tracing spans, structured-logger request IDs, any middleware-injected values). The previous `context.WithoutCancel(requestCtx)` approach preserved those values while still detaching from cancellation. Now, any downstream code that reads request-scoped values from the context (e.g., OpenTelemetry span propagation, `slog` attributes added by request middleware) will find them missing for every handler that has `h.appCtx` set.

Reviews (2): Last reviewed commit: "fix: decouple context for activities" | Re-trigger Greptile

@kmendell
kmendell marked this pull request as ready for review May 28, 2026 22:08
@kmendell
kmendell requested a review from a team as a code owner May 28, 2026 22:08

kmendell commented May 28, 2026

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@graphite-app

graphite-app Bot commented May 28, 2026

Copy link
Copy Markdown

Graphite Automations

"Warn authors when publishing large PRs" took an action on this PR • (05/28/26)

1 teammate was notified to this PR based on Kyle Mendell's automation.

Comment on lines 101 to 105
if writeErr != nil && (w.activityService == nil || w.activityID == "") {
return 0, writeErr
}

return len(p), nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Dead-code error-propagation branch

NewWriter already returns the raw writer (bypassing *Writer entirely) when activityService == nil || activityID == "", so any call to (*Writer).Write has w.activityService != nil and w.activityID != "" by construction. The condition w.activityService == nil || w.activityID == "" is therefore always false inside this method, making the return 0, writeErr branch unreachable. All HTTP write errors are unconditionally swallowed when a *Writer is in use — which appears to be the intended design — but the guard reads as meaningful protection that isn't actually exercised.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/pkg/libarcane/activity/writer.go
Line: 101-105

Comment:
**Dead-code error-propagation branch**

`NewWriter` already returns the raw writer (bypassing `*Writer` entirely) when `activityService == nil || activityID == ""`, so any call to `(*Writer).Write` has `w.activityService != nil` and `w.activityID != ""` by construction. The condition `w.activityService == nil || w.activityID == ""` is therefore always `false` inside this method, making the `return 0, writeErr` branch unreachable. All HTTP write errors are unconditionally swallowed when a `*Writer` is in use — which appears to be the intended design — but the guard reads as meaningful protection that isn't actually exercised.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex Fix in Claude Code

Comment thread backend/api/webhooks_trigger.go
@kmendell
kmendell force-pushed the fix/decouple-context branch from 6fa8c80 to 8d1124d Compare May 28, 2026 22:25
@kmendell
kmendell force-pushed the fix/decouple-context branch from 8d1124d to 8306633 Compare May 28, 2026 23:46
@kmendell
kmendell merged commit abb8f1b into breaking/v2.0.0 May 28, 2026
15 of 16 checks passed
@kmendell
kmendell deleted the fix/decouple-context branch May 28, 2026 23:46
kmendell added a commit that referenced this pull request May 28, 2026
kmendell added a commit that referenced this pull request May 29, 2026
kmendell added a commit that referenced this pull request May 31, 2026
kmendell added a commit that referenced this pull request Jun 2, 2026
kmendell added a commit that referenced this pull request Jun 4, 2026
kmendell added a commit that referenced this pull request Jun 4, 2026
kmendell added a commit that referenced this pull request Jun 4, 2026
kmendell added a commit that referenced this pull request Jun 4, 2026
kmendell added a commit that referenced this pull request Jun 4, 2026
kmendell added a commit that referenced this pull request Jun 7, 2026
kmendell added a commit that referenced this pull request Jun 7, 2026
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