Design: modules for composable, versioned, typed Flowstate packages
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 9
- Forks
- 0
- Avg merge
- 3h 3m
- Merged PRs (30d)
- 509
Description
Motivation
call: provides local compile-time workflow composition, but Flowstate does not yet have a coherent module model for teams to publish, discover, version, and reuse workflows together with the contracts and capabilities that make them usable. Without one, reuse fragments into ad hoc file conventions, copied policy/helpers/docs, and separate distribution stories.
A module is a declarative package resolved before execution. It may contribute workflows and supporting assets to compilation, but it does not execute code or gain ambient authority. Modules should make a team's tenth workflow mostly composition while preserving a normalized, hermetic Protobuf IR.
Scope
- Define module identity, manifests, imports/exports, namespaces, visibility, dependencies, version constraints, compatibility, lock/pin semantics, and cycle/conflict rules.
- Package coherent declarative assets: workflows, named types/schemas, policy fragments or references, CEL helper declarations where allowed by the one evaluator, tests/examples, documentation, and plugin capability requirements/bindings.
- Define export signatures and private implementation details; callers depend on typed interfaces rather than source layout.
- Support local filesystem and repository resolution first, with reproducible offline behavior. Preserve room for future remote/content-addressed resolution without selecting a registry now.
- Specify discovery and tooling across compile/validate/fmt/fix/test/breaking, CLI/API/LSP/MCP, generated docs, and SDK builders.
- Record provenance and resolved dependency identity in compilation output/lock material while embedding normalized executable content into the submitted IR.
- Clarify module versus plugin ownership: modules package declarative composition; plugins provide runtime effects/capabilities through #713.
Invariants
- Modules compose typed contracts, never tokens or YAML text. No templating or import-time computation.
- Resolution happens before execution. Workers never fetch a module or follow a mutable name while a run is executing.
- The submitted IR is hermetic and records sufficient provenance/digests to explain exactly what was compiled.
- Imports are explicit, namespaced, bounded, acyclic, and visibility-checked. A dependency cannot inject unqualified names or silently shadow another.
- A module cannot smuggle capability, identity, secrets, egress, or policy authority. It declares requirements; deployment admission decides whether they are available.
- A module's public surface is schema-owned and compatibility-checkable; private declarations may evolve without becoming caller contract.
- One dependency model governs direct and transitive modules. Resolution order must not decide meaning.
- Local/repository modules remain fully supported without a cloud service. Future remote resolution adds distribution, not new composition semantics.
- Module metadata and archives are untrusted bounded inputs; digests establish content identity, while signatures/provenance and policy establish trust.
Module and plugin ownership
- Module: declarative, inspectable without executing provider code, normalized into the IR. Owns workflows, schemas/types, policy content/references, helper declarations, tests/examples, docs, and capability requirements.
- Plugin: executable capability provider behind a Protobuf contract. Owns task/secret or future effect implementations, lifecycle, cancellation, errors, isolation, and runtime compatibility.
- Combined distribution: one product may ship a module that composes tasks from a companion plugin. The module imports declarative content and declares a qualified capability requirement; installing the module never launches or authorizes the plugin.
Relationship to adjacent layers and issues
- #528 is the one-program-model architecture record; modules are the primary breadth-through-composition mechanism.
- #234 governs why reusable behavior prefers modules over syntax.
- #1232 owns normalized IR and provenance; modules resolve into it before execution.
- #1231 owns execution semantics; modules do not create a second executor or runtime linker.
- #713 owns plugin capabilities and the module/plugin boundary.
- #172 owns current local
call:vendoring, isolation, digests, and static-analysis behavior; module calls should extend rather than replace it. - #177/#637 own typed workflow contracts; #346 records ecosystem/module tooling and standard-library governance; #548 owns composable policy vocabulary.
- #146/#721 own plugin integrity/isolation patterns that inform, but do not fully define, module supply-chain trust.
- #1226 can package governed development workflows as modules while obtaining effects from plugins.
Non-goals
- Selecting or building a public registry, marketplace, BSR/OCI layout, or remote transport now.
- Go-style minimum version selection, npm-style resolution, or any other algorithm before compatibility and reproducibility requirements are tested.
- Runtime dynamic linking, mutable lookup, import-time code execution, macros, or YAML preprocessing.
- Making every policy, document, or plugin binary part of one monolithic archive.
- Granting trust because a package is discoverable or signed; admission policy remains separate.
- Making a call a Temporal child workflow by definition. Runtime semantics decide execution boundaries independently of packaging.
Open design questions
- What is the smallest module manifest and canonical package identity for local and repository use?
- Are exports named workflows/types/policy/helpers individually, or grouped through one namespace object?
- Which visibility and re-export rules keep dependency graphs understandable without blocking useful facades?
- What version/constraint and lock semantics preserve reproducibility, offline use, and super-alpha evolution?
- How are source, normalized IR, docs, tests, and generated artifacts related by digests without hashing irrelevant formatting into semantic identity?
- How does
flow breakingcompare module public contracts, including workflow inputs/outputs, types, signals/triggers, policy interfaces, and capability requirements? - Which CEL helpers can a module contribute without creating a second evaluator or allowing behavior to change under a frozen run?
- How should future remote discovery authenticate publishers, verify provenance, handle revocation, and prevent dependency confusion?
- What belongs in a Flowstate standard library, and which tiers distinguish core primitives, blessed modules, official plugins, and community packages?
Acceptance criteria
- A module model defines identity, manifest, namespaces, imports/exports, visibility, dependencies, versions, discovery, and reproducible resolution.
- Local and repository resolution work offline; future remote resolution is specified only as a compatible extension point.
- A package can coherently include workflows, schemas/types, policy/helper declarations, docs/tests, and qualified plugin capability requirements without conflating declarative and executable content.
- Compilation produces a hermetic normalized IR plus bounded provenance/lock information; execution performs no module fetch.
- Cycles, conflicts, shadowing, path escape, graph size/depth, archive size, and unknown security metadata fail closed with positioned diagnostics where source exists.
- Public/private contract and
flow breakingsemantics are concrete enough to test across versions. - CLI/API/LSP/MCP/docs/test/fmt/fix surfaces understand the same module graph and never maintain separate resolvers.
- Module installation cannot launch a plugin, release a secret, widen egress, or grant authority.
- Two representative modules—one pure workflow/schema library and one companion to a plugin—demonstrate the ownership boundary.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the architecture and ownership records referenced in #528, #1232, #1231, #713, and #172 to define the module boundary. Then work through the open design questions and acceptance criteria for manifests, resolution, normalized IR, contracts, and tooling. Done means a concrete, testable module design covering the listed invariants and representative module examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100