Skip to content

Refactor Actor to interface and clean up dependencies#1

Merged
inf3cti0n95 merged 5 commits into
mainfrom
feature/mdk-actor-interface
Jun 6, 2026
Merged

Refactor Actor to interface and clean up dependencies#1
inf3cti0n95 merged 5 commits into
mainfrom
feature/mdk-actor-interface

Conversation

@inf3cti0n95

@inf3cti0n95 inf3cti0n95 commented Jun 6, 2026

Copy link
Copy Markdown
Member

Refactors mdk.Actor to an interface, decouples db model in auth, introduces lightweight local/mem CloudProvider, simplifies HTMLUIProvider, cleans dependencies, and updates tests.

Summary by CodeRabbit

  • Documentation

    • Added project changelog and a security/vulnerability reporting policy.
  • New Features

    • Introduced JSON metadata support for flexible data storage.
  • Refactor

    • Switched identity model to an interface-based representation.
    • Simplified workflow steps to a declarative form.
    • Aligned the testing harness with external module types and removed the dynamic CLI command registry.
    • Module UI provider now returns consolidated HTML with explicit error handling.
  • Tests

    • Added metadata serialization and scan tests.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Need the big picture first? Review this PR in Change Stack to see what changed before going file by file.

Review Change Stack

📝 Walkthrough

Walkthrough

Refactors identity to use an Actor interface, updates workflow/module contracts, migrates the test harness to external mdk types, adds a Metadata DB type with tests, and adds CHANGELOG and SECURITY policy.

Changes

MDK Type System and Testing Infrastructure

Layer / File(s) Summary
Actor Identity Abstraction
identity.go
Actor becomes an interface with accessor methods; BaseActor implements it. Context helpers and TokenValidator updated to use the interface.
Workflow and Module Contract Updates
workflow.go, module.go, registry.go
Step struct becomes declarative (removes Handler/Compensate). HTMLUIProvider.RenderHTML now returns (title, html, error). CLI command registry removed.
Metadata Persistence Type
models.go, models_test.go
Adds Metadata type with Value() and Scan() for DB integration; tests validate JSON, nil, and invalid-scan behaviors.
Testing Harness Migration to External MDK Package
mdktest/testing.go
Migrates test mocks and runtime to use github.com/GoHyperrr/mdk types (Event, EventHandler, Workflow, StepHandler, StepStatus, LineageData, Projector). Adds ProjectorModule, enables real handler unsubscription and handler lookup via Step.Uses.
Project Documentation and Security Policy
CHANGELOG.md, SECURITY.md, .coderabbit.yaml
Adds initial v0.1.0 changelog, SECURITY.md describing supported versions and vulnerability reporting, and updates review config schema and keys.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit hops through types anew,
Actors wear interfaces bright and true,
Steps stand bare, no handlers baked in,
Tests now speak mdk as they begin,
Changelog and security — a new release grin. 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main architectural change: refactoring Actor to an interface and removing unnecessary dependencies, which aligns with the primary modifications across identity.go, registry.go, and related cleanup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mdk-actor-interface

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
mdktest/testing.go (2)

178-185: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Goroutine uses detached context, losing cancellation signal.

Execute launches a goroutine with context.Background(), ignoring the caller's context. If the caller expects cancellation to propagate (e.g., test timeout), this won't work. Consider passing the original context or a derived context with the goroutine.

