picatz / picatz/flowstate

Design: entity workflows — a run that is the state of a thing

Open
#105 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

design engine kind/design-record
Dominant language
Go
Stars
9
Forks
0
Avg merge
3h 3m
Merged PRs (30d)
509

Description

Everything shipped so far is a run: it starts, does its steps, and finishes. Temporal's other great pattern is the entity workflow — a long-lived execution that is the current state of one thing (a tenant, a subscription, an incident, a device), mutated by signals and updates, compacted through Continue-As-New, addressed by a stable ID that names the entity. Flowstate should be able to express it, because it is the difference between "orchestrate a task" and "manage a thing over its lifetime" — and the second is where docs/ARCHITECTURE.md's workload list (business processes spanning months, operational runbooks) actually lives.

What the codebase already holds open for this, deliberately:

  • docs/DSL.md files state: and entities in Phase 5, and the byte-bound decision is already made ("state: gets a byte bound now, not an open question") — an entity's state rides in RunState, which already has size enforcement (CheckRunStateSize) and the wire-contract discipline of invariant 10.
  • Continue-As-New with signal draining already works — the exact machinery an entity needs to live past history limits without dropping a mutation in flight.
  • The signal path (wait_for_signal:, buffered early signals, PendingSignal carried across CAN) is the mutation-delivery half, already built.
  • Interpreter versioning (pinned per run, auto-upgrade at CAN) matters more for entities than for runs: an entity lives across many deploys by definition, and the CAN seam is exactly where it picks up new interpreter behavior safely.

What does not exist and the design must answer:

  1. A DSL shape. A run is steps: executed once. An entity is state plus handlers — "when signal X arrives, do these steps; when queried, answer this." That is a different top-level document shape, not a step kind. Sketch to argue with:
    edition: v2026.x
    entity: subscription
    state:
      plan: ${payload.plan}
      renewals: ${0}
    on:
      renew:
        steps:
          - id: charge
            http: ...
      cancel:
        steps: [...]
    
  2. Update, not just Signal. A mutation whose caller needs the answer (approve-and-tell-me) is Temporal's Update primitive, ranked in the earlier capability survey as needing a RunState mutation model under invariant 10 plus a local-driver request/response hook. Entities are the feature that finally justifies that cost — signals alone make every mutation fire-and-forget.
  3. Addressing and lifecycle. An entity's workflow ID is the entity key (subscription/cust-123), which means signal-with-start semantics (mutating a thing that does not exist yet creates it), tenancy on the key (the namespace prefix discipline the fairness key already uses), and a flow entity CLI/RPC surface distinct from flow run.
  4. The local driver question, faced honestly. A local rehearsal of an entity is a process that must accept mutations over time — closer to flow run local --signal than it looks, but the loop never ends. Probably: local entities rehearse one mutation at a time (flow entity local --state state.json --signal renew=... answering with the next state), which keeps invariant 3 meaningful without pretending a laptop process is durable.

Sequencing: behind Phase 2's typed contract (state and handler payloads want types) and alongside Update. Not Phase 5-distant, though — the schema groundwork (RunState fields, reserved document keys) is cheap to hold open now and expensive to retrofit. Related: #106 (modules — an entity definition is a natural thing for a module to export), #96 (webhooks — an inbound webhook mutating an entity is the composition that makes both features sing).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read docs/DSL.md and docs/ARCHITECTURE.md first, then review the existing RunState, CheckRunStateSize, signal buffering, Continue-As-New, and interpreter-versioning behavior described in the issue. The design is complete when it resolves the entity DSL, Update semantics, addressing and lifecycle, and local-driver behavior, with sequencing against #106 and #96 documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend-api-design, distributed-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.