Repository navigation
feat: add channels - #14
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds end-to-end channel support: domain models, routing and target resolution, a delivery broker, registry service, daemon/runtime orchestration, extension host API and manager delivery integration, HTTP/UDS API + CLI surfaces, OpenAPI spec updates, and extensive unit/integration tests and adapter conformance harnesses. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 19
🧹 Nitpick comments (18)
internal/api/udsapi/udsapi_integration_test.go (1)
316-338: Add context-wrapped errors in lifecycle helper methods.Line 316 through Line 338 return raw errors from
UpdateInstanceState, which makes failures harder to diagnose in integration runs.♻️ Suggested change
func (s *integrationChannelService) StartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - return s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ ID: id, Enabled: true, Status: channelspkg.ChannelStatusStarting, }) + if err != nil { + return nil, fmt.Errorf("start channel instance %q: %w", id, err) + } + return instance, nil } func (s *integrationChannelService) StopInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - return s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ ID: id, Enabled: false, Status: channelspkg.ChannelStatusDisabled, }) + if err != nil { + return nil, fmt.Errorf("stop channel instance %q: %w", id, err) + } + return instance, nil } func (s *integrationChannelService) RestartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - return s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ ID: id, Enabled: true, Status: channelspkg.ChannelStatusStarting, }) + if err != nil { + return nil, fmt.Errorf("restart channel instance %q: %w", id, err) + } + return instance, nil }As per coding guidelines, "Use explicit error returns with wrapped context:
fmt.Errorf("context: %w", err)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/udsapi_integration_test.go` around lines 316 - 338, The StartInstance, StopInstance, and RestartInstance helpers currently return raw errors from UpdateInstanceState; update each method (StartInstance, StopInstance, RestartInstance) to check the error returned by UpdateInstanceState and return a wrapped error using fmt.Errorf with contextual text (e.g., "start instance %s: %w", "stop instance %s: %w", "restart instance %s: %w") so failures include operation and instance id context while preserving the original error.internal/api/httpapi/channels_test.go (1)
13-131: Consolidate these handler tests into table-drivent.Run("Should...")cases.Line 13 through Line 131 repeats the same setup in three standalone tests; moving to table-driven subtests will reduce duplication and align with the repository test conventions.
As per coding guidelines, "Use table-driven tests with subtests (
t.Run) as default" and "MUST uset.Run("Should...")pattern for ALL test cases".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/httpapi/channels_test.go` around lines 13 - 131, Combine the three standalone tests TestCreateChannelHandlerCreatesChannelInstance, TestListChannelRoutesHandlerReturnsRequestedRouteSet, and TestChannelTestDeliveryHandlerResolvesTypedTarget into a single table-driven test (e.g., TestChannelsHandlers) that uses t.Run("Should ...") subtests; create a slice of cases each with a name and per-case stubChannelService behavior (CreateInstanceFn, ListRoutesFn, ResolveDeliveryTargetFn) and expected request/response checks, reuse common setup like homePaths and engine created with newTestHandlersWithChannels, and for each case call performRequest and decodeJSONResponse then assert expectations — this consolidates duplicated setup and follows the repository t.Run pattern.internal/api/udsapi/channels_integration_test.go (1)
77-88: Consider logging or handling the Close error.Per coding guidelines, errors should not be ignored with
_. While ignoringbody.Close()errors is common in test cleanup (where recovery isn't meaningful), you could log it for visibility:♻️ Suggested alternative
func mustReadAll(t *testing.T, body io.ReadCloser) string { t.Helper() - defer func() { - _ = body.Close() - }() + defer body.Close() data, err := io.ReadAll(body)Or if you want to surface close errors in tests:
+ t.Cleanup(func() { + if err := body.Close(); err != nil { + t.Logf("body.Close() error = %v", err) + } + })As per coding guidelines: "Never ignore errors with _ — every error must be handled or have a written justification."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/channels_integration_test.go` around lines 77 - 88, The helper mustReadAll currently ignores the error from body.Close(); change the defer to capture and handle the Close error (do not assign to `_`): in the defer in mustReadAll, call err := body.Close() and, if non-nil, report it via the testing.T (e.g., t.Logf or t.Errorf) so the test surfaces cleanup failures; ensure the defer still runs t.Helper() context and returns the read string as before.internal/extension/channel_delivery_integration_test.go (1)
305-305: Minor: Redundant string cast.The cast
string("channel.adapter")is unnecessary since the literal is already a string.♻️ Suggested fix
- capabilities: []string{string("channel.adapter")}, + capabilities: []string{"channel.adapter"},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/channel_delivery_integration_test.go` at line 305, The capabilities slice contains a redundant cast string("channel.adapter"); remove the unnecessary conversion and use the string literal "channel.adapter" directly in the capabilities field (the slice assigned to capabilities) to simplify the code.internal/channels/target_integration_test.go (1)
107-112: Redundant assertion can be removed.The assertion at lines 110-112 is logically redundant. You already assert at lines 107-109 that
workspaceTarget.GroupIDis empty, and at lines 95-97 thatglobalTarget.GroupIDis"global-group". If both assertions pass, they can never be equal, making the final comparison unnecessary.♻️ Suggested simplification
if workspaceTarget.GroupID != "" { t.Fatalf("workspaceTarget.GroupID = %q, want empty", workspaceTarget.GroupID) } - if workspaceTarget.GroupID == globalTarget.GroupID { - t.Fatalf("workspaceTarget.GroupID = %q, want different value than global target", workspaceTarget.GroupID) - } if workspaceTarget.Mode != channelspkg.DeliveryModeReply {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/channels/target_integration_test.go` around lines 107 - 112, The second check comparing workspaceTarget.GroupID to globalTarget.GroupID is redundant because workspaceTarget.GroupID is already asserted to be empty and globalTarget.GroupID is asserted earlier to be "global-group"; remove the entire if block that checks "if workspaceTarget.GroupID == globalTarget.GroupID { t.Fatalf(...)}" so only the emptiness assertion for workspaceTarget.GroupID remains (referencing workspaceTarget.GroupID and globalTarget.GroupID to locate the code).internal/daemon/daemon.go (1)
108-114: Type assertion silently returns nil for non-implementing services.The pattern is safe since callers handle
nil, but consider adding a comment explaining this intentional behavior for maintainability:func channelObserveSource(service core.ChannelService) observe.ChannelSource { if service == nil { return nil } + // Returns nil if service doesn't implement ChannelSource - callers must handle nil. source, _ := service.(observe.ChannelSource) return source }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/daemon.go` around lines 108 - 114, The type assertion in channelObserveSource intentionally ignores the boolean ok and returns nil when the provided core.ChannelService does not implement observe.ChannelSource; add a clear comment above the channelObserveSource function explaining that this silent-nil behavior is deliberate (callers expect and handle nil) and why we avoid returning an error or panicking, referencing the types core.ChannelService and observe.ChannelSource so future maintainers understand the rationale.internal/api/core/channels.go (3)
156-156: Missing whitespace normalization on path parameter.Same inconsistency—
c.Param("id")should be trimmed.♻️ Suggested fix
- targetReq, err := req.ToResolveDeliveryTargetRequest(c.Param("id")) + targetReq, err := req.ToResolveDeliveryTargetRequest(strings.TrimSpace(c.Param("id")))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels.go` at line 156, The call passing the raw path param into ToResolveDeliveryTargetRequest should normalize whitespace first; instead of passing c.Param("id") directly to req.ToResolveDeliveryTargetRequest, trim the parameter (e.g., via strings.TrimSpace) and pass the trimmed value so targetReq, err := req.ToResolveDeliveryTargetRequest(...) receives a normalized id; update the code around the targetReq creation to use the trimmed id and import strings if needed.
96-96: Missing whitespace normalization on path parameter.Same inconsistency as line 74—
c.Param("id")should be trimmed for consistency with other handlers.♻️ Suggested fix
- updateReq, err := req.ToUpdateInstanceRequest(c.Param("id")) + updateReq, err := req.ToUpdateInstanceRequest(strings.TrimSpace(c.Param("id")))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels.go` at line 96, Trim whitespace from the path parameter before passing it to the request builder: replace the direct c.Param("id") usage with strings.TrimSpace(c.Param("id")) when calling req.ToUpdateInstanceRequest so updateReq is created from a normalized id (ensure strings is imported if not present); this mirrors the same normalization applied elsewhere (e.g., the earlier handler using c.Param("id") at line 74) and prevents inconsistent ID handling.
74-74: Inconsistent whitespace handling for path parameter.
c.Param("id")is not trimmed here, but lines 133, 196, 208, and 220 usestrings.TrimSpace(c.Param("id")). This inconsistency could cause lookup mismatches if IDs contain surrounding whitespace.♻️ Suggested fix
- instance, err := channels.GetInstance(c.Request.Context(), strings.TrimSpace(c.Param("id"))) + instance, err := channels.GetInstance(c.Request.Context(), strings.TrimSpace(c.Param("id")))Apply the same pattern to lines 96 and 156 as well.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels.go` at line 74, The code calls channels.GetInstance with c.Param("id") without trimming; make it consistent by wrapping the path param with strings.TrimSpace(c.Param("id")) wherever you pass the ID to lookups (e.g., replace the call to channels.GetInstance(c.Request.Context(), c.Param("id")) with channels.GetInstance(c.Request.Context(), strings.TrimSpace(c.Param("id"))) and apply the same change for the other occurrences flagged (the calls around the code handling the same ID at the other two locations), ensuring all lookups use strings.TrimSpace on c.Param("id").internal/api/core/channels_test.go (3)
108-110: Remove dead code assertion.The
handlersvariable is always non-nil sincenewChannelHandlerFixturereturns a valid*core.BaseHandlers. This check provides no value.🧹 Suggested removal
- if handlers == nil { - t.Fatal("expected handlers") - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels_test.go` around lines 108 - 110, Remove the redundant nil-check assertion for handlers in channels_test.go: delete the if block that tests "if handlers == nil { t.Fatal(\"expected handlers\") }" because newChannelHandlerFixture always returns a valid *core.BaseHandlers; update or re-run the test to ensure no other code depends on that assertion and rely on direct use of handlers (symbol: handlers, function: newChannelHandlerFixture, type: *core.BaseHandlers).
128-145: Wrap table cases int.Runfor better isolation and debugging.The table-driven test iterates directly without using
t.Run. This makes it harder to identify which case failed and prevents running subtests independently.♻️ Suggested improvement
for _, tc := range []struct { path string status channelspkg.ChannelStatus }{ {path: "/channels/chan-core/enable", status: channelspkg.ChannelStatusStarting}, {path: "/channels/chan-core/disable", status: channelspkg.ChannelStatusDisabled}, {path: "/channels/chan-core/restart", status: channelspkg.ChannelStatusStarting}, } { + t.Run("Should transition status via "+tc.path, func(t *testing.T) { resp := performRequest(t, engine, http.MethodPost, tc.path, nil) if resp.Code != http.StatusOK { t.Fatalf("%s status = %d body=%s", tc.path, resp.Code, resp.Body.String()) } var payload contract.ChannelResponse testutil.DecodeJSONResponse(t, resp, &payload) if payload.Channel.Status != tc.status { t.Fatalf("%s status payload = %q, want %q", tc.path, payload.Channel.Status, tc.status) } + }) }As per coding guidelines: "Use table-driven tests with subtests (t.Run) as default" and "MUST use t.Run("Should...") pattern for ALL test cases".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels_test.go` around lines 128 - 145, The table-driven loop in channels_test.go should wrap each case in t.Run to isolate failures and allow subtest execution: inside the for loop, call t.Run with a descriptive name like "Should <action> <path>" (e.g., using tc.path or the expected status) and move the test body into that closure; also shadow the loop variable (add tc := tc at the start of the closure) to avoid capture issues, then call performRequest(t, engine, http.MethodPost, tc.path, nil), decode into contract.ChannelResponse with testutil.DecodeJSONResponse, and assert payload.Channel.Status == tc.status as before. Ensure each subtest uses the same assertions but is contained within the t.Run closure so failures report the specific case.
147-149: Remove dead code assertion.Same issue as above —
handlersis always non-nil from the fixture.🧹 Suggested removal
- if handlers == nil { - t.Fatal("expected handlers") - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/channels_test.go` around lines 147 - 149, The assertion that checks `if handlers == nil { t.Fatal("expected handlers") }` is dead code because the test fixture always returns a non-nil `handlers`; remove that `if` block from the test in channels_test.go (the check referencing the `handlers` variable) so the test no longer contains the redundant nil-guard and relies on the fixture directly.internal/extension/host_api_channels.go (1)
223-248: Consider logging cleanup failures or returning early on non-critical path.The
maybeCleanupChannelIngestDedupfunction returns an error on cleanup failure, which would fail the entire ingest operation. Since this is a periodic maintenance task, consider whether cleanup failures should be logged but not block message ingestion.♻️ Consider logging instead of failing
if err := retrySQLiteBusy(ctx, hostAPIBusyRetryAttempts, func() error { _, err := h.dedupStore.DeleteExpiredChannelIngestDedup(ctx, now) return err }); err != nil { - return fmt.Errorf("extension: cleanup expired channel ingest dedup: %w", err) + // Log but don't fail ingestion for cleanup errors + if h.logger != nil { + h.logger.Warn("cleanup expired channel ingest dedup failed", "error", err) + } } return nil🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/host_api_channels.go` around lines 223 - 248, The cleanup in maybeCleanupChannelIngestDedup currently returns an error when retrySQLiteBusy(...) fails, which can bubble up and block ingestion; change the behavior to treat cleanup as best-effort: when retrySQLiteBusy (and the underlying h.dedupStore.DeleteExpiredChannelIngestDedup) returns an error, log the failure via the appropriate logger (e.g., processLogger or h.logger) with context (including the error and now value) and return nil so ingestion is not blocked; keep the existing locking and shouldRun logic and only change the error handling around the retrySQLiteBusy call and its fmt.Errorf wrapping.internal/extension/channel_delivery_notifier_test.go (1)
362-372: Consider using a channel-based wait instead of polling.The
waitForExtensionDeliveryCallsusestime.Sleeppolling. While acceptable in tests, a channel-based notification would be more responsive and eliminate arbitrary sleep durations.This is a minor optimization since the current implementation works correctly for test purposes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/channel_delivery_notifier_test.go` around lines 362 - 372, Replace the polling loop in waitForExtensionDeliveryCalls with a channel-based wait: add a notification channel field (e.g., deliveryCh) to recordingDeliveryTransport and have the transport send a signal on that channel whenever its call list grows (the code path that currently updates snapshotCalls). Then change waitForExtensionDeliveryCalls to wait on that channel with a timeout (using select and time.After) and check transport.snapshotCalls() when woken; return when count >= want or fail on timeout. Update recordingDeliveryTransport constructors/teardown to initialize/close the channel as needed.internal/daemon/channels_test.go (1)
31-60: Consider addingt.Parallel()for independent tests.None of the tests in this file use
t.Parallel(). Since each test appears to create its own isolated test fixtures (homePaths, config, DB), they could potentially run in parallel to speed up test execution.As per coding guidelines: "Use t.Parallel() for independent subtests".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/channels_test.go` around lines 31 - 60, Add t.Parallel() as the first statement inside TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence to allow it to run concurrently; ensure the test setup functions you call (testHomePaths, testConfig, newTestDaemon, openDaemonTestGlobalDB) already produce isolated fixtures or use t.TempDir/t.Cleanup so there is no shared state when running in parallel, and keep the rest of the test (composeChannelRuntime checks and bootState/registry manipulation) unchanged.internal/extension/host_api.go (2)
768-768: Useh.now()instead oftime.Now()for testability.The handler has an injectable
nowfunction but this line usestime.Now()directly, making deadline behavior harder to test.- deadline := time.Now().Add(seedPollWindow) + deadline := h.now().Add(seedPollWindow)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/host_api.go` at line 768, Replace the direct call to time.Now() when computing deadline with the handler's injectable clock by using h.now(): change the line that sets deadline := time.Now().Add(seedPollWindow) to use h.now() so deadline := h.now().Add(seedPollWindow) (keep seedPollWindow and the deadline variable intact) to make timing behavior testable via the handler's now() function.
763-766: Consider making seed poll configuration injectable.The hardcoded 5ms poll interval and 30ms window are quite aggressive. While functional, exposing these as options (similar to
channelIngestDedupTTL) would improve testability and allow tuning in production.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/host_api.go` around lines 763 - 766, The hardcoded constants seedPollInterval and seedPollWindow are too aggressive and should be made configurable like channelIngestDedupTTL: add configurable fields (e.g., SeedPollInterval, SeedPollWindow) to the same config/options struct used for channelIngestDedupTTL, wire those fields into the code paths that currently reference seedPollInterval and seedPollWindow (replace uses of the constants with the configured values, falling back to 5ms/30ms defaults), and update any constructors/new functions that create the host API to accept or populate these options so tests and production can override them.internal/api/testutil/apitest.go (1)
185-298: Add a compile-time assertion forStubChannelService.This file already pins the other stubs to their interfaces, but the new channel stub is missing the same guard. Adding
var _ core.ChannelService = (*StubChannelService)(nil)near the existing assertions will make interface drift fail fast.As per coding guidelines, "Use compile-time interface verification:
var _ Interface = (*Type)(nil)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/testutil/apitest.go` around lines 185 - 298, Add a compile-time interface assertion to ensure StubChannelService implements the core.ChannelService interface: declare a var assignment using core.ChannelService and (*StubChannelService)(nil) near the other stub assertions (e.g., alongside existing var _ ... = (*...)(nil) checks) so any future interface drift fails at compile time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/api/core/errors_test.go`:
- Around line 16-80: The subtest names in the tests table use freeform
descriptions instead of the repository convention; update each test case's name
field in the tests slice (e.g. the entries used by t.Run) so they follow the
"Should ..." pattern (for example "Should return bad request when body path
mismatch") so t.Run(tt.name, ...) generates consistent names; adjust all entries
that reference contract.ErrChannelInstanceMismatch,
channelspkg.ErrChannelInstanceNotFound, channelspkg.ErrChannelRouteNotFound,
workspacepkg.ErrWorkspaceNotFound, channelspkg.ErrChannelInstanceUnavailable,
channelspkg.ErrInvalidChannelStateTransition, channelspkg.ErrDeliveryNotFound,
channelspkg.ErrDeliveryQueueSaturated,
channelspkg.ErrDeliveryTransportUnavailable and the generic error used with
StatusForChannelError accordingly.
In `@internal/api/httpapi/httpapi_integration_test.go`:
- Around line 638-660: StartInstance and RestartInstance set instances to
ChannelStatusStarting and leave them stuck; update them to mark the instance
ready synchronously by calling UpdateInstanceState with Status:
channelspkg.ChannelStatusReady (or perform a second UpdateInstanceState to
transition from ChannelStatusStarting to ChannelStatusReady) after enabling the
instance; change the implementations of StartInstance and RestartInstance to
ensure the final state is ChannelStatusReady so availability-gated operations
see the channel as ready.
In `@internal/api/httpapi/server.go`:
- Around line 255-258: The handler wiring currently injects server.channels into
handlerConfig (server.handlers = newHandlers(handlerConfig{...})) while New()
still permits server.channels to be nil; change New() or the handler
registration so that if server.channels (the ChannelService) is nil you either
(a) return an error from New() to fail fast, or (b) avoid registering the
channel-related handlers inside newHandlers/handlerConfig (skip the
/api/channels setup) and log a clear warning; locate the code that constructs
handlerConfig and/or newHandlers and add a nil-check for server.channels
(ChannelService) to enforce one of the two behaviors consistently.
In `@internal/channels/delivery_broker.go`:
- Around line 319-340: The code mutates delivery state (delivery.latestSeq,
latestEventType, currentContent, final, errorText, updatedAt) and calls
recordDeliveryFailureLocked before calling enqueueEventLocked, allowing enqueue
failures to leave the in-memory delivery advanced; change the flow so you
prepare any values from normalized but do NOT assign them to delivery until
after enqueueEventLocked succeeds: call route := b.ensureRouteLocked(...), err =
b.enqueueEventLocked(route, delivery, normalized) while still holding b.mu (or
document lock handoff), and only on err == nil then set delivery.latestSeq =
normalized.Seq, delivery.latestEventType = normalized.EventType,
delivery.currentContent = normalized.Content, delivery.final = normalized.Final,
delivery.updatedAt = b.now(), and if EventType == DeliveryEventTypeError set
delivery.errorText = deliveryErrorText(normalized.Metadata) and call
b.recordDeliveryFailureLocked(...) else clear delivery.errorText; then unlock
and call b.signalRoute(route). Ensure the same change is applied to the other
mentioned blocks (lines ~411-429, 467-499, 808-880) to avoid state advancement
on enqueue failure.
In `@internal/channels/registry.go`:
- Around line 153-166: In CreateInstance, wrap underlying errors with
action-specific context instead of returning raw errors: for failures from
s.checkReady, req.toInstance, and s.store.InsertChannelInstance, return
fmt.Errorf("create channel instance: %w", err) (or include more granular context
like "create channel instance: validation: %w" for req.toInstance and "create
channel instance: store insert: %w" for s.store.InsertChannelInstance) so
callers can distinguish create failures; apply the same wrapping pattern to
sibling registry methods that call checkReady, toInstance, and store operations
(retain use of cloneChannelInstance for the success return).
- Around line 181-186: ListInstances currently returns the slice from
s.store.ListChannelInstances which exposes mutable DeliveryDefaults buffers;
change ListInstances to deep-copy each ChannelInstance returned by
s.store.ListChannelInstances (iterate over the returned slice), clone the
ChannelInstance struct and allocate/copy the DeliveryDefaults slice (and any
nested mutable slices/maps if present) for each element, and return the new
slice so callers cannot mutate shared state; reference the ListInstances method,
ChannelInstance type, DeliveryDefaults field, and s.store.ListChannelInstances
when making the change.
In `@internal/channels/types.go`:
- Around line 174-175: The code calls normalizeRawJSON(...) only to check for
errors but discards the normalized output; instead capture the normalized result
returned by normalizeRawJSON and assign it back to the canonical fields so they
remain canonical (e.g., set normalized.DeliveryDefaults = <normalized output>
after validate call and similarly set DeliveryEvent.Metadata = <normalized
output> where the validator currently calls normalizeRawJSON for error-only
checking); update the validators that reference normalizeRawJSON to both
validate and replace the original value with the normalized JSON string/bytes
returned by normalizeRawJSON.
- Around line 439-445: InboundMessageEnvelope.normalize() currently does
normalized := e which only copies the slice header so looping and assigning
normalized.Attachments[idx] mutates the original e.Attachments; fix by making a
deep copy of the attachments slice before normalizing (e.g., create a new slice
with make(len(e.Attachments)) or use append to copy elements into a new backing
array, then assign sanitized MessageAttachment values into that new slice) so
that normalize() updates normalized.Attachments without modifying the caller's
envelope; update the code paths in InboundMessageEnvelope.normalize, the
normalized variable handling, and any use of normalized.Attachments to use the
copied slice.
In `@internal/cli/command_paths_test.go`:
- Around line 74-82: The test stubs (getChannelFn, channelRoutesFn,
testChannelDeliveryFn) unconditionally return success and don't assert the
inputs, allowing regressions to pass; modify these closures to validate the
incoming parameters (e.g., check the channel ID argument passed to getChannelFn
and channelRoutesFn, and assert fields of the ChannelTestDeliveryRequest in
testChannelDeliveryFn such as PeerID and Mode) and return a non-nil error when
the inputs don't match the expected values (e.g., expected "chan-1", expected
peer id, expected mode) so the test matrix fails on wiring regressions.
In `@internal/daemon/daemon_test.go`:
- Around line 3074-3078: The helper scenarios use time.Sleep to drive a
goroutine that calls os.Exit (when h.scenario == "auto_exit_record_initialize")
and a similar sleep elsewhere, which makes tests flaky; replace the sleep-based
orchestration with explicit synchronization: add a channel or sync.WaitGroup
that the test harness can close/signal at the exact point you want the goroutine
to call os.Exit (or simulate slow delivery) and wire that into the existing
goroutine logic that currently references h.scenario so it blocks on the channel
instead of time.Sleep; update both the "auto_exit_record_initialize" path and
the other scenario (the similar block around the 3091–3097 region) to wait on
the same explicit signal so test timing is deterministic.
- Around line 3207-3215: The appendMarkerLine function currently ignores
file.Close() error; change it to capture the close error and return it (or wrap
it) if non-nil so failures during flush are propagated. Specifically, after
opening the file via os.OpenFile and writing with fmt.Fprintf, store the result
of file.Close() (instead of discarding with `_`), check that error and return it
(or if fmt.Fprintf already returned a non-nil error, return that first or
combine/wrap both) so the function does not report success when Close() fails.
In `@internal/extension/channel_delivery_notifier.go`:
- Around line 56-59: The call to n.broker.ProjectEvent currently swallows the
error ( `_ = n.broker.ProjectEvent(...)` ); change it to capture and handle the
error from n.broker.ProjectEvent(ctx, sessionID,
projectionEventFromAgentEvent(event)) — e.g. `if err :=
n.broker.ProjectEvent(...); err != nil { n.logger.Errorf("ProjectEvent failed
for session %s: %v", sessionID, err) }` — or if logging isn’t available add a
one-line justified comment explaining why ignoring the error is safe; ensure the
fix references n.broker.ProjectEvent and projectionEventFromAgentEvent(event) so
delivery projection failures are not silently dropped.
- Around line 43-45: The call to FailSession on n.broker currently discards its
error ( _ = n.broker.FailSession(ctx, sess.ID, "") ); update this to handle the
error: capture the returned error and either log it via the component's logger
(e.g., n.logger.Errorf or n.log.Warnf) with context (session ID and action), or
if you intentionally want to ignore it, replace the discard with a short comment
explaining why ignoring is safe and referencing any invariants; ensure you
modify the call site (the block that checks n.broker != nil && sess != nil) so
the error is not silently dropped.
In `@internal/extension/describe_test.go`:
- Around line 8-45: Refactor the two standalone tests (e.g.
TestDescribeExtensionReportsActiveSubprocessRuntime and the other test around
lines 47-71 that also calls DescribeExtension) into a single table-driven test:
create a slice of test cases with a descriptive Name string starting with
"Should..." and fields for input Extension, isSubprocess bool, now time, and
expected outputs (Type, State, Health, UptimeSeconds); then iterate cases with
for _, tc := range cases { tc := tc; t.Run(tc.Name, func(t *testing.T) {
t.Parallel(); payload := DescribeExtension(&tc.Extension, tc.isSubprocess,
tc.now); assert each expected field using the existing checks (Type, State,
Health, UptimeSeconds) }) } so each case runs as a subtest and preserves
parallelization and original assertions.
In `@internal/extension/host_api_integration_test.go`:
- Around line 226-293: The test
TestHostAPIIntegrationChannelsMessagesIngestConcurrentSameRoutingKeyUsesOneRouteAndSession
must not call t.Fatal/t.Fatalf from background goroutines (decodeResult may call
t.Fatal). Fix by preventing goroutine panics from escaping: wrap the goroutine
body with a panic catcher that converts panics into results[idx].err (e.g. defer
func(){ if r:=recover(); r!=nil { results[idx].err = fmt.Errorf("panic: %v", r)
} }()), and/or change decodeResult to return an error instead of calling t.Fatal
so the goroutine can set results[idx].err on decode failures; locate
decodeResult and the anonymous goroutine in
TestHostAPIIntegrationChannelsMessagesIngestConcurrentSameRoutingKeyUsesOneRouteAndSession
to apply these changes.
In `@internal/extension/manager_test.go`:
- Around line 1632-1640: The nil-receiver check in
stubChannelRuntimeResolver.ResolveChannelRuntime is unreachable because the
method was already invoked on r; fix by checking for a nil receiver before
accessing fields: first test if r == nil || r.runtimes == nil and return
nil,nil, then check r.err and return it if non-nil, and finally call
subprocess.CloneInitializeChannelRuntime with the lookup on
r.runtimes[strings.TrimSpace(extensionName)]; update the control-flow in
ResolveChannelRuntime accordingly and remove the post-invocation nil check.
In `@internal/extensiontest/channel_adapter_harness_integration_test.go`:
- Around line 126-135: The test assigns report := harness.Report(t) before
calling harness.WaitForDeliveries but never uses that first report since it is
immediately overwritten by another report = harness.Report(t); remove the unused
initial assignment or replace it with an assertion that uses the first report.
Specifically, in
internal/extensiontest/channel_adapter_harness_integration_test.go adjust the
code around the calls to harness.Report(t) and harness.WaitForDeliveries so that
either the first report variable is removed (delete the line with report :=
harness.Report(t)) or add the missing check/assertion against the first report
(referencing the report variable) before calling harness.WaitForDeliveries;
ensure references to DeliveryRecord/normalizeEventType and
channelspkg.DeliveryEventTypeResume remain intact.
In `@internal/extensiontest/channel_adapter_harness.go`:
- Around line 779-786: The cleanup loop in Harness.stopSessions currently
discards the error from h.Sessions.Stop; change it to check the returned error
and fail the test if non-nil. In Harness.stopSessions, after skipping nil info
and calling h.Sessions.Stop(aghtestutil.Context(t), info.ID), capture the error
and call t.Fatalf (or t.Fatalff-style message) with the session ID and error
(e.g., "failed to stop session %s: %v", info.ID, err) so cleanup failures are
reported instead of ignored.
- Around line 401-417: The Prompt method currently ignores the passed ctx and
launches a goroutine that sleeps and sends events unconditionally; change the
goroutine in Prompt to watch ctx.Done() (use select with time.After or a
time.Timer) before each send and exit early if ctx is cancelled to ensure no
events are emitted after teardown (update references: Prompt, events channel,
startedAt, script loop, item.Delay, acp.AgentEvent sends). Also stop discarding
errors in stopSessions: remove `_ = h.Sessions.Stop(...)` and propagate or
return the error (or wrap/log it) from stopSessions so cleanup failures aren’t
hidden (reference: stopSessions and h.Sessions.Stop).
---
Nitpick comments:
In `@internal/api/core/channels_test.go`:
- Around line 108-110: Remove the redundant nil-check assertion for handlers in
channels_test.go: delete the if block that tests "if handlers == nil {
t.Fatal(\"expected handlers\") }" because newChannelHandlerFixture always
returns a valid *core.BaseHandlers; update or re-run the test to ensure no other
code depends on that assertion and rely on direct use of handlers (symbol:
handlers, function: newChannelHandlerFixture, type: *core.BaseHandlers).
- Around line 128-145: The table-driven loop in channels_test.go should wrap
each case in t.Run to isolate failures and allow subtest execution: inside the
for loop, call t.Run with a descriptive name like "Should <action> <path>"
(e.g., using tc.path or the expected status) and move the test body into that
closure; also shadow the loop variable (add tc := tc at the start of the
closure) to avoid capture issues, then call performRequest(t, engine,
http.MethodPost, tc.path, nil), decode into contract.ChannelResponse with
testutil.DecodeJSONResponse, and assert payload.Channel.Status == tc.status as
before. Ensure each subtest uses the same assertions but is contained within the
t.Run closure so failures report the specific case.
- Around line 147-149: The assertion that checks `if handlers == nil {
t.Fatal("expected handlers") }` is dead code because the test fixture always
returns a non-nil `handlers`; remove that `if` block from the test in
channels_test.go (the check referencing the `handlers` variable) so the test no
longer contains the redundant nil-guard and relies on the fixture directly.
In `@internal/api/core/channels.go`:
- Line 156: The call passing the raw path param into
ToResolveDeliveryTargetRequest should normalize whitespace first; instead of
passing c.Param("id") directly to req.ToResolveDeliveryTargetRequest, trim the
parameter (e.g., via strings.TrimSpace) and pass the trimmed value so targetReq,
err := req.ToResolveDeliveryTargetRequest(...) receives a normalized id; update
the code around the targetReq creation to use the trimmed id and import strings
if needed.
- Line 96: Trim whitespace from the path parameter before passing it to the
request builder: replace the direct c.Param("id") usage with
strings.TrimSpace(c.Param("id")) when calling req.ToUpdateInstanceRequest so
updateReq is created from a normalized id (ensure strings is imported if not
present); this mirrors the same normalization applied elsewhere (e.g., the
earlier handler using c.Param("id") at line 74) and prevents inconsistent ID
handling.
- Line 74: The code calls channels.GetInstance with c.Param("id") without
trimming; make it consistent by wrapping the path param with
strings.TrimSpace(c.Param("id")) wherever you pass the ID to lookups (e.g.,
replace the call to channels.GetInstance(c.Request.Context(), c.Param("id"))
with channels.GetInstance(c.Request.Context(), strings.TrimSpace(c.Param("id")))
and apply the same change for the other occurrences flagged (the calls around
the code handling the same ID at the other two locations), ensuring all lookups
use strings.TrimSpace on c.Param("id").
In `@internal/api/httpapi/channels_test.go`:
- Around line 13-131: Combine the three standalone tests
TestCreateChannelHandlerCreatesChannelInstance,
TestListChannelRoutesHandlerReturnsRequestedRouteSet, and
TestChannelTestDeliveryHandlerResolvesTypedTarget into a single table-driven
test (e.g., TestChannelsHandlers) that uses t.Run("Should ...") subtests; create
a slice of cases each with a name and per-case stubChannelService behavior
(CreateInstanceFn, ListRoutesFn, ResolveDeliveryTargetFn) and expected
request/response checks, reuse common setup like homePaths and engine created
with newTestHandlersWithChannels, and for each case call performRequest and
decodeJSONResponse then assert expectations — this consolidates duplicated setup
and follows the repository t.Run pattern.
In `@internal/api/testutil/apitest.go`:
- Around line 185-298: Add a compile-time interface assertion to ensure
StubChannelService implements the core.ChannelService interface: declare a var
assignment using core.ChannelService and (*StubChannelService)(nil) near the
other stub assertions (e.g., alongside existing var _ ... = (*...)(nil) checks)
so any future interface drift fails at compile time.
In `@internal/api/udsapi/channels_integration_test.go`:
- Around line 77-88: The helper mustReadAll currently ignores the error from
body.Close(); change the defer to capture and handle the Close error (do not
assign to `_`): in the defer in mustReadAll, call err := body.Close() and, if
non-nil, report it via the testing.T (e.g., t.Logf or t.Errorf) so the test
surfaces cleanup failures; ensure the defer still runs t.Helper() context and
returns the read string as before.
In `@internal/api/udsapi/udsapi_integration_test.go`:
- Around line 316-338: The StartInstance, StopInstance, and RestartInstance
helpers currently return raw errors from UpdateInstanceState; update each method
(StartInstance, StopInstance, RestartInstance) to check the error returned by
UpdateInstanceState and return a wrapped error using fmt.Errorf with contextual
text (e.g., "start instance %s: %w", "stop instance %s: %w", "restart instance
%s: %w") so failures include operation and instance id context while preserving
the original error.
In `@internal/channels/target_integration_test.go`:
- Around line 107-112: The second check comparing workspaceTarget.GroupID to
globalTarget.GroupID is redundant because workspaceTarget.GroupID is already
asserted to be empty and globalTarget.GroupID is asserted earlier to be
"global-group"; remove the entire if block that checks "if
workspaceTarget.GroupID == globalTarget.GroupID { t.Fatalf(...)}" so only the
emptiness assertion for workspaceTarget.GroupID remains (referencing
workspaceTarget.GroupID and globalTarget.GroupID to locate the code).
In `@internal/daemon/channels_test.go`:
- Around line 31-60: Add t.Parallel() as the first statement inside
TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence to
allow it to run concurrently; ensure the test setup functions you call
(testHomePaths, testConfig, newTestDaemon, openDaemonTestGlobalDB) already
produce isolated fixtures or use t.TempDir/t.Cleanup so there is no shared state
when running in parallel, and keep the rest of the test (composeChannelRuntime
checks and bootState/registry manipulation) unchanged.
In `@internal/daemon/daemon.go`:
- Around line 108-114: The type assertion in channelObserveSource intentionally
ignores the boolean ok and returns nil when the provided core.ChannelService
does not implement observe.ChannelSource; add a clear comment above the
channelObserveSource function explaining that this silent-nil behavior is
deliberate (callers expect and handle nil) and why we avoid returning an error
or panicking, referencing the types core.ChannelService and
observe.ChannelSource so future maintainers understand the rationale.
In `@internal/extension/channel_delivery_integration_test.go`:
- Line 305: The capabilities slice contains a redundant cast
string("channel.adapter"); remove the unnecessary conversion and use the string
literal "channel.adapter" directly in the capabilities field (the slice assigned
to capabilities) to simplify the code.
In `@internal/extension/channel_delivery_notifier_test.go`:
- Around line 362-372: Replace the polling loop in waitForExtensionDeliveryCalls
with a channel-based wait: add a notification channel field (e.g., deliveryCh)
to recordingDeliveryTransport and have the transport send a signal on that
channel whenever its call list grows (the code path that currently updates
snapshotCalls). Then change waitForExtensionDeliveryCalls to wait on that
channel with a timeout (using select and time.After) and check
transport.snapshotCalls() when woken; return when count >= want or fail on
timeout. Update recordingDeliveryTransport constructors/teardown to
initialize/close the channel as needed.
In `@internal/extension/host_api_channels.go`:
- Around line 223-248: The cleanup in maybeCleanupChannelIngestDedup currently
returns an error when retrySQLiteBusy(...) fails, which can bubble up and block
ingestion; change the behavior to treat cleanup as best-effort: when
retrySQLiteBusy (and the underlying
h.dedupStore.DeleteExpiredChannelIngestDedup) returns an error, log the failure
via the appropriate logger (e.g., processLogger or h.logger) with context
(including the error and now value) and return nil so ingestion is not blocked;
keep the existing locking and shouldRun logic and only change the error handling
around the retrySQLiteBusy call and its fmt.Errorf wrapping.
In `@internal/extension/host_api.go`:
- Line 768: Replace the direct call to time.Now() when computing deadline with
the handler's injectable clock by using h.now(): change the line that sets
deadline := time.Now().Add(seedPollWindow) to use h.now() so deadline :=
h.now().Add(seedPollWindow) (keep seedPollWindow and the deadline variable
intact) to make timing behavior testable via the handler's now() function.
- Around line 763-766: The hardcoded constants seedPollInterval and
seedPollWindow are too aggressive and should be made configurable like
channelIngestDedupTTL: add configurable fields (e.g., SeedPollInterval,
SeedPollWindow) to the same config/options struct used for
channelIngestDedupTTL, wire those fields into the code paths that currently
reference seedPollInterval and seedPollWindow (replace uses of the constants
with the configured values, falling back to 5ms/30ms defaults), and update any
constructors/new functions that create the host API to accept or populate these
options so tests and production can override them.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 94b7b876-edfc-47d3-b57e-4b075b292b5b
⛔ Files ignored due to path filters (32)
.compozy/tasks/_archived/20260411-014454-ext-architecture/_protocol.mdis excluded by!**/*.md.compozy/tasks/channels/_meta.mdis excluded by!**/*.md.compozy/tasks/channels/_tasks.mdis excluded by!**/*.md.compozy/tasks/channels/memory/MEMORY.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_01.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_02.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_03.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_04.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_05.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_06.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_07.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_08.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_09.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_10.mdis excluded by!**/*.md.compozy/tasks/channels/memory/task_11.mdis excluded by!**/*.md.compozy/tasks/channels/task_01.mdis excluded by!**/*.md.compozy/tasks/channels/task_02.mdis excluded by!**/*.md.compozy/tasks/channels/task_03.mdis excluded by!**/*.md.compozy/tasks/channels/task_04.mdis excluded by!**/*.md.compozy/tasks/channels/task_05.mdis excluded by!**/*.md.compozy/tasks/channels/task_06.mdis excluded by!**/*.md.compozy/tasks/channels/task_07.mdis excluded by!**/*.md.compozy/tasks/channels/task_08.mdis excluded by!**/*.md.compozy/tasks/channels/task_09.mdis excluded by!**/*.md.compozy/tasks/channels/task_10.mdis excluded by!**/*.md.compozy/tasks/channels/task_11.mdis excluded by!**/*.mdbun.lockis excluded by!**/*.lockopenapi/agh.jsonis excluded by!**/*.jsonsdk/examples/telegram-reference/README.mdis excluded by!**/*.mdsdk/examples/telegram-reference/extension.tomlis excluded by!**/*.tomlsdk/typescript/src/generated/contracts.tsis excluded by!**/generated/**web/src/generated/agh-openapi.d.tsis excluded by!**/generated/**
📒 Files selected for processing (104)
internal/api/contract/channels.gointernal/api/contract/channels_test.gointernal/api/contract/contract.gointernal/api/core/channels.gointernal/api/core/channels_test.gointernal/api/core/conversions.gointernal/api/core/errors.gointernal/api/core/errors_test.gointernal/api/core/handlers.gointernal/api/core/interfaces.gointernal/api/httpapi/channels_integration_test.gointernal/api/httpapi/channels_test.gointernal/api/httpapi/handlers.gointernal/api/httpapi/handlers_test.gointernal/api/httpapi/helpers_test.gointernal/api/httpapi/httpapi_integration_test.gointernal/api/httpapi/routes.gointernal/api/httpapi/server.gointernal/api/spec/spec.gointernal/api/spec/spec_test.gointernal/api/testutil/apitest.gointernal/api/udsapi/channels_integration_test.gointernal/api/udsapi/channels_test.gointernal/api/udsapi/handlers_test.gointernal/api/udsapi/helpers_test.gointernal/api/udsapi/routes.gointernal/api/udsapi/server.gointernal/api/udsapi/server_test.gointernal/api/udsapi/udsapi_integration_test.gointernal/channels/delivery_broker.gointernal/channels/delivery_broker_test.gointernal/channels/delivery_metrics.gointernal/channels/delivery_projection_test.gointernal/channels/delivery_types.gointernal/channels/dimensions.gointernal/channels/doc.gointernal/channels/lifecycle.gointernal/channels/registry.gointernal/channels/registry_integration_test.gointernal/channels/registry_test.gointernal/channels/routing.gointernal/channels/target.gointernal/channels/target_integration_test.gointernal/channels/target_test.gointernal/channels/types.gointernal/channels/types_test.gointernal/cli/channel.gointernal/cli/channel_test.gointernal/cli/cli_integration_test.gointernal/cli/client.gointernal/cli/client_test.gointernal/cli/command_paths_test.gointernal/cli/helpers_test.gointernal/cli/root.gointernal/daemon/boot.gointernal/daemon/channels.gointernal/daemon/channels_test.gointernal/daemon/daemon.gointernal/daemon/daemon_integration_test.gointernal/daemon/daemon_test.gointernal/extension/capability.gointernal/extension/capability_test.gointernal/extension/channel_delivery_integration_test.gointernal/extension/channel_delivery_notifier.gointernal/extension/channel_delivery_notifier_test.gointernal/extension/contract/host_api.gointernal/extension/contract/sdk.gointernal/extension/describe_test.gointernal/extension/host_api.gointernal/extension/host_api_channels.gointernal/extension/host_api_integration_test.gointernal/extension/host_api_test.gointernal/extension/manager.gointernal/extension/manager_integration_test.gointernal/extension/manager_test.gointernal/extension/protocol/host_api.gointernal/extension/telegram_reference_integration_test.gointernal/extensiontest/channel_adapter_harness.gointernal/extensiontest/channel_adapter_harness_integration_test.gointernal/extensiontest/channel_adapter_harness_test.gointernal/observe/channels.gointernal/observe/channels_test.gointernal/observe/health.gointernal/observe/observer.gointernal/observe/observer_test.gointernal/store/globaldb/global_db.gointernal/store/globaldb/global_db_channel.gointernal/store/globaldb/global_db_channels_integration_test.gointernal/store/globaldb/global_db_channels_test.gointernal/subprocess/handshake.gointernal/subprocess/process_test.gointernal/transcript/transcript.gointernal/transcript/transcript_test.gosdk/examples/telegram-reference/.gitignoresdk/examples/telegram-reference/main.gosdk/examples/telegram-reference/main_test.gosdk/typescript/src/extension.test.tssdk/typescript/src/extension.tssdk/typescript/src/host-api.test.tssdk/typescript/src/host-api.tssdk/typescript/src/index.tsweb/src/systems/daemon/adapters/daemon-api.test.tsweb/src/systems/daemon/hooks/use-daemon-health.test.tsweb/src/systems/daemon/types.test.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
internal/extension/channel_delivery_notifier.go (1)
79-89:⚠️ Potential issue | 🟠 MajorHandle or justify fingerprint marshal failures explicitly.
transcript.MarshalAgentEvent(event)failures are currently swallowed. Please either log the error (best-effort path) or add a short inline justification comment for why this failure is safe to ignore.🔧 Suggested fix
-func projectionEventFromAgentEvent(event acp.AgentEvent) channelspkg.DeliveryProjectionEvent { +func projectionEventFromAgentEvent(ctx context.Context, event acp.AgentEvent) channelspkg.DeliveryProjectionEvent { projected := channelspkg.DeliveryProjectionEvent{ Type: event.Type, TurnID: event.TurnID, Timestamp: event.Timestamp, Text: event.Text, Error: event.Error, } - if fingerprint, err := transcript.MarshalAgentEvent(event); err == nil { - projected.Fingerprint = fingerprint - } + fingerprint, err := transcript.MarshalAgentEvent(event) + if err != nil { + slog.WarnContext(ctx, "extension: failed to fingerprint agent event", + "event_type", event.Type, + "turn_id", event.TurnID, + "error", err, + ) + return projected + } + projected.Fingerprint = fingerprint return projected }- if err := n.broker.ProjectEvent(ctx, sessionID, projectionEventFromAgentEvent(event)); err != nil { + if err := n.broker.ProjectEvent(ctx, sessionID, projectionEventFromAgentEvent(ctx, event)); err != nil {As per coding guidelines, "Never ignore errors with
_— every error must be handled or have a written justification."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/channel_delivery_notifier.go` around lines 79 - 89, In projectionEventFromAgentEvent, do not silently ignore errors from transcript.MarshalAgentEvent(event); instead handle the error by logging it via the appropriate logger (or return/propagate if applicable) or add a concise inline comment justifying why a marshal failure is safe to ignore; update the block around transcript.MarshalAgentEvent to capture the error variable and either call the logger (e.g., processLogger/errorLogger) with context including event.Type/TurnID or add a one-line comment referencing why ignoring the error is acceptable for DeliveryProjectionEvent fingerprinting.
🧹 Nitpick comments (8)
internal/api/udsapi/udsapi_integration_test.go (2)
316-364: DeduplicateStartInstanceandRestartInstancetransition flow.Both methods run the same Starting → Ready sequence. A shared helper would reduce maintenance drift.
Refactor sketch
+func (s *integrationChannelService) transitionToReady(ctx context.Context, id, action string) (*channelspkg.ChannelInstance, error) { + if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + ID: id, + Enabled: true, + Status: channelspkg.ChannelStatusStarting, + }); err != nil { + return nil, fmt.Errorf("%s channel instance %q: %w", action, id, err) + } + instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + ID: id, + Enabled: true, + Status: channelspkg.ChannelStatusReady, + }) + if err != nil { + return nil, fmt.Errorf("mark %s channel instance %q ready: %w", action, id, err) + } + return instance, nil +} + func (s *integrationChannelService) StartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusStarting, - }); err != nil { - return nil, fmt.Errorf("start channel instance %q: %w", id, err) - } - instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusReady, - }) - if err != nil { - return nil, fmt.Errorf("mark channel instance %q ready: %w", id, err) - } - return instance, nil + return s.transitionToReady(ctx, id, "start") } func (s *integrationChannelService) RestartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusStarting, - }); err != nil { - return nil, fmt.Errorf("restart channel instance %q: %w", id, err) - } - instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusReady, - }) - if err != nil { - return nil, fmt.Errorf("mark restarted channel instance %q ready: %w", id, err) - } - return instance, nil + return s.transitionToReady(ctx, id, "restart") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/udsapi_integration_test.go` around lines 316 - 364, Extract the duplicate Starting→Ready sequence from StartInstance and RestartInstance into a small helper (e.g., ensureInstanceStartsAndReady or transitionInstanceToReady) that takes ctx and id, calls UpdateInstanceState twice (first with Status: channelspkg.ChannelStatusStarting, Enabled: true and then with Status: channelspkg.ChannelStatusReady, Enabled: true), and returns the final *channelspkg.ChannelInstance and error; then have StartInstance and RestartInstance call that helper and preserve the existing wrapped error messages (use the helper's returned error to wrap with the same contextual strings you currently use in StartInstance and RestartInstance), leaving StopInstance unchanged.
308-314: Add an explicit compile-time interface assertion for the channel wrapper.
integrationChannelServiceis now a contract boundary forWithChannelService; addingvar _ <interface> = (*integrationChannelService)(nil)here makes drift fail at compile time.As per coding guidelines, "Use compile-time interface verification:
var _ Interface = (*Type)(nil)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/udsapi_integration_test.go` around lines 308 - 314, Add a compile-time interface assertion so the compiler will catch drift: in the same file declare a verification line asserting integrationChannelService implements the interface expected by WithChannelService (i.e., add a statement of the form var _ <InterfaceName> = (*integrationChannelService)(nil) using the interface type that WithChannelService requires from channelspkg). This ensures integrationChannelService and the WithChannelService contract stay in sync.internal/daemon/daemon_test.go (2)
3239-3252: Silent error handling in test utility.
markerLineCountreturns 0 onos.ReadFileerror without logging or indication. While acceptable for test helper code (treating missing/unreadable files as empty), consider adding a brief comment explaining this intentional behavior for future maintainers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/daemon_test.go` around lines 3239 - 3252, The helper function markerLineCount silently returns 0 on os.ReadFile errors without explanation; add a brief comment above markerLineCount stating that returning 0 on read errors is intentional for tests (treating missing/unreadable files as empty) so future maintainers understand the silence, and mention any cases where this behavior would be unacceptable; keep the function logic unchanged but document the rationale.
3126-3140: Fire-and-forget goroutine may cause race with shutdown.The goroutines spawned by
sendDelayedDeliveryResultare not tracked. WhenreleaseSlowDeliveriesis called during shutdown, pending goroutines unblock but there's no wait for them to complete sending results before the shutdown response is sent and the process potentially exits.This could cause test flakiness if tests expect all delivery acks to arrive before the shutdown ack.
♻️ Consider tracking pending deliveries with WaitGroup
type daemonExtensionHelperServer struct { scenario string marker string scanner *bufio.Scanner encoder *json.Encoder slowDeliveryRelease chan struct{} slowDeliveryReleaseOnce sync.Once + pendingDeliveries sync.WaitGroup mu sync.Mutex } func (h *daemonExtensionHelperServer) sendDelayedDeliveryResult(id any, ack channelspkg.DeliveryAck) { + h.pendingDeliveries.Add(1) go func() { + defer h.pendingDeliveries.Done() <-h.slowDeliveryRelease if err := h.sendResult(id, ack); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }() } func (h *daemonExtensionHelperServer) releaseSlowDeliveries() { h.slowDeliveryReleaseOnce.Do(func() { close(h.slowDeliveryRelease) }) + h.pendingDeliveries.Wait() }As per coding guidelines, "
**/*.go: No fire-and-forget goroutines — track with sync.WaitGroup or equivalent`".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/daemon_test.go` around lines 3126 - 3140, sendDelayedDeliveryResult spawns untracked goroutines which can race with shutdown; add a sync.WaitGroup field (e.g., pendingDeliveries) to the helper, call pendingDeliveries.Add(1) before starting the goroutine in sendDelayedDeliveryResult, ensure the goroutine calls pendingDeliveries.Done() after sendResult returns (including on error), and change releaseSlowDeliveries to close(slowDeliveryRelease) and then wait for pendingDeliveries.Wait() (keeping slowDeliveryReleaseOnce). This ensures all delayed sendResult calls complete before shutdown proceeds.internal/extension/host_api.go (1)
722-722: Consider tracking the drain goroutine or documenting the justification.
go drainAgentEvents(eventsCh)is a fire-and-forget goroutine. While this pattern is common for preventing channel blocking, it technically violates the guideline requiring goroutine tracking.The goroutine will terminate when the channel closes (after session completion), so it's bounded. However, if many prompts are submitted rapidly before completions, multiple drain goroutines could accumulate.
Consider either:
- Adding a brief comment justifying why tracking isn't needed here
- Using a bounded worker pool or tracking mechanism if this is a hot path
As per coding guidelines: "No fire-and-forget goroutines — track with sync.WaitGroup or equivalent".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/extension/host_api.go` at line 722, The call to start drainAgentEvents as a fire-and-forget goroutine (go drainAgentEvents(eventsCh)) can create untracked goroutines; either document why this is safe (the goroutine is bounded and exits on eventsCh close) or convert to tracked work: pass a context or sync.WaitGroup into drainAgentEvents and register the goroutine (wg.Add(1)/wg.Done()) or run it from a small fixed worker pool to avoid accumulation; update the invocation site where go drainAgentEvents(eventsCh) is called and the drainAgentEvents signature to accept the tracking primitive (context or *sync.WaitGroup) and ensure the goroutine exits cleanly when the session ends.internal/channels/registry.go (1)
392-407:ListRoutesreturns routes without cloning, unlikeListInstances.
ListInstancesdeep-clones each returned instance (lines 196-200), butListRoutesreturns the store's slice directly. IfChannelRoutecontains mutable fields (e.g., JSON data, metadata maps), callers could mutate shared state.♻️ Suggested fix for consistency
func (s *Service) ListRoutes(ctx context.Context, channelInstanceID string) ([]ChannelRoute, error) { if err := s.checkReady(ctx, "list channel routes"); err != nil { return nil, err } trimmedInstanceID := strings.TrimSpace(channelInstanceID) if _, err := s.store.GetChannelInstance(ctx, trimmedInstanceID); err != nil { return nil, fmt.Errorf("channels: list channel routes for %q: load channel instance: %w", trimmedInstanceID, err) } routes, err := s.store.ListChannelRoutes(ctx, trimmedInstanceID) if err != nil { return nil, fmt.Errorf("channels: list channel routes for %q: %w", trimmedInstanceID, err) } - return routes, nil + if len(routes) == 0 { + return routes, nil + } + cloned := make([]ChannelRoute, 0, len(routes)) + for _, route := range routes { + cloned = append(cloned, *cloneChannelRoute(route)) + } + return cloned, nil }Additionally, verify whether
cloneChannelRouteneeds to deep-copy any mutable fields (similar to howcloneChannelInstancecopiesDeliveryDefaults).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/channels/registry.go` around lines 392 - 407, ListRoutes currently returns the store slice directly, risking shared-mutable state unlike ListInstances which deep-clones each instance; update Service.ListRoutes to iterate over routes returned by s.store.ListChannelRoutes and append cloned copies (use or add a cloneChannelRoute function) before returning, mirroring the pattern used in ListInstances (lines around cloneChannelInstance usage). Ensure cloneChannelRoute performs deep copies of any mutable fields (maps, slices, and JSON/encoding/json.RawMessage or similar fields, and DeliveryDefaults if present) so callers cannot mutate underlying store data.internal/channels/delivery_projection_test.go (1)
366-460: Split this validation matrix into table-driven subtests.This single test mixes snapshot validation, request validation, normalization, and metadata helpers, so one failure hides the rest and makes the broken path harder to spot. A small table with
t.Run("Should...")subtests would fit the rest of this suite much better. As per coding guidelines, "Use table-driven tests with subtests (t.Run) as default" and "MUST use t.Run("Should...") pattern for ALL test cases".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/channels/delivery_projection_test.go` around lines 366 - 460, Split TestDeliveryValidationAndMetadataHelpers into table-driven subtests using t.Run("Should ...") so each validation path is isolated: create separate subtests for snapshot validation (exercising DeliverySnapshot.Validate and cases like lastAcked>lastSent and mismatched DeliveryID), request validation (DeliveryRequest.Validate covering resume-with-snapshot, resume-without-snapshot, and mismatched-snapshot cases), normalization (DeliveryProjectionEvent.normalize trimming fields), and metadata helpers (deliveryMetadataJSON and deliveryErrorText behaviors). For each case either build a test-case struct slice and iterate with t.Run or create individual t.Run blocks that assert the same expectations currently in the test, keeping the existing helper values (snapshot, resumeEvent, badSnapshot) and using descriptive "Should ..." names per coding guidelines.internal/channels/delivery_broker_test.go (1)
556-579: Replace sleep-based polling with explicit synchronization.These helpers spin on
time.Sleep, which makes the async broker tests timing-sensitive under CI load. Prefer signaling from the fake transport or another synchronization primitive instead of fixed 10ms polling loops. As per coding guidelines, "Never use time.Sleep() in orchestration — use proper synchronization primitives".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/channels/delivery_broker_test.go` around lines 556 - 579, The helpers waitForCalls and waitForCondition use time.Sleep polling which makes tests flaky; update them to use explicit synchronization: add a notification mechanism on fakeDeliveryTransport (e.g., a callsChanged channel or cond var inside fakeDeliveryTransport referenced by snapshotCalls) and have waitForCalls block waiting on that channel/cond with a timeout instead of sleeping, waking and rechecking when the fake transport signals a new call; similarly change waitForCondition to accept a context or a notify channel and wait on that channel (or the context deadline) to detect the condition becoming true; ensure fakeDeliveryTransport signals after mutating its internal call slice so tests unblock deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/channels/delivery_broker_test.go`:
- Around line 234-250: The test currently waits only for transport call count
via waitForCalls(t, transport, 2) which races with
fakeDeliveryTransport.DeliverChannel appending requests before the DeliveryAck
is applied; change the test to wait for the actual ack state before asserting
snapshot fields: either add an explicit ack-notify channel on
fakeDeliveryTransport that is closed/sent to when the handler applies the
DeliveryAck and use that in the test, or poll broker.Snapshot(ctx,
reg.DeliveryID) until snapshot.LastAckedSeq == 2 (with a short timeout) before
asserting RemoteMessageID and ReplaceRemoteMessageID; update uses of
waitForCalls, fakeDeliveryTransport.DeliverChannel, and broker.Snapshot
accordingly.
In `@internal/channels/delivery_projection_test.go`:
- Around line 481-483: Replace the direct sentinel error equality check in the
test for projectEventLocked with an errors.Is() check: change the condition that
currently compares err != ErrDeliveryNotFound to use !errors.Is(err,
ErrDeliveryNotFound) (and ensure the errors package is imported); apply the same
pattern wherever enqueueEventLocked or projectEventLocked sentinel errors are
compared to ErrDeliveryNotFound in this file to match existing usage.
---
Duplicate comments:
In `@internal/extension/channel_delivery_notifier.go`:
- Around line 79-89: In projectionEventFromAgentEvent, do not silently ignore
errors from transcript.MarshalAgentEvent(event); instead handle the error by
logging it via the appropriate logger (or return/propagate if applicable) or add
a concise inline comment justifying why a marshal failure is safe to ignore;
update the block around transcript.MarshalAgentEvent to capture the error
variable and either call the logger (e.g., processLogger/errorLogger) with
context including event.Type/TurnID or add a one-line comment referencing why
ignoring the error is acceptable for DeliveryProjectionEvent fingerprinting.
---
Nitpick comments:
In `@internal/api/udsapi/udsapi_integration_test.go`:
- Around line 316-364: Extract the duplicate Starting→Ready sequence from
StartInstance and RestartInstance into a small helper (e.g.,
ensureInstanceStartsAndReady or transitionInstanceToReady) that takes ctx and
id, calls UpdateInstanceState twice (first with Status:
channelspkg.ChannelStatusStarting, Enabled: true and then with Status:
channelspkg.ChannelStatusReady, Enabled: true), and returns the final
*channelspkg.ChannelInstance and error; then have StartInstance and
RestartInstance call that helper and preserve the existing wrapped error
messages (use the helper's returned error to wrap with the same contextual
strings you currently use in StartInstance and RestartInstance), leaving
StopInstance unchanged.
- Around line 308-314: Add a compile-time interface assertion so the compiler
will catch drift: in the same file declare a verification line asserting
integrationChannelService implements the interface expected by
WithChannelService (i.e., add a statement of the form var _ <InterfaceName> =
(*integrationChannelService)(nil) using the interface type that
WithChannelService requires from channelspkg). This ensures
integrationChannelService and the WithChannelService contract stay in sync.
In `@internal/channels/delivery_broker_test.go`:
- Around line 556-579: The helpers waitForCalls and waitForCondition use
time.Sleep polling which makes tests flaky; update them to use explicit
synchronization: add a notification mechanism on fakeDeliveryTransport (e.g., a
callsChanged channel or cond var inside fakeDeliveryTransport referenced by
snapshotCalls) and have waitForCalls block waiting on that channel/cond with a
timeout instead of sleeping, waking and rechecking when the fake transport
signals a new call; similarly change waitForCondition to accept a context or a
notify channel and wait on that channel (or the context deadline) to detect the
condition becoming true; ensure fakeDeliveryTransport signals after mutating its
internal call slice so tests unblock deterministically.
In `@internal/channels/delivery_projection_test.go`:
- Around line 366-460: Split TestDeliveryValidationAndMetadataHelpers into
table-driven subtests using t.Run("Should ...") so each validation path is
isolated: create separate subtests for snapshot validation (exercising
DeliverySnapshot.Validate and cases like lastAcked>lastSent and mismatched
DeliveryID), request validation (DeliveryRequest.Validate covering
resume-with-snapshot, resume-without-snapshot, and mismatched-snapshot cases),
normalization (DeliveryProjectionEvent.normalize trimming fields), and metadata
helpers (deliveryMetadataJSON and deliveryErrorText behaviors). For each case
either build a test-case struct slice and iterate with t.Run or create
individual t.Run blocks that assert the same expectations currently in the test,
keeping the existing helper values (snapshot, resumeEvent, badSnapshot) and
using descriptive "Should ..." names per coding guidelines.
In `@internal/channels/registry.go`:
- Around line 392-407: ListRoutes currently returns the store slice directly,
risking shared-mutable state unlike ListInstances which deep-clones each
instance; update Service.ListRoutes to iterate over routes returned by
s.store.ListChannelRoutes and append cloned copies (use or add a
cloneChannelRoute function) before returning, mirroring the pattern used in
ListInstances (lines around cloneChannelInstance usage). Ensure
cloneChannelRoute performs deep copies of any mutable fields (maps, slices, and
JSON/encoding/json.RawMessage or similar fields, and DeliveryDefaults if
present) so callers cannot mutate underlying store data.
In `@internal/daemon/daemon_test.go`:
- Around line 3239-3252: The helper function markerLineCount silently returns 0
on os.ReadFile errors without explanation; add a brief comment above
markerLineCount stating that returning 0 on read errors is intentional for tests
(treating missing/unreadable files as empty) so future maintainers understand
the silence, and mention any cases where this behavior would be unacceptable;
keep the function logic unchanged but document the rationale.
- Around line 3126-3140: sendDelayedDeliveryResult spawns untracked goroutines
which can race with shutdown; add a sync.WaitGroup field (e.g.,
pendingDeliveries) to the helper, call pendingDeliveries.Add(1) before starting
the goroutine in sendDelayedDeliveryResult, ensure the goroutine calls
pendingDeliveries.Done() after sendResult returns (including on error), and
change releaseSlowDeliveries to close(slowDeliveryRelease) and then wait for
pendingDeliveries.Wait() (keeping slowDeliveryReleaseOnce). This ensures all
delayed sendResult calls complete before shutdown proceeds.
In `@internal/extension/host_api.go`:
- Line 722: The call to start drainAgentEvents as a fire-and-forget goroutine
(go drainAgentEvents(eventsCh)) can create untracked goroutines; either document
why this is safe (the goroutine is bounded and exits on eventsCh close) or
convert to tracked work: pass a context or sync.WaitGroup into drainAgentEvents
and register the goroutine (wg.Add(1)/wg.Done()) or run it from a small fixed
worker pool to avoid accumulation; update the invocation site where go
drainAgentEvents(eventsCh) is called and the drainAgentEvents signature to
accept the tracking primitive (context or *sync.WaitGroup) and ensure the
goroutine exits cleanly when the session ends.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fb78c5ea-91e2-4485-b61c-19330d5e64af
⛔ Files ignored due to path filters (38)
.compozy/tasks/channels/reviews-001/_meta.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_001.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_002.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_003.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_004.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_005.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_006.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_007.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_008.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_009.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_010.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_011.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_012.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_013.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_014.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_015.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_016.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_017.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_018.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_019.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_020.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_021.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_022.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_023.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_024.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_025.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_026.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_027.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_028.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_029.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_030.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_031.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_032.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_033.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_034.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_035.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_036.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-001/issue_037.mdis excluded by!**/*.md
📒 Files selected for processing (25)
internal/api/core/channels_test.gointernal/api/core/errors_test.gointernal/api/httpapi/httpapi_integration_test.gointernal/api/testutil/apitest.gointernal/api/udsapi/channels_integration_test.gointernal/api/udsapi/udsapi_integration_test.gointernal/channels/delivery_broker.gointernal/channels/delivery_broker_test.gointernal/channels/delivery_projection_test.gointernal/channels/registry.gointernal/channels/registry_test.gointernal/channels/target_integration_test.gointernal/channels/types.gointernal/channels/types_test.gointernal/daemon/channels_test.gointernal/daemon/daemon_test.gointernal/extension/channel_delivery_integration_test.gointernal/extension/channel_delivery_notifier.gointernal/extension/channel_delivery_notifier_test.gointernal/extension/describe_test.gointernal/extension/host_api.gointernal/extension/host_api_integration_test.gointernal/extension/manager_test.gointernal/extensiontest/channel_adapter_harness.gointernal/extensiontest/channel_adapter_harness_integration_test.go
✅ Files skipped from review due to trivial changes (8)
- internal/api/core/errors_test.go
- internal/channels/target_integration_test.go
- internal/channels/registry_test.go
- internal/api/core/channels_test.go
- internal/extension/channel_delivery_notifier_test.go
- internal/daemon/channels_test.go
- internal/channels/types.go
- internal/api/testutil/apitest.go
🚧 Files skipped from review as they are similar to previous changes (6)
- internal/extension/host_api_integration_test.go
- internal/api/udsapi/channels_integration_test.go
- internal/extension/channel_delivery_integration_test.go
- internal/channels/types_test.go
- internal/extension/describe_test.go
- internal/channels/delivery_broker.go
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
internal/api/udsapi/udsapi_integration_test.go (1)
319-367: Optional DRY refactor: consolidate duplicated start/restart transition flow.
StartInstanceandRestartInstanceshare the same two-step transition logic. Extracting a helper would reduce duplication and keep error wording consistent.♻️ Suggested refactor
+func (s *integrationChannelService) transitionToReady(ctx context.Context, id, op string) (*channelspkg.ChannelInstance, error) { + if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + ID: id, + Enabled: true, + Status: channelspkg.ChannelStatusStarting, + }); err != nil { + return nil, fmt.Errorf("%s channel instance %q: %w", op, id, err) + } + instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ + ID: id, + Enabled: true, + Status: channelspkg.ChannelStatusReady, + }) + if err != nil { + return nil, fmt.Errorf("mark %s channel instance %q ready: %w", op, id, err) + } + return instance, nil +} + func (s *integrationChannelService) StartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusStarting, - }); err != nil { - return nil, fmt.Errorf("start channel instance %q: %w", id, err) - } - instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusReady, - }) - if err != nil { - return nil, fmt.Errorf("mark channel instance %q ready: %w", id, err) - } - return instance, nil + return s.transitionToReady(ctx, id, "start") } func (s *integrationChannelService) RestartInstance(ctx context.Context, id string) (*channelspkg.ChannelInstance, error) { - if _, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusStarting, - }); err != nil { - return nil, fmt.Errorf("restart channel instance %q: %w", id, err) - } - instance, err := s.UpdateInstanceState(ctx, channelspkg.UpdateInstanceStateRequest{ - ID: id, - Enabled: true, - Status: channelspkg.ChannelStatusReady, - }) - if err != nil { - return nil, fmt.Errorf("mark restarted channel instance %q ready: %w", id, err) - } - return instance, nil + return s.transitionToReady(ctx, id, "restart") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/udsapi_integration_test.go` around lines 319 - 367, StartInstance and RestartInstance duplicate the same two-step state transitions via UpdateInstanceState; extract a small helper (e.g., performStartTransition or setInstanceStartingThenReady) that accepts (ctx context.Context, id string) and runs the two UpdateInstanceState calls (Status: ChannelStatusStarting then ChannelStatusReady, Enabled: true), returns (*channelspkg.ChannelInstance, error), and use that helper from both StartInstance and RestartInstance so the flow and error messages are consolidated and consistent.internal/channels/delivery_projection_test.go (1)
675-681: Replace sleep-based polling inwaitForSnapshot.This helper busy-waits with
time.Sleep(10 * time.Millisecond), which tends to make parallel test runs slower and more flaky in CI. A notification-driven wait, or an existing eventual helper frominternal/testutil, would be more reliable here.As per coding guidelines, "Never use time.Sleep() in orchestration — use proper synchronization primitives" and "Use shared test helpers from
internal/testutilandinternal/api/testutil".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/channels/delivery_projection_test.go` around lines 675 - 681, The busy-wait loop in waitForSnapshot that polls broker.Snapshot(...) with time.Sleep should be replaced with the shared eventual helper from internal/testutil (e.g., testutil.Eventually) to avoid sleep-based polling; implement testutil.Eventually(t, timeout, interval, func() bool { snapshot, err := broker.Snapshot(testutil.Context(t), deliveryID); if err != nil { return false }; return match(snapshot) }) and capture/return the snapshot via an outer variable so waitForSnapshot still returns the found snapshot, or alternatively use a notification-based wait from the broker if available; update references in waitForSnapshot to use testutil.Context(t), deliveryID, match and ensure the helper drives the timeout instead of time.Sleep.internal/daemon/channels_test.go (1)
31-407: Convert this suite tot.Run("Should...")table-driven subtests.The coverage is solid, but the file currently encodes each scenario as a separate top-level test. That diverges from the repo’s default test structure and makes the repeated setup harder to extend consistently.
As per coding guidelines,
**/*_test.go:Use table-driven tests with subtests (t.Run) as defaultandMUST use t.Run("Should...") pattern for ALL test cases.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/channels_test.go` around lines 31 - 407, The file contains many top-level tests (e.g., TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence, TestWithChannelSecretResolverAppliesOption, TestBootExtensionsInjectsChannelRuntimeDeps, TestChannelRuntimeStartInstanceTransitionsDisabledInstance, TestChannelRuntimeCreateEnabledInstanceReloadsExtensions, TestChannelRuntimeResolveChannelRuntimeResolvesBoundSecrets, TestChannelRuntimeResolveChannelRuntimeRequiresSecretResolverWhenBindingsExist, TestChannelRuntimeStopInstanceBlocksIngressPreservesRoutes, TestChannelRuntimeRestartPreservesRoutesAndReloadsExtensions) that must be converted into a single table-driven suite using t.Run("Should...") subtests: extract the common setup (homePaths/testConfig/newTestDaemon/openDaemonTestGlobalDB/newChannelRuntime/recording resolvers/etc.) into shared setup code and create a slice of cases each with a Name (starting with "Should...") and a function that executes the original test body; in the parent Test function loop over cases and call t.Run(case.Name, func(t *testing.T){ t.Parallel(); case.Run(t) }); for tests that previously used independent top-level t.Parallel, move that into each subtest; preserve all unique calls and assertions to functions/methods like composeChannelRuntime, WithChannelSecretResolver, bootExtensions, StartInstance, CreateInstance, ResolveChannelRuntime, PutChannelSecretBinding, GetInstance, StopInstance, ListRoutes, ResolveRoute, RestartInstance and ensure extension mocks (fakeExtensionRuntime), resolvers (recordingChannelSecretResolver), and DB helpers (openDaemonTestGlobalDB, mustCreateDaemonChannelInstance, mustUpsertDaemonChannelRoute) are initialized per-case or reused safely to avoid test interference.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/channels/delivery_projection_test.go`:
- Around line 416-450: Update the three failing tests to assert the specific
validation error instead of only checking err != nil: in
ShouldRejectSnapshotWhenLastAckedExceedsLastSent assert that snapshot.Validate()
returns an error whose message contains the specific phrase/validation
identifier for last-acked > last-sent (use ErrorContains or ErrorAs against the
validation error value used in Validate); in ShouldRejectMissingSnapshot assert
that newResumeRequest(snapshot).Validate() returns the missing-snapshot
validation error (check error message or error type for "missing snapshot"); and
in ShouldRejectMismatchedSnapshotDeliveryID assert that req.Validate() returns
the mismatched-delivery-id validation error (check for the delivery id mismatch
message/type). Reference the calls to snapshot.Validate() and req.Validate() in
the tests and replace the generic nil-checks with ErrorContains/ErrorAs
assertions against the exact validation error text or exported error variable
used in the code.
In `@internal/daemon/channels_test.go`:
- Around line 291-294: The test currently checks error text via
containsErrorText after calling
runtime.ResolveChannelRuntime(instance.ExtensionName); replace this brittle
string match with a robust error assertion: have ResolveChannelRuntime return a
sentinel or typed error (e.g., runtime.ErrMissingSecretResolver or a typed error
value) and in the test use errors.Is (or require.ErrorIs/AssertErrorIs) to
assert that returned err matches that sentinel; if the contract must remain
message-based, replace containsErrorText with the test helper ErrorContains
assertion instead of directly matching err.Error(). Ensure references to
ResolveChannelRuntime and containsErrorText are updated accordingly and
add/export the sentinel error from the runtime package if it does not yet exist.
In `@internal/daemon/channels.go`:
- Around line 91-100: The code currently commits the new channel via
r.Service.CreateInstance and then returns a hard error if reloadExtensions
fails, which misleads callers; update the CreateInstance flow to avoid a plain
failure after the durable mutation by either attempting a rollback (call
r.Service.DeleteInstance(ctx, created.ID) or an explicit rollback method when
reloadExtensions returns an error, and if rollback fails include both errors in
the returned wrapped error) or by returning a partial-success result (return the
created instance plus a wrapped error describing the failed reload) instead of
nil; apply the same change to the similar block around reloadExtensions at lines
referenced (the other instance handling block) and use the symbols
Service.CreateInstance, reloadExtensions, and Service.DeleteInstance (or the
service's rollback API) to locate and implement the fix.
- Around line 165-173: The code currently calls transitionInstance(...,
channelspkg.ChannelStatusStarting, ...) before resolveBoundSecrets, which can
persist the instance as Starting even if resolveBoundSecrets fails; change the
call order so resolveBoundSecrets(ctx, instance.ID) is executed and succeeds
before invoking transitionInstance to persist the Starting state (or modify
transitionInstance usage to delay persistence until after bound secrets are
resolved), ensuring that transitionInstance/launching is only created after
resolveBoundSecrets returns successfully and preserving consistency between
launching/launching.ID and resolved secrets.
In `@internal/daemon/daemon_test.go`:
- Around line 3117-3119: The helper currently overwrites the marker file via
os.WriteFile(h.marker, []byte("shutdown"), 0o600) which truncates earlier JSONL
records; change it to open the file with os.OpenFile(h.marker,
os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) and write the shutdown line (include
a newline if other records are newline-delimited), then close the file and
return any write/close error. Update the block referencing h.marker so it
appends instead of replacing the file.
In `@internal/extension/host_api.go`:
- Around line 725-745: The current code finds turnID immediately from a single
h.sessions.Events call and errors if not found; instead, fold turnID discovery
into the existing post-submit polling loop so we wait for the first post-submit
user-message to appear before failing. Replace the one-off Events lookup and the
immediate error return with logic inside the polling loop that repeatedly calls
h.sessions.Events(ctx, sessionID, store.EventQuery{AfterSequence: lastSequence})
and scans returned events for
strings.TrimSpace(event.Type)==acp.EventTypeUserMessage and a non-empty
strings.TrimSpace(event.TurnID); set turnID when found and only return
hostAPIPromptSubmission{} with errors.New("extension: prompt turn id not found
after prompt submission") after the polling timeout/limit elapses. Ensure you
still break out early when turnID is discovered and reuse lastSequence correctly
so you don’t miss events.
- Around line 718-723: The goroutine spawned for draining events is
fire-and-forget and lacks cancellation/ownership; update the call site that
invokes h.sessions.Prompt (where promptCtx, eventsCh are created) to pass a
derived cancellable context (e.g., ctxWithOwner) and a wait-group or tracker,
then change drainAgentEvents to accept (ctx context.Context, wg *sync.WaitGroup,
eventsCh <-chan AgentEvent) and use defer wg.Done() plus a select loop that
listens on ctx.Done() and the eventsCh to exit gracefully; ensure the caller
increments the wg before starting the goroutine and cancels the derived context
when the session/owner shuts down so the drain goroutine has an explicit
shutdown path and is tracked.
---
Nitpick comments:
In `@internal/api/udsapi/udsapi_integration_test.go`:
- Around line 319-367: StartInstance and RestartInstance duplicate the same
two-step state transitions via UpdateInstanceState; extract a small helper
(e.g., performStartTransition or setInstanceStartingThenReady) that accepts (ctx
context.Context, id string) and runs the two UpdateInstanceState calls (Status:
ChannelStatusStarting then ChannelStatusReady, Enabled: true), returns
(*channelspkg.ChannelInstance, error), and use that helper from both
StartInstance and RestartInstance so the flow and error messages are
consolidated and consistent.
In `@internal/channels/delivery_projection_test.go`:
- Around line 675-681: The busy-wait loop in waitForSnapshot that polls
broker.Snapshot(...) with time.Sleep should be replaced with the shared eventual
helper from internal/testutil (e.g., testutil.Eventually) to avoid sleep-based
polling; implement testutil.Eventually(t, timeout, interval, func() bool {
snapshot, err := broker.Snapshot(testutil.Context(t), deliveryID); if err != nil
{ return false }; return match(snapshot) }) and capture/return the snapshot via
an outer variable so waitForSnapshot still returns the found snapshot, or
alternatively use a notification-based wait from the broker if available; update
references in waitForSnapshot to use testutil.Context(t), deliveryID, match and
ensure the helper drives the timeout instead of time.Sleep.
In `@internal/daemon/channels_test.go`:
- Around line 31-407: The file contains many top-level tests (e.g.,
TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence,
TestWithChannelSecretResolverAppliesOption,
TestBootExtensionsInjectsChannelRuntimeDeps,
TestChannelRuntimeStartInstanceTransitionsDisabledInstance,
TestChannelRuntimeCreateEnabledInstanceReloadsExtensions,
TestChannelRuntimeResolveChannelRuntimeResolvesBoundSecrets,
TestChannelRuntimeResolveChannelRuntimeRequiresSecretResolverWhenBindingsExist,
TestChannelRuntimeStopInstanceBlocksIngressPreservesRoutes,
TestChannelRuntimeRestartPreservesRoutesAndReloadsExtensions) that must be
converted into a single table-driven suite using t.Run("Should...") subtests:
extract the common setup
(homePaths/testConfig/newTestDaemon/openDaemonTestGlobalDB/newChannelRuntime/recording
resolvers/etc.) into shared setup code and create a slice of cases each with a
Name (starting with "Should...") and a function that executes the original test
body; in the parent Test function loop over cases and call t.Run(case.Name,
func(t *testing.T){ t.Parallel(); case.Run(t) }); for tests that previously used
independent top-level t.Parallel, move that into each subtest; preserve all
unique calls and assertions to functions/methods like composeChannelRuntime,
WithChannelSecretResolver, bootExtensions, StartInstance, CreateInstance,
ResolveChannelRuntime, PutChannelSecretBinding, GetInstance, StopInstance,
ListRoutes, ResolveRoute, RestartInstance and ensure extension mocks
(fakeExtensionRuntime), resolvers (recordingChannelSecretResolver), and DB
helpers (openDaemonTestGlobalDB, mustCreateDaemonChannelInstance,
mustUpsertDaemonChannelRoute) are initialized per-case or reused safely to avoid
test interference.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d109ad76-df97-4b0b-a690-7ba8b5f686e7
⛔ Files ignored due to path filters (14)
.agents/skills/cy-idea-factory/SKILL.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/council.mdis excluded by!**/*.md,!.agents/**.compozy/tasks/channels/qa/review_001.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/_meta.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_001.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_002.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_003.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_004.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_005.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_006.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_007.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_008.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_009.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-002/issue_010.mdis excluded by!**/*.md
📒 Files selected for processing (14)
internal/api/udsapi/udsapi_integration_test.gointernal/channels/delivery_broker_test.gointernal/channels/delivery_projection_test.gointernal/channels/registry.gointernal/channels/registry_test.gointernal/daemon/channels.gointernal/daemon/channels_test.gointernal/daemon/daemon_integration_test.gointernal/daemon/daemon_test.gointernal/extension/host_api.gointernal/extension/host_api_channels.gointernal/extension/host_api_test.gointernal/extension/registry.gointernal/extension/registry_test.go
✅ Files skipped from review due to trivial changes (2)
- internal/channels/registry_test.go
- internal/daemon/daemon_integration_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
- internal/channels/delivery_broker_test.go
- internal/channels/registry.go
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
internal/daemon/channels_test.go (1)
30-586: Reshape this new suite intot.Run("Should...")cases.The coverage is useful, but this file mostly uses one top-level test per scenario instead of the repo's default subtest style. Grouping related cases into table-driven
t.Run("Should...")blocks will reduce setup duplication and align the suite with the project's test conventions.As per coding guidelines,
**/*_test.go:Use table-driven tests with subtests (t.Run) as defaultandMUST use t.Run("Should...") pattern for ALL test cases.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/channels_test.go` around lines 30 - 586, Consolidate the many top-level tests in this file into table-driven subtests using t.Run("Should...") — for example, replace individual tests like TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence, TestWithChannelSecretResolverAppliesOption, TestBootExtensionsInjectsChannelRuntimeDeps, TestChannelRuntimeStartInstanceTransitionsDisabledInstance, TestChannelRuntimeCreateEnabledInstanceReloadsExtensions, TestChannelRuntimeCreateEnabledInstanceRollsBackToDisabledOnReloadFailure, TestChannelRuntimeResolveChannelRuntimeResolvesBoundSecrets, TestChannelRuntimeResolveChannelRuntimeRequiresSecretResolverWhenBindingsExist, TestChannelRuntimeResolveChannelRuntimeDoesNotPersistStartingWhenSecretResolutionFails, TestChannelRuntimeStopInstanceBlocksIngressPreservesRoutes, and TestChannelRuntimeRestartPreservesRoutesAndReloadsExtensions into a single TestChannelRuntimeSuite (or similar) that iterates over a table of cases and calls t.Run("Should <short description>", func(t *testing.T) { ... }) for each case; preserve existing setup code (homePaths, cfg, newTestDaemon, openDaemonTestGlobalDB, newChannelRuntime, runtime methods like StartInstance/CreateInstance/ResolveChannelRuntime/StopInstance/RestartInstance, and helpers mustCreateDaemonChannelInstance/mustUpsertDaemonChannelRoute), move per-case differences into the table (initial instance request, resolver/extension behavior, expected outcomes), run each subtest in parallel where appropriate, and keep assertions unchanged so behavior is identical but organized as subtests.internal/daemon/daemon_test.go (2)
2864-2875: Preserve explicit empty capability/action/security lists in fixtures.Using
len(...) == 0here makesniland[]string{}behave the same, so callers cannot build a manifest that intentionally declares noprovides,requires, orsecurity.capabilities. That weakens negative handshake/authorization coverage because the helper silently injects"memory.backend","sessions/list", and"session.read".Possible fix
capabilities := append([]string(nil), opts.capabilities...) - if len(capabilities) == 0 { + if opts.capabilities == nil { capabilities = []string{"memory.backend"} } actions := append([]string(nil), opts.actions...) - if len(actions) == 0 { + if opts.actions == nil { actions = []string{"sessions/list"} } security := append([]string(nil), opts.security...) - if len(security) == 0 { + if opts.security == nil { security = []string{"session.read"} }
3195-3225: Wrap marker-recording failures with operation context.These helpers currently bubble raw marshal / append errors. When the helper process exits, that leaves stderr without enough context to tell whether initialize encoding, delivery encoding, or file append failed.
Possible fix
payload, err := json.Marshal(daemonInitializeMarker{ Request: request, Response: response, }) if err != nil { - return err + return fmt.Errorf("marshal initialize marker: %w", err) } - return appendMarkerLine(h.marker, string(payload)) + if err := appendMarkerLine(h.marker, string(payload)); err != nil { + return fmt.Errorf("append initialize marker: %w", err) + } + return nil } func (h *daemonExtensionHelperServer) recordDelivery(request channelspkg.DeliveryRequest) error { @@ payload, err := json.Marshal(daemonDeliveryMarker{ PID: os.Getpid(), Request: request, }) if err != nil { - return err + return fmt.Errorf("marshal delivery marker: %w", err) } - return appendMarkerLine(h.marker, string(payload)) + if err := appendMarkerLine(h.marker, string(payload)); err != nil { + return fmt.Errorf("append delivery marker: %w", err) + } + return nil }As per coding guidelines, "Use explicit error returns with wrapped context:
fmt.Errorf("context: %w", err)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/daemon/daemon_test.go` around lines 3195 - 3225, Both recordInitialize and recordDelivery currently return raw errors from json.Marshal and appendMarkerLine; update them to wrap errors with contextual messages using fmt.Errorf so stderr shows whether the failure occurred during marshaling or file append. Specifically, in daemonExtensionHelperServer.recordInitialize wrap the error returned from json.Marshal with a message like "recordInitialize: marshal failed" and wrap the error from appendMarkerLine with "recordInitialize: appendMarkerLine failed"; do the analogous wrapping in daemonExtensionHelperServer.recordDelivery for the json.Marshal and appendMarkerLine errors, referencing those function names (recordInitialize, recordDelivery) and the called helper appendMarkerLine to locate the fixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/daemon/channels_test.go`:
- Around line 209-225: The test uses a sentinel reload error but only checks
that CreateInstance returned a non-nil error; change the assertion to verify the
error is the sentinel reloadErr using errors.Is (or
testing/require/AssertErrorIs equivalent) so the failure proves Reload was
invoked: keep the fakeExtensionRuntime{reloadErr: errors.New("reload boom")}
sentinel, call runtime.CreateInstance as before, then replace t.Fatal on nil
with an assertion like if !errors.Is(err, extensions.reloadErr) { t.Fatalf("...
expected reloadErr, got %v", err) }; apply the same specific-error assertion fix
to the other test at the referenced location (lines ~567-572) that currently
only checks non-nil.
In `@internal/daemon/channels.go`:
- Around line 100-115: This code performs lifecycle writes after reading
instance state (e.g., in the create flow around reloadExtensions and
persistCompensatingInstance) without guarding against stale-snapshot races;
either serialize per-instance lifecycle operations or make writes conditional on
a last-seen version. Fix by adding one of two options: (A) serialize operations
per instance ID by introducing an instance-level lock (e.g., a
map[string]*sync.Mutex or a singleflight/keyed mutex held around the read ->
reloadExtensions -> persist path using the channel ID) and use it in the
create/reload/rollback flows, or (B) convert the forward write and the
compensating rollback (persistCompensatingInstance and the normal persist/save
call used after reloadExtensions) into optimistic-concurrency updates that
include the prior UpdatedAt/version in the WHERE clause and return a conflict
error if no row matched; then handle that conflict by failing the operation (or
retrying) instead of blindly overwriting. Apply the same guard to the other
similar spots called out (the other lifecycle write paths around the
create/rollback and the block that currently calls persistCompensatingInstance)
so all lifecycle transitions for a given channel ID are either serialized or
conditional on the last-seen version.
In `@internal/daemon/daemon_test.go`:
- Around line 3120-3148: The test helper currently unmarshals a
channelspkg.DeliveryRequest into params and proceeds to record and ACK it
without validating; call params.Validate() (the existing method on
DeliveryRequest from internal/channels/delivery_types.go) immediately after
json.Unmarshal and before h.recordDelivery/sendResult/sendDelayedDeliveryResult,
return the validation error (or fail the request) if it returns non-nil so
malformed deliveries are rejected rather than ACKed; keep the subsequent logic
(ack construction, scenario branches, and calls to h.recordDelivery,
h.sendDelayedDeliveryResult, h.sendResult) unchanged except gated by the
successful Validate() call.
---
Nitpick comments:
In `@internal/daemon/channels_test.go`:
- Around line 30-586: Consolidate the many top-level tests in this file into
table-driven subtests using t.Run("Should...") — for example, replace individual
tests like
TestComposeChannelRuntimeBuildsOnlyWhenRegistrySupportsChannelPersistence,
TestWithChannelSecretResolverAppliesOption,
TestBootExtensionsInjectsChannelRuntimeDeps,
TestChannelRuntimeStartInstanceTransitionsDisabledInstance,
TestChannelRuntimeCreateEnabledInstanceReloadsExtensions,
TestChannelRuntimeCreateEnabledInstanceRollsBackToDisabledOnReloadFailure,
TestChannelRuntimeResolveChannelRuntimeResolvesBoundSecrets,
TestChannelRuntimeResolveChannelRuntimeRequiresSecretResolverWhenBindingsExist,
TestChannelRuntimeResolveChannelRuntimeDoesNotPersistStartingWhenSecretResolutionFails,
TestChannelRuntimeStopInstanceBlocksIngressPreservesRoutes, and
TestChannelRuntimeRestartPreservesRoutesAndReloadsExtensions into a single
TestChannelRuntimeSuite (or similar) that iterates over a table of cases and
calls t.Run("Should <short description>", func(t *testing.T) { ... }) for each
case; preserve existing setup code (homePaths, cfg, newTestDaemon,
openDaemonTestGlobalDB, newChannelRuntime, runtime methods like
StartInstance/CreateInstance/ResolveChannelRuntime/StopInstance/RestartInstance,
and helpers mustCreateDaemonChannelInstance/mustUpsertDaemonChannelRoute), move
per-case differences into the table (initial instance request,
resolver/extension behavior, expected outcomes), run each subtest in parallel
where appropriate, and keep assertions unchanged so behavior is identical but
organized as subtests.
In `@internal/daemon/daemon_test.go`:
- Around line 3195-3225: Both recordInitialize and recordDelivery currently
return raw errors from json.Marshal and appendMarkerLine; update them to wrap
errors with contextual messages using fmt.Errorf so stderr shows whether the
failure occurred during marshaling or file append. Specifically, in
daemonExtensionHelperServer.recordInitialize wrap the error returned from
json.Marshal with a message like "recordInitialize: marshal failed" and wrap the
error from appendMarkerLine with "recordInitialize: appendMarkerLine failed"; do
the analogous wrapping in daemonExtensionHelperServer.recordDelivery for the
json.Marshal and appendMarkerLine errors, referencing those function names
(recordInitialize, recordDelivery) and the called helper appendMarkerLine to
locate the fixes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c6e18595-6fa3-4b7e-bdfe-4a4b856151d2
⛔ Files ignored due to path filters (11)
.compozy/tasks/channels/reviews-003/_meta.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_001.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_002.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_003.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_004.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_005.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_006.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_007.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_008.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_009.mdis excluded by!**/*.md.compozy/tasks/channels/reviews-003/issue_010.mdis excluded by!**/*.md
📒 Files selected for processing (5)
internal/channels/delivery_projection_test.gointernal/daemon/channels.gointernal/daemon/channels_test.gointernal/daemon/daemon_integration_test.gointernal/daemon/daemon_test.go
✅ Files skipped from review due to trivial changes (1)
- internal/channels/delivery_projection_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/daemon/daemon_integration_test.go
feat: implement channel registry routing feat: add channel delivery target resolver feat: negotiate channel adapter delivery runtime feat: implement channel host api ingest feat: add channel delivery broker feat: compose daemon channel runtime feat: expose channel management api feat: add channel cli commands feat: add channel observability health reporting feat: add telegram reference adapter harness docs: update fix: address channels review batch 1 fix: resolve channels review batch 001 docs: update prd fix: resolve channels review batch 002 fix: review round fix: resolve channels review batch 003 docs: update prd fix: resolve channels review round 004 docs: update prd
…flake - Fix TestManagerIntegrationResumeClassifiesCrashAndActivates: crash classification persisted in meta but not reflected in Session.Info() — per-session finalization channel in manager.go and proper wait in manager_lifecycle.go - Fix secret-guard extension reference test flake: correlate markers by PID to avoid reading stale JSON from shutdown instances - Channel/automation improvements: host_api_channels, extension manager, daemon channels, validation, tests - Add composyz skill definitions
## Release v0.0.1 This PR prepares the release of version v0.0.1. ### Changelog ## 0.0.1 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.1 This PR prepares the release of version v0.0.1. ### Changelog ## 0.0.1 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process - Fix release sync - Decouple release dry-run npm auth - Persist web assets git auth ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated web assets dependency to a newer version for improved stability and performance. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/compozy/agh/pull/211?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-27 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout - Fix release dry-run token contract ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process - Fix release sync - Decouple release dry-run npm auth - Persist web assets git auth - Require npm auth before release merge ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated dependencies to latest versions. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/compozy/agh/pull/214?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Summary by CodeRabbit
New Features
Delivery
CLI
Observability
Tests