Proposed fix
 func (twe *TestWorkflowEngine) Execute(ctx context.Context, workflowID string, input map[string]any) (string, error) {
 	val := atomic.AddInt64(&runIDCounter, 1)
 	runID := fmt.Sprintf("wf_run_%d_%d", time.Now().UnixNano(), val)
 	go func() {
-		_, _ = twe.ExecuteSync(context.Background(), runID, workflowID, input)
+		_, _ = twe.ExecuteSync(ctx, runID, workflowID, input)
 	}()
 	return runID, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mdktest/testing.go` around lines 178 - 185, The goroutine in
TestWorkflowEngine.Execute uses context.Background(), which prevents
cancellation from the caller; change the goroutine to pass the caller's ctx (or
a derived context like with context.WithCancel/WithTimeout if you need
isolation) into ExecuteSync so cancellation/timeouts propagate. Update the
anonymous goroutine invocation to call twe.ExecuteSync(ctx, runID, workflowID,
input) (or a derivedCtx) instead of context.Background(), keeping runID
generation and return behavior unchanged.

129-140: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Function pointer comparison has edge cases with closures.

The reflect.ValueOf(h).Pointer() comparison works correctly for function values that are the same variable instance. However, be aware that two distinct closures with identical code but different captured variables will share the same code pointer, potentially causing unintended matches. This is acceptable for test utilities where the typical usage pattern is unsub, _ := Subscribe(..., handler); unsub().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mdktest/testing.go` around lines 129 - 140, The current removal loop in
teb.handlers uses reflect.ValueOf(h).Pointer() to match functions which compares
code pointers and can misidentify distinct closures; replace that pointer-based
check with a direct function value equality (use if h == handler) inside the
loop so closures with different captured state are compared correctly; update
the code where reflect.ValueOf(h).Pointer() ==
reflect.ValueOf(handler).Pointer() is used (inside the unsubscribe closure that
locks teb.mu and iterates handlers) to use h == handler instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 7-8: Add a single blank line after the markdown heading "###
Added" so the list item "- Initial modular commerce kernel design and MDK
decoupled integrations." is separated from the heading; update the block
containing the "### Added" heading to insert one empty line immediately
following that heading.

In `@SECURITY.md`:
- Line 5: Update the policy sentence "We currently support and patch security
vulnerabilities on the latest major release branch." to explicitly cover pre-1.0
semantics: replace or append it with a clear statement such as "For pre-1.0
releases we support the latest release or the latest patch series (e.g., 0.1.x),
and for 1.0+ we support the latest major release branch," so readers understand
whether you mean the latest release, latest patch series (0.x), or latest major
branch during the 0.x phase; edit the SECURITY.md line accordingly to include
that clarified scope.

---

Outside diff comments:
In `@mdktest/testing.go`:
- Around line 178-185: The goroutine in TestWorkflowEngine.Execute uses
context.Background(), which prevents cancellation from the caller; change the
goroutine to pass the caller's ctx (or a derived context like with
context.WithCancel/WithTimeout if you need isolation) into ExecuteSync so
cancellation/timeouts propagate. Update the anonymous goroutine invocation to
call twe.ExecuteSync(ctx, runID, workflowID, input) (or a derivedCtx) instead of
context.Background(), keeping runID generation and return behavior unchanged.
- Around line 129-140: The current removal loop in teb.handlers uses
reflect.ValueOf(h).Pointer() to match functions which compares code pointers and
can misidentify distinct closures; replace that pointer-based check with a
direct function value equality (use if h == handler) inside the loop so closures
with different captured state are compared correctly; update the code where
reflect.ValueOf(h).Pointer() == reflect.ValueOf(handler).Pointer() is used
(inside the unsubscribe closure that locks teb.mu and iterates handlers) to use
h == handler instead.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: da9863fc-78ca-404f-88a5-d6e7bb1de333

📥 Commits

Reviewing files that changed from the base of the PR and between a0e95e5 and 5e5609a.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • SECURITY.md
  • identity.go
  • mdktest/testing.go
  • models.go
  • models_test.go
  • module.go
  • registry.go
  • workflow.go
💤 Files with no reviewable changes (1)
  • registry.go

Comment thread CHANGELOG.md
Comment thread SECURITY.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
mdktest/testing.go (3)

114-120: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return subscriber failures from Publish.

Publish drops every handler error and still returns nil. That makes this test double report success for flows that should fail, so tests won't catch error-handling regressions around mdk.EventBus.Publish.

