Kronos is being pivoted from a generic job scheduler to "the durable workflow engine with a time-travel debugger" — a production-grade system for debugging, replaying, and fixing failed workflows in production.
Goal: Build a launch-worthy v1 that becomes the go-to open-source durable execution debugger, gets a Show HN front-page moment, and demonstrates top-tier systems engineering for portfolio purposes.
- Commit:
fe9caa4(2026-03-15) - Rewrote README with new positioning: "durable workflows you can debug like regular programs"
- Established the three killer features: time-travel debugging, local replay, workflow versioning
- Positioned existing job queue as "execution substrate"
Commits: 2099525 through 3bb6b28 (2026-03-17 through 2026-03-30)
workflows— versioned, immutable definitions (name + fingerprint)workflow_runs— execution instances with pinned workflow_idworkflow_steps— materialized projection of step stateworkflow_events— append-only event log (time-travel source of truth)workflow_blobs— content-addressed storage for large inputs/outputs
Workflow,Step,StepFunc,StepDef— definition primitivesWorkflowRun,StepExecution— execution recordsEvent,EventType— 12 event types covering the full lifecycle- Workflow DAG validation with cycle detection
- Interface-based design for testability
- Postgres implementation with proper transactional semantics
- Event appending, blob management, run/step querying
- Mirrors
internal/store/postgres.gopatterns
- Name → versioned definition lookup
- Handles multiple versions of the same workflow
- Thread-safe with RWMutex
ExecuteStep()— executes a single step in a run- Constructs step input from upstream recorded outputs
- Records events transactionally
- Detects workflow completion and marks run as done
- Handles blob storage for large payloads
- Workflow RPC definitions:
StartWorkflow,GetRun,ListRuns,CancelRun,GetRunEvents,StreamRunHistory - Generated gRPC stubs with proper messaging
- Merged workflow types into
kronos.prototo avoid import complications - Workflow service integrated into
KronosService
- Instantiated
WorkflowStore,WorkflowRegistry,WorkflowEngine - Registered
kronos.workflow_stephandler that delegates to engine - Workflow steps execute as ordinary jobs through existing pipeline (zero changes to scheduler/worker/retries)
- Unit tests for engine logic with mock store
- Linear workflow test, cycle detection test
- Integration test with real Postgres via testcontainers
- End-to-end test: create run, execute steps, verify events and completion
Commit: a6d2882 (2026-04-05)
- Implemented all six workflow RPC methods:
StartWorkflow()— creates run, records RunStarted event, returns run IDGetRun()— retrieves run by ID with proto conversionListRuns()— paginated listing with workflow/status filtersCancelRun()— cancels pending runsGetRunEvents()— returns all events for a runStreamRunHistory()— server-side streaming for replay clients
- Proto conversion helpers (run, status, event)
- Wired into gRPC server in main.go
- Proper error handling with gRPC status codes
User calls StartWorkflow RPC
↓
API creates workflow_runs row, records RunStarted event
↓
For each step with satisfied dependencies:
↓
Insert jobs row (type=kronos.workflow_step, payload={run_id, step_name})
↓
Scheduler claims job → Worker executes
↓
WorkflowEngine.ExecuteStep():
1. Load workflow definition (pinned version)
2. Assemble step input from upstream outputs
3. Invoke user's StepFunc
4. Write events transactionally (StepStarted, StepInput, StepOutput, StepCompleted)
5. Check for newly-ready downstream steps
6. Mark run completed when all steps done
↓
Replay client calls StreamRunHistory to fetch event log
- No new scheduler — Workflow steps use existing
SELECT FOR UPDATE SKIP LOCKEDjob claiming - Versioning baked in — In-flight runs pin
workflow_id(specific content hash); new runs get latest version - Event sourcing — Every state change is an append-only event; deterministic replay from event log
- Blob deduplication — Large inputs/outputs stored by content hash; shared across forks
- Single binary — Everything compiles to one executable; no separate services
- Zero regression — Existing job queue, cron, retries, dead letter, metrics all unchanged
Commit: (2026-04-17)
- Changed
StepFuncfromfunc(input json.RawMessage)tofunc(ctx context.Context, input json.RawMessage) - Unblocks cancellation propagation, timeout enforcement, and OpenTelemetry tracing
- Updated engine.go, engine_test.go, workflow_test.go, all handler registrations
Commit: (2026-04-17)
- Handler:
internal/debugger/handler.go— HTTP handlers for runs list, run detail, events, SSE, fork - Templates:
internal/debugger/templates/— Go templates with[[]]delimiters (avoids Alpine.js conflict)layout.html,runs.html,run_detail.html- Partials:
step_tree.html,timeline.html,step_detail.html
- Static assets:
internal/debugger/static/— CSS (dark mode, custom properties), JS (Alpine.js components, syntax highlighter) - Embed:
internal/debugger/embed.go—go:embedfor single-binary deployment - Routes:
/debugger/,/debugger/runs,/debugger/runs/{id},/debugger/runs/{id}/events,/debugger/runs/{id}/live,/debugger/runs/{id}/fork,/debugger/static/ - Features: Runs list with filters, 3-pane run detail, timeline scrubber, step inspector, JSON syntax highlighting, SSE live updates, fork from any step
- Wired into admin HTTP server in
cmd/kronos/main.go
Commit: (2026-04-17)
- Added
ForkRunRPC toKronosServicein proto - Implemented
Engine.ForkFromStep()ininternal/workflow/engine.go - Implemented
Server.ForkRun()ininternal/api/server.go - Fork creates new run with parent_run_id, copies upstream events, schedules downstream steps
Commit: (2026-04-17)
- CLI:
kronos replay <run-id> [--break-at step] [--fork-from step] [--server addr] - Client:
internal/replay/client.go— gRPC client fetching run history + events - Replayer:
internal/replay/replayer.go— walks DAG re-invoking handlers against recorded outputs, breakpoint support (PID print + stdin wait), fork-from support - Diff:
internal/replay/diff.go— structural JSON diff for recorded vs replayed outputs - Tests:
internal/replay/diff_test.go— 10 tests covering diff, arg parsing - Subcommand dispatch: Added to
cmd/kronos/main.gobefore server startup - Workflow registration: Extracted
registerWorkflows()shared by server and replay
Commit: (2026-04-17)
- Added missing
//go:build integrationtag toworkflow_test.go - Integration tests no longer run during
go test ./...
Commit: (2026-04-17)
- Added badges (Go Report Card, MIT License, Go Reference, CI)
- Added comparison table (Kronos vs Temporal vs Hatchet vs River)
- Added "Why Kronos?" section with 4 key differentiators
- Restructured: badges → tagline → comparison → quick start → define workflow → replay → architecture → features → performance → config → tests → structure → roadmap
- Updated project structure to include debugger/ and replay/ packages
- Updated roadmap to reflect completed phases
- CLI subcommand:
cmd/kronos/replay.go— dispatch on argv before server startup - Client:
internal/replay/client.go— gRPC streaming client to fetch event history - Replayer:
internal/replay/replayer.go— takes event stream + local registry + breakpoint config; walks DAG re-invoking handlers - Diff:
internal/replay/diff.go— structural JSON diff for recorded vs. replayed outputs - Debugger API:
proto/kronos/v1/debugger.proto—StreamRunHistory,ForkRunRPC
Developer Experience:
$ kronos replay abc-123-def --break-at fetch_user_data
[replay] connecting to kronos://localhost:50051
[replay] fetched 47 events for run abc-123-def
[replay] step 1/8: validate_input ok (diff: 0 bytes)
[replay] step 2/8: fetch_user_data BREAK
[replay] pid=48291 — attach your debugger now, press enter to continueFeatures:
- Re-executes production run locally against recorded outputs
--break-at step_name— pause and attach Delve/VS Code/GoLand--fork-from step_name— test a fix without re-executing upstream (reuse recorded outputs)--skip-step— skip expensive steps- Output diffs surface divergence from production
Key Constraint:
- Developer must compile workflow definitions into replay binary themselves (e.g.,
github.com/me/myapp/cmd/replay) - This is the same model as
go run ./cmd/myapp— documented clearly
- Workflow version diffing in debugger and CLI (
kronos workflows diff v3 v5) - Typed step I/O via Go generics:
workflow.NewStep[Input, Output](name, handler, deps) - Per-step cost tracking via metadata (
step.Emit("cost_usd", 0.042)) - Optional auth middleware on debugger UI (HTTP basic / bearer token)
contrib/ai/llmstep.go— optional AI primitives (OpenAI/Anthropic clients, rate-limit retries, token counting, streaming)- Python client SDK for workflow submission (not replay)
- Design + spike HTMX + Alpine timeline scrubber (2 days)
- Build debugger server and handlers (3 days)
- Implement templates and static assets (4 days)
- Polish UI design and styling (3 days)
- Launch Phase 2 — Show HN post with screen recording of debugger
- Implement replay CLI subcommand (3 days)
- Streaming client + replayer engine (4 days)
- IDE debugger integration (Delve attach flow) (2 days)
- Fork workflow execution (2 days)
- Launch Phase 3 — GitHub release, follow-up HN comments
- Polish versioning UX
- Add AI primitives (optional, conditional on community interest)
- Improve documentation
- Address user feedback from Phase 2/3 launch
internal/workflow/definition.go— Workflow, Step, StepFunc, validationinternal/workflow/events.go— Event types and payloadsinternal/workflow/store.go— WorkflowStore interfaceinternal/workflow/postgres.go— Postgres implementationinternal/workflow/registry.go— Versioned workflow lookupinternal/workflow/engine.go— Step executor and event recorder
internal/api/server.go— All six workflow RPC implementationsproto/kronos/v1/kronos.proto— Workflow messages and servicegen/kronos/v1/*.pb.go— Generated stubs
migrations/005_create_workflows.up.sql— Schemamigrations/005_create_workflows.down.sql— Teardown
internal/integration/workflow_test.go— End-to-end test with Postgresinternal/workflow/engine_test.go— Unit tests with mock store
cmd/kronos/main.go— Startup, wiring, handler registration (line 80-100 for workflow setup)- TODO:
cmd/kronos/replay.go— Replay subcommand (Phase 3)
internal/store/postgres.go— SQL patterns (SELECT FOR UPDATE, transactional updates, cursor-based pagination)internal/admin/handler.go— HTTP handler pattern for mounting on admin muxinternal/scheduler/scheduler.go— Poll loop pattern for background workinternal/worker/worker.go— Handler registry pattern
- Visual UI renders workflow runs with step tree
- Timeline scrubber allows scrubbing through events
- Step details show exact input/output/error
- Fork workflow button creates new run
- SSE live updates for in-progress runs
- UI looks professional (screenshot worthy for HN)
-
kronos replay <run-id>fetches event history and replays locally -
--break-at step_namepauses and allows IDE debugger attachment - Output diffs surface divergence from production
-
--fork-fromcreates fork and skips upstream steps - All existing tests still pass with
-race
- Job queue functionality unchanged (echo, webhook-delivery handlers work)
- Cron scheduling unchanged
- Retries and dead letter unchanged
- Metrics endpoints work
- Leader election unchanged
- README updated with debugger screenshots/GIF
- Show HN post with compelling demo of time-travel debugging
- GitHub release v1.0.0
- 1k+ upvotes on HN (stretch goal: 5k+ stars on GitHub)
| Risk | Mitigation |
|---|---|
| UI quality is critical. Bootstrap-style UI = 300 stars; polished = 5k+ stars. | Hire designer 20h on Upwork or use Tailwind UI templates. Spend full week on polish. Screenshots at 2x retina. |
| HTMX + Alpine scrubber might feel janky. | 2-day spike in Phase 2. Fallback: minimal Preact for scrubber only. |
| Replay assumes determinism in recorded outputs. Users expect re-execution of upstream. | Clear docs: "replay uses recorded outputs; use --rerun-from to re-execute upstream." |
| Event log growth on long runs balloons Postgres. | Hard cap: 100k events per run. Auto-archive completed runs older than 30d. Ship kronos workflows prune command. |
| Hatchet (well-funded YC) ships debugger in 6 months. | Speed to Phase 2 is everything. Moats: open-source purity, single-binary, first-mover on keyword. |
| Code-version pinning vs in-place patches. Users ask "apply fix to live runs." | v1 cannot do this safely. Be explicit: "in-flight runs use pinned version; fork to apply fix." Defensible. |
Phase 0 (3/15): README pivot Phase 1 (3/17–3/30): Workflow types, store, registry, engine, proto, wiring, unit tests Phase 1.5 (4/03–4/07): Proto generation, API RPC methods, integration tests
Total commits so far: ~15 commits spanning March 15 – April 7, 2026 Lines of code (Phase 1): ~3,500 lines (workflows + tests) Test coverage: Unit + integration tests with mock and real Postgres
- Setup: Ensure protoc + plugins are available
- Design spike: Sketch debugger UI in Figma or by hand
- Start Phase 2: Build debugger server, templates, and static assets
- Target: Debugger UI functional by end of next session
Estimated effort for Phase 2: 3-4 weeks of focused development Estimated effort for Phase 3: 1-2 weeks after Phase 2 launch
Last updated: 2026-04-11 Status: Phase 1 complete, Phase 2 ready to start