7 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 679ae61163 feat(transport): address the engine and the SDK, so both can use a socket
The engine took a PORT and the SDK an Options.HostPort, so neither end
could use a unix socket for traffic that never leaves the host. The
field name was not the only obstacle: luxfi/zap bound net.Listen("tcp",
":port") and reached a peer with net.DialTimeout("tcp", addr), so a path
was refused at the dialer with "missing port in address" and could not
be bound at all. That is fixed upstream (luxfi/zap Address + Network);
this pins the commit until it is tagged.

EmbedConfig.Address, Embedded.Address, ServeGated(addr) and
client.Options.Address each take ONE address, because a port is a detail
of one transport rather than a thing every listener has. A path binds or
dials a unix socket; anything else is host:port.

TestDialRequiresHostPort became TestDialRequiresAddress: Dial needs an
address, and asserts a socket path is refused for being absent rather
than for being portless. TestE2E_UnixSocket runs a real workflow over a
socket end to end, and TestE2E_UnixSocketRebind proves the socket is
unlinked on stop so a restart binds cleanly.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 16:27:19 -07:00
zeekay 903a94a84e feat(sdk/workflow): add GetVersion / SideEffect / MutableSideEffect / metrics / search-attributes
System workers (scheduler, deletenamespace, workerdeployment) need:
- GetVersion(ctx, changeID, min, max) — replay-determinism marker
- SideEffect(ctx, fn) — once-per-ordinal recorded computation
- MutableSideEffect(ctx, changeID, fn, eq) — replaceable recorded value
- GetMetricsHandler(ctx).WithTags(...) — workflow-scoped metrics
- UpsertSearchAttributes(ctx, attrs) — visibility upsert

CoroutineEnv extended with the five seam methods. workerEnv carries
per-run maps (versions, sideEffects, mutables) keyed by changeID /
call ordinal; replays on the same input re-record the same values at
the same call sites — Phase-1 contract.

StubEnv mirrors the contract for tests, plus SetMetricsHandler and
UpsertedSearchAttributes() for assertions.

Property tests cover record-once, clamping, independent changeIDs,
side-effect ordinal stability, mutable equality, and metrics routing.

Refused without proven concrete need: HasLastCompletionResult,
GetLastCompletionResult, UpsertTypedSearchAttributes, GetTypedSearch-
Attributes. Nothing kept calls them.
2026-04-25 12:53:18 -07:00
zeekay ad7af646a6 fix(workflow): seal Channel / ReceiveChannel interfaces (R2.3)
Both interfaces now carry an unexported sentinel method sealed() so
external packages cannot implement them. A fork that shadows the
workflow package will fail to satisfy the interface at compile
time, preventing silent divergence in selector / coroutine-scheduler
semantics.

The worker package's signalChan wrapper had to go — it lived
outside the workflow package and so cannot satisfy sealed().
Replaced with workflow.NewSignalChannel(name, buffer), which
returns the canonical sealed chanImpl directly. The name is
carried on the channel itself, so the wrapper was cosmetic.

Added pkg/sdk/workflow/channel_sealed_test.go asserting the
canonical impl satisfies the sealed interfaces.
2026-04-23 20:33:00 -07:00
zeekay 072ecefbfe feat(sdk): close caller-symbol gap for base/commerce migration
Adds every symbol hanzoai/base/plugins/tasks and hanzoai/commerce
call today that was missing from pkg/sdk/*: SignalWithStartWorkflow,
GetWorkflow, QueryWorkflow, CheckHealth, WorkflowRunGetOptions +
WorkflowRun.GetWithOptions, ExecuteChildWorkflow + ChildWorkflowFuture,
LocalActivityOptions + WithLocalActivityOptions + ExecuteLocalActivity
(Phase-1 remote-path), worker.InterruptCh(), worker.Options rate-limit
fields wired via golang.org/x/time/rate.

New opcodes 0x0066 signalWithStartWorkflow / 0x0067 queryWorkflow
land here; frontend-side handlers were wired in PR-A via the
translate.go dispatch table.

Closes red-review §2 gap table. Dry-run base migration compiles.
2026-04-23 12:53:15 -07:00
zeekay a9d72f6b95 feat(sdk/workflow): real wall-clock timers + edge-triggered Select
NewTimer used to collapse any duration to zero — production workflows
with schedule-level timers (24h dunning, weekly roll-up) fired on
the next dispatch loop tick. Selector busy-spun at 1 kHz.

Real timer.Timer behind each workflow.NewTimer; Selector.Await
blocks on fan-in over Future.readyCh channels. StubEnv becomes
test-only; the production dispatch env in pkg/sdk/worker uses the
real impl.

Closes red-review §5.2 and §5.9.
2026-04-23 12:29:21 -07:00
zeekay 7006ef3638 feat(sdk/worker): real wire-backed workflow env + FailureCommand
Replaces StubEnv use in dispatch.go with workerEnv, a real
CoroutineEnv implementation. Activity dispatch now goes over the
wire via ScheduleActivity (opcode 0x006B) + long-poll
WaitActivityResult (0x006C), not a pre-populated in-memory map.
NewTimer uses time.NewTimer in a per-timer goroutine; Select uses
a fan-in wake channel with a 20ms watcher poll (no per-iteration
allocation). Root cancel scope propagates to timers, activity polls,
and child scopes on return.

Workflow return errors are no longer swallowed — non-nil fn returns
get encoded via temporal.Encode into a single FailureCommand entry
in the commands envelope so the frontend can fail the execution
per its retry policy.

Phase-1 note documented in struct comment: every dispatch re-runs
the workflow from the top with the same input — activities must be
idempotent. Phase 2 replaces this with event-sourced replay
without changing the public surface.

Adds WorkerTransport.ScheduleActivity / WaitActivityResult /
StartChildWorkflow with v1 JSON-in-envelope bodies. Schema
schema/tasks.zap gains the 0x006B/0x006C struct definitions.
2026-04-23 12:01:27 -07:00
zeekay e0171b4015 feat(sdk/workflow): native Context/Future/Selector/Channel surface
Ports the workflow-code-facing API that base/plugins/tasks and
commerce/infra rely on today, with zero go.temporal.io or grpc
imports. Wire shape mirrors Temporal exactly so call-site migration
is a pure import-path swap.

Package layout (pkg/sdk/workflow):
  env.go       — CoroutineEnv interface, the one seam the worker
                 (pkg/sdk/worker, separately) must satisfy; Info,
                 SelectCase, CancelFunc. NewContextFromEnv is the
                 single entry point user ctx flows through.
  context.go   — Context interface (Deadline/Done/Err/Value), ctx
                 chain with WithCancel / WithValue / WithActivityOptions;
                 GetLogger, Now, Sleep, GetInfo.
  future.go    — Future + settleable impl. NewFuture / Settle split
                 so both the worker and the in-memory stub drive
                 completion through the same concrete. EncodePayload
                 is the Phase-1 JSON codec; Phase 2 swaps for ZAP.
  channel.go   — ReceiveChannel / Channel; unbuffered + buffered
                 chanImpl backing both user channels and signal
                 queues. Send/Receive/ReceiveAsync/Close.
  selector.go  — Selector with AddFuture / AddReceive / AddDefault;
                 Select delegates to env.Select and dispatches to
                 the matched callback.
  timer.go     — NewTimer schedules a durable timer Future.
  signal.go    — GetSignalChannel returns shared-by-name signal queues.
  activity.go  — ActivityOptions mirror of Temporal shape;
                 WithActivityOptions attaches per-call opts;
                 ExecuteActivity reads opts from ctx and dispatches.
  reflect.go   — Sole reflect use site (activity name resolution).
  stub.go      — StubEnv: synchronous, in-memory CoroutineEnv for
                 unit-testing workflow code without a worker.
                 OnActivity(...).Return(...), SignalAsync, AdvanceClock,
                 Cancel. Used throughout workflow_test.go.
  workflow_test.go — 27 tests exercising Context, cancel scopes,
                 Now/Sleep, timers, activities (success/error/nil),
                 signal channels, user channels, Selector (future/
                 receive/default), and an end-to-end AgentTaskWorkflow-
                 shaped test that mirrors base/plugins/tasks.

Phase 1 determinism caveats (documented in env.go):
  * No event-sourced replay yet — crashes restart from the top with
    the original input. Same idempotency contract as Temporal.
  * Payloads encoded as JSON, not the canonical ZAP codec.
  * time.Now / rand / goroutines inside workflow code remain banned;
    CI linter comes in a follow-up.

go vet clean. 27 tests pass including -race.
go list -deps reports zero go.temporal.io or google.golang.org/grpc.
2026-04-23 10:34:17 -07:00