Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
197 lines
11 KiB
Markdown
197 lines
11 KiB
Markdown
# PRD — Workflow Markdown Text Composer
|
|
|
|
- Slug: `workflow-markdown-text-composer`
|
|
- Date: `2026-03-14`
|
|
- Status: Draft
|
|
|
|
## Summary
|
|
|
|
Add a standard workflow transform action for composing one or more named markdown text outputs from literal content plus simple workflow references. Authoring should use a constrained BlockNote-based inline chip editor for a high-quality WYSIWYG experience, while persistence and runtime should use an Alga-owned template document model and emit plain markdown strings.
|
|
|
|
## Problem
|
|
|
|
Workflow authors currently have two poor options when they need richer text composition:
|
|
|
|
- write a single literal string directly in a consuming field such as `ai.infer.prompt`
|
|
- wire a single reference into that field from elsewhere
|
|
|
|
That breaks down when authors need to build prompts, email bodies, summaries, or notification text from several pieces of workflow context. Prompt-specific templating inside `ai.infer` would solve the symptom in one place but would create improper layering and leave email and notification composition unsolved.
|
|
|
|
The workflow system already has a transform category for building derived values. Text composition belongs there, but the current text transforms are scalar utilities such as concat, replace, join, or trim. They do not provide:
|
|
|
|
- multi-output authoring
|
|
- inline reference placeholders
|
|
- markdown-capable editing
|
|
- explicit missing-reference failure semantics
|
|
|
|
There is also a UX gap. Placeholder-string editing in a plain text box is easy for engineering, but not ideal for authors. We already have BlockNote infrastructure and custom inline content patterns in the repo, which can support a more natural authoring surface if we keep BlockNote confined to the UI layer.
|
|
|
|
## Goals
|
|
|
|
- Introduce a dedicated standard transform action for text composition rather than adding prompt-specific behavior to `ai.infer`.
|
|
- Support multiple related text outputs in a single step.
|
|
- Let authors use freeform output labels while preserving stable downstream reference paths.
|
|
- Provide a constrained BlockNote-based editor with inline reference placeholders for authoring.
|
|
- Persist an Alga-owned template document model rather than BlockNote JSON.
|
|
- Render composed outputs to markdown strings at runtime.
|
|
- Fail execution explicitly when any referenced value is missing.
|
|
- Surface composed outputs in downstream workflow reference browsing and validation.
|
|
|
|
## Non-goals
|
|
|
|
- No AI-specific prompt builder embedded into consuming actions.
|
|
- No inline launch of the composer from `ai.infer.prompt` or other fixed-value fields in this phase.
|
|
- No generic expression segments, nested transform invocation, or arbitrary code execution inside templates.
|
|
- No media, attachments, tables, or other document-heavy BlockNote concepts in the composer.
|
|
- No HTML output from the transform itself; markdown-to-HTML conversion remains the responsibility of downstream consumers.
|
|
- No broad reusable rich-editor platform for all workflow fields in this phase.
|
|
|
|
## Users and Primary Flows
|
|
|
|
- Workflow authors building AI prompts, email bodies, customer-facing summaries, internal notes, or notification text from payload and prior step outputs.
|
|
|
|
Primary flows:
|
|
|
|
- Author adds a `transform.compose_text` step and saves it as `composed`.
|
|
- Author creates several related outputs in the same step such as “Prompt”, “Email Body”, and “Summary”.
|
|
- Author writes markdown-friendly text with inline reference placeholders chosen from the workflow source browser.
|
|
- Author maps downstream action fields by reference, for example `vars.composed.prompt`.
|
|
- Workflow runtime resolves references, renders markdown strings, and fails the step if a referenced value is missing.
|
|
|
|
## UX / UI Notes
|
|
|
|
- The composer exists only as a dedicated transform action editor.
|
|
- The editor shows a list of outputs, each with:
|
|
- freeform author label
|
|
- generated stable reference key
|
|
- constrained BlockNote editing surface
|
|
- The UI should present the author label prominently and the stable reference key secondarily, including a copyable downstream reference path.
|
|
- The editing surface should feel WYSIWYG:
|
|
- inline reference chips instead of raw placeholder syntax
|
|
- markdown-safe text formatting only
|
|
- no media insertion affordances
|
|
- Authors should be able to:
|
|
- add, rename, delete, and reorder outputs
|
|
- add literal text
|
|
- insert simple references from existing workflow data context
|
|
- see validation issues for duplicate labels, duplicate keys, or invalid references
|
|
- The UI may show a serialized markdown preview or reference summary, but v1 does not need resolved runtime-value preview.
|
|
|
|
## Requirements
|
|
|
|
### Functional Requirements
|
|
|
|
- The workflow runtime must register a new dedicated transform action for text composition under the Transform category.
|
|
- The action must support multiple named outputs inside one step.
|
|
- Each authored output must have a freeform display label.
|
|
- Each authored output must also have a stable, reference-safe key used for downstream paths and schema fields.
|
|
- Stable keys must be generated automatically from display labels and remain stable across later label edits unless the user explicitly regenerates them.
|
|
- The persisted action config must store an Alga-owned template document model rather than BlockNote JSON.
|
|
- The template document model must support markdown-capable text structure plus inline reference nodes without leaking BlockNote-specific node shapes.
|
|
- Reference nodes must only allow simple workflow references, not arbitrary expressions or nested transform definitions.
|
|
- The dedicated editor must use a constrained BlockNote-based surface for authoring and must round-trip between BlockNote UI state and the Alga-owned template document model.
|
|
- The constrained editor must disable media and other unsupported content types.
|
|
- Runtime execution must resolve references against workflow context and render each output to a markdown string.
|
|
- Runtime execution must fail the step if any referenced value is missing, identifying the output and reference that failed.
|
|
- The action result must be a plain object of markdown strings keyed by stable reference-safe keys.
|
|
- Workflow output schema derivation must reflect the configured composed outputs so downstream reference browsing and validation expose the correct fields.
|
|
- Downstream actions such as `ai.infer`, email actions, and notifications must consume composed outputs via ordinary reference mode with no special-case integration.
|
|
|
|
### Non-functional Requirements
|
|
|
|
- The composer must stay reusable and not introduce AI-specific layering into workflow actions.
|
|
- BlockNote must remain an authoring-only dependency for this feature; runtime contracts and stored step config must remain independent of BlockNote internals.
|
|
- The chosen markdown-capable editing subset must be intentionally constrained to features that round-trip cleanly through the Alga-owned model and markdown renderer.
|
|
- Validation and schema derivation must remain deterministic so downstream references do not drift after author label edits.
|
|
|
|
## Architecture
|
|
|
|
- Runtime layer:
|
|
- new `transform.compose_text` action definition
|
|
- config schema for outputs and template document structure
|
|
- runtime renderer from template document model to markdown strings
|
|
- missing-reference failure handling
|
|
- Designer layer:
|
|
- dedicated compose-text step editor
|
|
- constrained BlockNote schema with inline reference placeholder node
|
|
- serialization/deserialization between editor content and template document model
|
|
- output list management and stable key affordances
|
|
- Schema / reference layer:
|
|
- dynamic output schema derivation for configured composed outputs
|
|
- workflow data context and reference browser support for `vars.<saveAs>.<stableKey>`
|
|
|
|
## Data / API / Integrations
|
|
|
|
Recommended persisted config shape:
|
|
|
|
```ts
|
|
type ComposeTextStepConfig = {
|
|
actionId: 'transform.compose_text';
|
|
version: 1;
|
|
outputs: Array<{
|
|
id: string;
|
|
label: string;
|
|
stableKey: string;
|
|
document: TemplateDocument;
|
|
}>;
|
|
};
|
|
|
|
type TemplateDocument = {
|
|
version: 1;
|
|
blocks: TemplateBlock[];
|
|
};
|
|
|
|
type TemplateBlock =
|
|
| { type: 'paragraph'; children: TemplateInlineNode[] }
|
|
| { type: 'bullet_list_item'; children: TemplateInlineNode[] }
|
|
| { type: 'ordered_list_item'; children: TemplateInlineNode[] }
|
|
| { type: 'heading'; level: 1 | 2 | 3; children: TemplateInlineNode[] }
|
|
| { type: 'blockquote'; children: TemplateInlineNode[] }
|
|
| { type: 'code_block'; text: string };
|
|
|
|
type TemplateInlineNode =
|
|
| { type: 'text'; text: string; marks?: Array<'bold' | 'italic' | 'code' | 'link'>; href?: string }
|
|
| { type: 'reference'; path: string; label: string };
|
|
```
|
|
|
|
Notes:
|
|
|
|
- The exact block subset can be refined, but it must remain markdown-compatible and must exclude media-heavy nodes.
|
|
- `label` is author-facing and freeform.
|
|
- `stableKey` is reference-safe and immutable by default so downstream `vars.<saveAs>.<stableKey>` paths do not break when the author changes the display label.
|
|
- The registry output schema may be broad at definition time, but publish-time/designer-time output schema resolution must override it with a config-derived object schema containing one string field per configured `stableKey`.
|
|
|
|
## Security / Permissions
|
|
|
|
- No new permissions are introduced.
|
|
- Existing workflow authoring and workflow read/update permission gates continue to govern access.
|
|
- The composer must not allow arbitrary expression execution or secret resolution outside the existing workflow reference/runtime mechanisms.
|
|
|
|
## Observability
|
|
|
|
- No new observability or metrics work is in scope for this phase.
|
|
|
|
## Rollout / Migration
|
|
|
|
- Add the new transform action without changing existing actions.
|
|
- Existing workflows remain untouched.
|
|
- New workflows may adopt `transform.compose_text` incrementally.
|
|
- AI prompt composition, email body composition, and similar use cases should migrate by adding an upstream compose-text step rather than modifying consuming action contracts.
|
|
|
|
## Open Questions
|
|
|
|
- Exact markdown-capable formatting subset for v1 should be finalized during implementation, but it should remain deliberately narrower than a full document editor.
|
|
- Whether the UI should expose explicit “regenerate stable key” behavior on rename or keep that as an advanced secondary control needs a final UX call.
|
|
- Whether a serialized markdown preview adds enough value in v1 to justify the extra UI surface remains optional.
|
|
|
|
## Acceptance Criteria (Definition of Done)
|
|
|
|
- Workflow authors can add a dedicated transform action that composes one or more markdown text outputs from literal content and simple references.
|
|
- The authoring experience uses a constrained BlockNote-based editor with inline reference placeholders and no media affordances.
|
|
- The stored step config uses an Alga-owned template document model rather than BlockNote JSON.
|
|
- Each output has a freeform author label and a stable downstream reference-safe key.
|
|
- Runtime execution renders each configured output to a markdown string.
|
|
- Missing referenced values fail the step explicitly instead of silently producing empty strings.
|
|
- Downstream workflow authoring surfaces expose composed outputs under `vars.<saveAs>.<stableKey>`.
|
|
- Consuming actions continue to use ordinary string references and require no prompt-specific or email-specific composition logic.
|