Suggested fix
 func (teb *TestEventBus) Publish(ctx context.Context, e mdk.Event) error {
 	teb.mu.Lock()
 	if e.OccurredAt.IsZero() {
 		e.OccurredAt = time.Now()
 	}
 	teb.Published = append(teb.Published, e)
 	teb.mu.Unlock()
@@
+	var firstErr error
 	for _, h := range handlers {
-		_ = h(ctx, e)
+		if err := h(ctx, e); err != nil && firstErr == nil {
+			firstErr = err
+		}
 	}
 	for _, h := range wildcardHandlers {
-		_ = h(ctx, e)
+		if err := h(ctx, e); err != nil && firstErr == nil {
+			firstErr = err
+		}
 	}
-	return nil
+	return firstErr
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mdktest/testing.go` around lines 114 - 120, Publish currently ignores errors
from subscriber calls in the loops over handlers and wildcardHandlers and always
returns nil; update the Publish implementation in testing.go to collect
subscriber errors (from handlers and wildcardHandlers) and return an aggregated
or first error instead of nil. Specifically, in the function containing the
handlers/wildcardHandlers loops, capture each h(ctx, e) error, append to a slice
or use an error-aggregator, and at the end return a combined error (or the first
non-nil error) so caller/tests receive failure information from subscriber
handlers.

178-184: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when Execute cannot start a run.

Line 182 discards ExecuteSync's error inside the goroutine, so Execute returns (runID, nil) even when workflowID is unknown. Because runs[runID] is only initialized inside ExecuteSync, an immediate Status call can also see "" instead of a valid mdk.StepStatus. Check workflowID before spawning the goroutine and seed the run as mdk.StepPending.

Suggested fix
 func (twe *TestWorkflowEngine) Execute(ctx context.Context, workflowID string, input map[string]any) (string, error) {
+	twe.mu.RLock()
+	_, ok := twe.workflows[workflowID]
+	twe.mu.RUnlock()
+	if !ok {
+		return "", fmt.Errorf("workflow not found: %s", workflowID)
+	}
+
 	val := atomic.AddInt64(&runIDCounter, 1)
 	runID := fmt.Sprintf("wf_run_%d_%d", time.Now().UnixNano(), val)
+
+	twe.mu.Lock()
+	twe.runs[runID] = mdk.StepPending
+	twe.mu.Unlock()
+
 	go func() {
 		_, _ = twe.ExecuteSync(ctx, runID, workflowID, input)
 	}()
 	return runID, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mdktest/testing.go` around lines 178 - 184, The Execute method currently
spawns a goroutine and discards ExecuteSync's error, causing Execute to always
return (runID, nil) and leaving runs[runID] uninitialized; fix this by
validating the workflowID and seeding the run before launching the goroutine:
call the same validation/initialization logic that ExecuteSync uses (or call
ExecuteSync synchronously up to the point it seeds runs) to ensure runs[runID] =
mdk.StepPending and return an error if validation fails, then start the
background goroutine to continue execution; update TestWorkflowEngine.Execute to
perform these checks and seeding (referencing ExecuteSync, runs map, runID
generation, and mdk.StepPending).

129-139: ⚠️ Potential issue | 🟠 Major

Replace reflect.Value.Pointer() with an explicit subscription ID for unsubscribe.

reflect.Value.Pointer() on Func values returns only the underlying code pointer and is not guaranteed to uniquely identify a specific closure/method value instance (the docs only guarantee it’s zero for a nil func). Using it can cause unsubscribing one subscription to remove a different handler that happens to share the same underlying pointer. Store a generated subscription ID alongside each handler and remove by ID instead.
File mdktest/testing.go lines 129-139.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mdktest/testing.go` around lines 129 - 139, The unsubscribe closure currently
matches handlers by reflect.ValueOf(h).Pointer(), which is unsafe; instead,
change the handlers storage (teb.handlers) to hold a small struct with a
generated subscription ID and the handler function (e.g., {id string; fn
HandlerType}) when registering, generate a unique id (UUID or incremental) and
return an unsubscribe function that captures that id, and then implement removal
by comparing ids (remove the entry whose id equals the captured id) in the code
path that currently iterates handlers (the block referencing teb.handlers[key],
handler, and the returned func). Ensure locking (teb.mu.Lock/Unlock) stays the
same and that the registration API returns the unsubscribe func and nil error as
before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@mdktest/testing.go`:
- Around line 114-120: Publish currently ignores errors from subscriber calls in
the loops over handlers and wildcardHandlers and always returns nil; update the
Publish implementation in testing.go to collect subscriber errors (from handlers
and wildcardHandlers) and return an aggregated or first error instead of nil.
Specifically, in the function containing the handlers/wildcardHandlers loops,
capture each h(ctx, e) error, append to a slice or use an error-aggregator, and
at the end return a combined error (or the first non-nil error) so caller/tests
receive failure information from subscriber handlers.
- Around line 178-184: The Execute method currently spawns a goroutine and
discards ExecuteSync's error, causing Execute to always return (runID, nil) and
leaving runs[runID] uninitialized; fix this by validating the workflowID and
seeding the run before launching the goroutine: call the same
validation/initialization logic that ExecuteSync uses (or call ExecuteSync
synchronously up to the point it seeds runs) to ensure runs[runID] =
mdk.StepPending and return an error if validation fails, then start the
background goroutine to continue execution; update TestWorkflowEngine.Execute to
perform these checks and seeding (referencing ExecuteSync, runs map, runID
generation, and mdk.StepPending).
- Around line 129-139: The unsubscribe closure currently matches handlers by
reflect.ValueOf(h).Pointer(), which is unsafe; instead, change the handlers
storage (teb.handlers) to hold a small struct with a generated subscription ID
and the handler function (e.g., {id string; fn HandlerType}) when registering,
generate a unique id (UUID or incremental) and return an unsubscribe function
that captures that id, and then implement removal by comparing ids (remove the
entry whose id equals the captured id) in the code path that currently iterates
handlers (the block referencing teb.handlers[key], handler, and the returned
func). Ensure locking (teb.mu.Lock/Unlock) stays the same and that the
registration API returns the unsubscribe func and nil error as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5fa66935-0888-4f25-89e2-14ecd7fd1055

📥 Commits

Reviewing files that changed from the base of the PR and between 4a78180 and f09b2ec.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • SECURITY.md
  • mdktest/testing.go

@inf3cti0n95 inf3cti0n95 merged commit 11ac3d4 into main Jun 6, 2026
2 checks passed
@inf3cti0n95 inf3cti0n95 deleted the feature/mdk-actor-interface branch June 6, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant