UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,832 lines (1,322 loc) 82.8 kB
--- role: language-specification summary: | Complete syntax grammar, validation rules, and compilation semantics for OpenProse. Read this file when compiling, validating, or resolving ambiguous syntax. Assumes prose.md is already in context for execution semantics. see-also: - SKILL.md: Activation triggers, onboarding - prose.md: Execution semantics, how to run programs - state/filesystem.md: File-system state management (default) - state/in-context.md: In-context state management (on request) --- # OpenProse Language Reference OpenProse is a programming language for AI sessions. An AI session is a Turing-complete computer; this document provides complete documentation for the language syntax, semantics, and execution model. --- ## Document Purpose: Compiler + Validator This document serves a dual role: ### As Compiler When asked to "compile" a `.prose` file, use this specification to: 1. **Parse** the program according to the syntax grammar 2. **Validate** that the program is well-formed and semantically valid 3. **Transform** the program into "best practice" canonical form: - Expand syntax sugar where appropriate - Normalize formatting and structure - Apply optimizations (e.g., hoisting block definitions) ### As Validator The validation criterion: **Would a blank agent with only `prose.md` understand this program as self-evident?** When validating, check: - Syntax correctness (all constructs match grammar) - Semantic validity (references resolve, types match) - Self-evidence (program is clear without this full spec) If a construct is ambiguous or non-obvious, it should be flagged or transformed into a clearer form. ### When to Read This Document - **Compilation requested**: Read fully to apply all rules - **Validation requested**: Read fully to check all constraints - **Ambiguous syntax encountered**: Reference specific sections - **Interpretation only**: Use `prose.md` instead (smaller, faster) --- ## Table of Contents 1. [Overview](#overview) 2. [File Format](#file-format) 3. [Comments](#comments) 4. [String Literals](#string-literals) 5. [Use Statements](#use-statements-program-composition) 6. [Input Declarations](#input-declarations) 7. [Output Bindings](#output-bindings) 8. [Program Invocation](#program-invocation) 9. [Agent Definitions](#agent-definitions) 10. [Session Statement](#session-statement) 11. [Resume Statement](#resume-statement) 12. [Variables & Context](#variables--context) 13. [Composition Blocks](#composition-blocks) 14. [Parallel Blocks](#parallel-blocks) 15. [Fixed Loops](#fixed-loops) 16. [Unbounded Loops](#unbounded-loops) 17. [Pipeline Operations](#pipeline-operations) 18. [Error Handling](#error-handling) 19. [Choice Blocks](#choice-blocks) 20. [Conditional Statements](#conditional-statements) 21. [Execution Model](#execution-model) 22. [Validation Rules](#validation-rules) 23. [Examples](#examples) 24. [Future Features](#future-features) --- ## Overview OpenProse provides a declarative syntax for defining multi-agent workflows. Programs consist of statements that are executed sequentially, with each `session` statement spawning a subagent to complete a task. ### Design Principles - **Pattern over framework**: The simplest solution is barely anything at all—just structure for English - **Self-evident**: Programs should be understandable with minimal documentation - **The OpenProse VM is intelligent**: Design for understanding, not parsing - **Framework-agnostic**: Works with Claude Code, OpenCode, and any future agent framework - **Files are artifacts**: `.prose` is the portable unit of work ### Current Implementation Status The following features are implemented: | Feature | Status | Description | | ---------------------- | ----------- | -------------------------------------------- | | Comments | Implemented | `# comment` syntax | | Single-line strings | Implemented | `"string"` with escapes | | Simple session | Implemented | `session "prompt"` | | Agent definitions | Implemented | `agent name:` with model/prompt properties | | Session with agent | Implemented | `session: agent` with property overrides | | Use statements | Implemented | `use "@handle/slug" as name` | | Agent skills | Implemented | `skills: ["skill1", "skill2"]` | | Agent permissions | Implemented | `permissions:` block with rules | | Let binding | Implemented | `let name = session "..."` | | Const binding | Implemented | `const name = session "..."` | | Variable reassignment | Implemented | `name = session "..."` (for let only) | | Context property | Implemented | `context: var` or `context: [a, b, c]` | | do: blocks | Implemented | Explicit sequential blocks | | Inline sequence | Implemented | `session "A" -> session "B"` | | Named blocks | Implemented | `block name:` with `do name` invocation | | Parallel blocks | Implemented | `parallel:` for concurrent execution | | Named parallel results | Implemented | `x = session "..."` inside parallel | | Object context | Implemented | `context: { a, b, c }` shorthand | | Join strategies | Implemented | `parallel ("first"):` or `parallel ("any"):` | | Failure policies | Implemented | `parallel (on-fail: "continue"):` | | Repeat blocks | Implemented | `repeat N:` fixed iterations | | Repeat with index | Implemented | `repeat N as i:` with index variable | | For-each blocks | Implemented | `for item in items:` iteration | | For-each with index | Implemented | `for item, i in items:` with index | | Parallel for-each | Implemented | `parallel for item in items:` fan-out | | Unbounded loop | Implemented | `loop:` with optional max iterations | | Loop until | Implemented | `loop until **condition**:` AI-evaluated | | Loop while | Implemented | `loop while **condition**:` AI-evaluated | | Loop with index | Implemented | `loop as i:` or `loop until ... as i:` | | Map pipeline | Implemented | `items \| map:` transform each item | | Filter pipeline | Implemented | `items \| filter:` keep matching items | | Reduce pipeline | Implemented | `items \| reduce(acc, item):` accumulate | | Parallel map | Implemented | `items \| pmap:` concurrent transform | | Pipeline chaining | Implemented | `\| filter: ... \| map: ...` | | Try/catch blocks | Implemented | `try:` with `catch:` for error handling | | Try/catch/finally | Implemented | `finally:` for cleanup | | Error variable | Implemented | `catch as err:` access error context | | Throw statement | Implemented | `throw` or `throw "message"` | | Retry property | Implemented | `retry: 3` automatic retry on failure | | Backoff strategy | Implemented | `backoff: exponential` delay between retries | | Input declarations | Implemented | `input name: "description"` | | Output bindings | Implemented | `output name = expression` | | Program invocation | Implemented | `name(input: value)` call imported programs | | Multi-line strings | Implemented | `"""..."""` preserving whitespace | | String interpolation | Implemented | `"Hello {name}"` variable substitution | | Block parameters | Implemented | `block name(param):` with parameters | | Block invocation args | Implemented | `do name(arg)` passing arguments | | Choice blocks | Implemented | `choice **criteria**: option "label":` | | If/elif/else | Implemented | `if **condition**:` conditional branching | | Persistent agents | Implemented | `persist: true` or `persist: project` | | Resume statement | Implemented | `resume: agent` to continue with memory | --- ## File Format | Property | Value | | ---------------- | -------------------- | | Extension | `.prose` | | Encoding | UTF-8 | | Case sensitivity | Case-sensitive | | Indentation | Spaces (Python-like) | | Line endings | LF or CRLF | --- ## Comments Comments provide documentation within programs and are ignored during execution. ### Syntax ```prose # This is a standalone comment session "Hello" # This is an inline comment ``` ### Rules 1. Comments begin with `#` and extend to end of line 2. Comments can appear on their own line or after a statement 3. Empty comments are valid: `#` 4. The `#` character inside string literals is NOT a comment ### Examples ```prose # Program header comment # Author: Example session "Do something" # Explain what this does # This comment is between statements session "Do another thing" ``` ### Compilation Behavior Comments are **stripped during compilation**. The OpenProse VM never sees them. They have no effect on execution and exist purely for human documentation. ### Important Notes - **Comments inside strings are NOT comments**: ```prose session "Say hello # this is part of the string" ``` The `#` inside the string literal is part of the prompt, not a comment. - **Comments inside indented blocks are allowed**: ```prose agent researcher: # This comment is inside the block model: sonnet # This comment is outside the block ``` --- ## String Literals String literals represent text values, primarily used for session prompts. ### Syntax Strings are enclosed in double quotes: ```prose "This is a string" ``` ### Escape Sequences The following escape sequences are supported: | Sequence | Meaning | | -------- | ------------ | | `\\` | Backslash | | `\"` | Double quote | | `\n` | Newline | | `\t` | Tab | ### Examples ```prose session "Hello world" session "Line one\nLine two" session "She said \"hello\"" session "Path: C:\\Users\\name" session "Column1\tColumn2" ``` ### Rules 1. Single-line strings must be properly terminated with a closing `"` 2. Unknown escape sequences are errors 3. Empty strings `""` are valid but generate a warning when used as prompts ### Multi-line Strings Multi-line strings use triple double-quotes (`"""`) and preserve internal whitespace and newlines: ```prose session """ This is a multi-line prompt. It preserves: - Indentation - Line breaks - All internal whitespace """ ``` #### Multi-line String Rules 1. Opening `"""` must be followed by a newline 2. Content continues until closing `"""` 3. Escape sequences work the same as single-line strings 4. Leading/trailing whitespace inside the delimiters is preserved ### String Interpolation Strings can embed variable references using `{varname}` syntax: ```prose let name = session "Get the user's name" session "Hello {name}, welcome to the system!" ``` #### Interpolation Syntax - Variables are referenced by wrapping the variable name in curly braces: `{varname}` - Works in both single-line and multi-line strings - Empty braces `{}` are treated as literal text, not interpolation - Nested braces are not supported #### Examples ```prose let research = session "Research the topic" let analysis = session "Analyze findings" # Single variable interpolation session "Based on {research}, provide recommendations" # Multiple interpolations session "Combining {research} with {analysis}, synthesize insights" # Multi-line with interpolation session """ Review Summary: - Research: {research} - Analysis: {analysis} Please provide final recommendations. """ ``` #### Interpolation Rules 1. Variable names must be valid identifiers 2. Referenced variables must be in scope 3. Empty braces `{}` are literal text 4. Backslash can escape braces: `\{` produces literal `{` ### Validation | Check | Result | | -------------------------------- | ------- | | Unterminated string | Error | | Unknown escape sequence | Error | | Empty string as prompt | Warning | | Undefined interpolation variable | Error | --- ## Use Statements (Program Composition) Use statements import other OpenProse programs from the registry at `p.prose.md`, enabling modular workflows. ### Syntax ```prose use "@handle/slug" use "@handle/slug" as alias ``` ### Path Format Import paths follow the format `@handle/slug`: - `@handle` identifies the program author/organization - `slug` is the program name An optional alias (`as name`) allows referencing by a shorter name. ### Examples ```prose # Import a program use "@alice/research" # Import with alias use "@bob/critique" as critic ``` ### Program URL Resolution When the OpenProse VM encounters a `use` statement: 1. Fetch the program from `https://p.prose.md/@handle/slug` 2. Parse the program to extract its contract (inputs/outputs) 3. Register the program in the Import Registry ### Validation Rules | Check | Severity | Message | | --------------------- | -------- | -------------------------------------- | | Empty path | Error | Use path cannot be empty | | Invalid path format | Error | Path must be @handle/slug format | | Duplicate import | Error | Program already imported | | Missing alias for dup | Error | Alias required when importing multiple | ### Execution Semantics Use statements are processed before any agent definitions or sessions. The OpenProse VM: 1. Fetches and validates all imported programs at the start of execution 2. Extracts input/output contracts from each program 3. Registers programs in the Import Registry for later invocation --- ## Input Declarations Inputs declare what values a program expects from its caller. ### Syntax ```prose input name: "description" ``` ### Examples ```prose input topic: "The subject to research" input depth: "How deep to go (shallow, medium, deep)" ``` ### Semantics Inputs: - Are declared at the top of the program (before executable statements) - Have a name and a description (for documentation) - Become available as variables within the program body - Must be provided by the caller when invoking the program ### Validation Rules | Check | Severity | Message | | ---------------------- | -------- | ---------------------------------------------------- | | Empty input name | Error | Input name cannot be empty | | Empty description | Warning | Consider adding a description | | Duplicate input name | Error | Input already declared | | Input after executable | Error | Inputs must be declared before executable statements | --- ## Output Bindings Outputs declare what values a program produces for its caller. ### Syntax ```prose output name = expression ``` ### Examples ```prose let raw = session "Research {topic}" output findings = session "Synthesize research" context: raw output sources = session "Extract sources" context: raw ``` ### Semantics The `output` keyword: - Marks a variable as an output (visible at assignment, not just at file top) - Works like `let` but also registers the value as a program output - Can appear anywhere in the program body - Multiple outputs are supported ### Validation Rules | Check | Severity | Message | | --------------------- | -------- | ----------------------------------- | | Empty output name | Error | Output name cannot be empty | | Duplicate output name | Error | Output already declared | | Output name conflicts | Error | Output name conflicts with variable | --- ## Program Invocation Call imported programs by providing their inputs. ### Syntax ```prose name(input1: value1, input2: value2) ``` ### Examples ```prose use "@alice/research" as research let result = research(topic: "quantum computing") ``` ### Accessing Outputs The result contains all outputs from the invoked program, accessible as properties: ```prose session "Write summary" context: result.findings session "Cite sources" context: result.sources ``` ### Destructuring Outputs For convenience, outputs can be destructured: ```prose let { findings, sources } = research(topic: "quantum computing") ``` ### Execution Semantics When a program invokes an imported program: 1. **Bind inputs**: Map caller-provided values to the imported program's inputs 2. **Execute**: Run the imported program (spawns its own sessions) 3. **Collect outputs**: Gather all `output` bindings from the imported program 4. **Return**: Make outputs available to the caller as a result object The imported program runs in its own execution context but shares the same VM session. ### Validation Rules | Check | Severity | Message | | ----------------------- | -------- | ------------------------------ | | Unknown program | Error | Program not imported | | Missing required input | Error | Required input not provided | | Unknown input name | Error | Input not declared in program | | Unknown output property | Error | Output not declared in program | --- ## Agent Definitions Agents are reusable templates that configure subagent behavior. Once defined, agents can be referenced in session statements. ### Syntax ```prose agent name: model: sonnet prompt: "System prompt for this agent" skills: ["skill1", "skill2"] permissions: read: ["*.md"] bash: deny ``` ### Properties | Property | Type | Values | Description | | ------------- | ---------- | ---------------------------- | ----------------------------------- | | `model` | identifier | `sonnet`, `opus`, `haiku` | The Claude model to use | | `prompt` | string | Any string | System prompt/context for the agent | | `persist` | value | `true`, `project`, or STRING | Enable persistent memory for agent | | `skills` | array | String array | Skills assigned to this agent | | `permissions` | block | Permission rules | Access control for the agent | ### Persist Property The `persist` property enables agents to maintain memory across invocations: ```prose # Execution-scoped persistence (memory dies with run) agent captain: model: opus persist: true prompt: "You coordinate and review" # Project-scoped persistence (memory survives across runs) agent advisor: model: opus persist: project prompt: "You provide architectural guidance" # Custom path persistence agent shared: model: opus persist: ".prose/custom/shared-agent/" prompt: "Shared across programs" ``` | Value | Memory Location | Lifetime | | --------- | --------------------------------- | ------------------- | | `true` | `.prose/runs/{id}/agents/{name}/` | Dies with execution | | `project` | `.prose/agents/{name}/` | Survives executions | | STRING | Specified path | User-controlled | ### Skills Property The `skills` property assigns imported skills to an agent: ```prose use "@anthropic/web-search" use "@anthropic/summarizer" as summarizer agent researcher: skills: ["web-search", "summarizer"] ``` Skills must be imported before they can be assigned. Referencing an unimported skill generates a warning. ### Permissions Property The `permissions` property controls agent access: ```prose agent secure-agent: permissions: read: ["*.md", "*.txt"] write: ["output/"] bash: deny network: allow ``` #### Permission Types | Type | Description | | --------- | -------------------------------------------- | | `read` | Files the agent can read (glob patterns) | | `write` | Files the agent can write (glob patterns) | | `execute` | Files the agent can execute (glob patterns) | | `bash` | Shell access: `allow`, `deny`, or `prompt` | | `network` | Network access: `allow`, `deny`, or `prompt` | #### Permission Values | Value | Description | | -------- | ------------------------------------------------- | | `allow` | Permission granted | | `deny` | Permission denied | | `prompt` | Ask user for permission | | Array | List of allowed patterns (for read/write/execute) | ### Examples ```prose # Define a research agent agent researcher: model: sonnet prompt: "You are a research assistant skilled at finding and synthesizing information" # Define a writing agent agent writer: model: opus prompt: "You are a technical writer who creates clear, concise documentation" # Agent with only model agent quick: model: haiku # Agent with only prompt agent expert: prompt: "You are a domain expert" # Agent with skills agent web-researcher: model: sonnet skills: ["web-search", "summarizer"] # Agent with permissions agent file-handler: permissions: read: ["*.md", "*.txt"] write: ["output/"] bash: deny ``` ### Model Selection | Model | Use Case | | -------- | ------------------------------------- | | `haiku` | Fast, simple tasks; quick responses | | `sonnet` | Balanced performance; general purpose | | `opus` | Complex reasoning; detailed analysis | ### Execution Semantics When a session references an agent: 1. The agent's `model` property determines which Claude model is used 2. The agent's `prompt` property is included as system context 3. Session properties can override agent defaults ### Validation Rules | Check | Severity | Message | | --------------------- | -------- | ------------------------------ | | Duplicate agent name | Error | Agent already defined | | Invalid model value | Error | Must be sonnet, opus, or haiku | | Empty prompt property | Warning | Consider providing a prompt | | Duplicate property | Error | Property already specified | --- ## Session Statement The session statement is the primary executable construct in OpenProse. It spawns a subagent to complete a task. ### Syntax Variants #### Simple Session (with inline prompt) ```prose session "prompt text" ``` #### Session with Agent Reference ```prose session: agentName ``` #### Named Session with Agent ```prose session sessionName: agentName ``` #### Session with Properties ```prose session: agentName prompt: "Override the agent's default prompt" model: opus # Override the agent's model ``` ### Property Overrides When a session references an agent, it can override the agent's properties: ```prose agent researcher: model: sonnet prompt: "You are a research assistant" # Use researcher with different model session: researcher model: opus # Use researcher with different prompt session: researcher prompt: "Research this specific topic in depth" # Override both session: researcher model: opus prompt: "Specialized research task" ``` ### Execution Semantics When the OpenProse VM encounters a `session` statement: 1. **Resolve Configuration**: Merge agent defaults with session overrides 2. **Spawn a Subagent**: Create a new Claude subagent with the resolved configuration 3. **Send the Prompt**: Pass the prompt string to the subagent 4. **Wait for Completion**: Block until the subagent finishes 5. **Continue**: Proceed to the next statement ### Execution Flow Diagram ``` OpenProse VM Subagent | | | spawn session | |----------------------------->| | | | send prompt | |----------------------------->| | | | [processing...] | | | | session complete | |<-----------------------------| | | | continue to next statement | v v ``` ### Sequential Execution Multiple sessions execute sequentially: ```prose session "First task" session "Second task" session "Third task" ``` Each session waits for the previous one to complete before starting. ### Using Claude Code's Task Tool To execute a session, use the Task tool: ```typescript // Simple session Task({ description: "OpenProse session", prompt: "The prompt from the session statement", subagent_type: "general-purpose", }); // Session with agent configuration Task({ description: "OpenProse session", prompt: "The session prompt", subagent_type: "general-purpose", model: "opus", // From agent or override }); ``` ### Validation Rules | Check | Severity | Message | | ------------------------- | -------- | -------------------------------------------- | | Missing prompt and agent | Error | Session requires a prompt or agent reference | | Undefined agent reference | Error | Agent not defined | | Empty prompt `""` | Warning | Session has empty prompt | | Whitespace-only prompt | Warning | Session prompt contains only whitespace | | Prompt > 10,000 chars | Warning | Consider breaking into smaller tasks | | Duplicate property | Error | Property already specified | ### Examples ```prose # Simple session session "Hello world" # Session with agent agent researcher: model: sonnet prompt: "You research topics thoroughly" session: researcher prompt: "Research quantum computing applications" # Named session session analysis: researcher prompt: "Analyze the competitive landscape" ``` ### Canonical Form The compiled output preserves the structure: ``` Input: agent researcher: model: sonnet session: researcher prompt: "Do research" Output: agent researcher: model: sonnet session: researcher prompt: "Do research" ``` --- ## Resume Statement The `resume` statement continues a persistent agent with its accumulated memory. ### Syntax ```prose resume: agentName prompt: "Continue from where we left off" ``` ### Semantics | Keyword | Behavior | | ---------- | ------------------------------------- | | `session:` | Ignores existing memory, starts fresh | | `resume:` | Loads memory, continues with context | ### Examples ```prose agent captain: model: opus persist: true prompt: "You coordinate and review" # First invocation - creates memory session: captain prompt: "Review the plan" context: plan # Later invocation - loads memory resume: captain prompt: "Review step 1 of the plan" context: step1 # Output capture works with resume let review = resume: captain prompt: "Final review of all steps" ``` ### Validation Rules | Check | Severity | Message | | ------------------------------------------ | -------- | -------------------------------------------------------------------- | | `resume:` on non-persistent agent | Error | Agent must have `persist:` property to use `resume:` | | `resume:` with no existing memory | Error | No memory file exists for agent; use `session:` for first invocation | | `session:` on persistent agent with memory | Warning | Will ignore existing memory; use `resume:` to continue | | Undefined agent reference | Error | Agent not defined | --- ## Variables & Context Variables allow you to capture the results of sessions and pass them as context to subsequent sessions. ### Let Binding The `let` keyword creates a mutable variable bound to a session result: ```prose let research = session "Research the topic thoroughly" # research now holds the output of that session ``` Variables can be reassigned: ```prose let draft = session "Write initial draft" # Revise the draft draft = session "Improve the draft" context: draft ``` ### Const Binding The `const` keyword creates an immutable variable: ```prose const config = session "Get configuration settings" # This would be an error: # config = session "Try to change" ``` ### Context Property The `context` property passes previous session outputs to a new session: #### Single Context ```prose let research = session "Research quantum computing" session "Write summary" context: research ``` #### Multiple Contexts ```prose let research = session "Research the topic" let analysis = session "Analyze the findings" session "Write final report" context: [research, analysis] ``` #### Empty Context (Fresh Start) Use an empty array to start a session without inherited context: ```prose session "Independent task" context: [] ``` #### Object Context Shorthand For passing multiple named results (especially from parallel blocks), use object shorthand: ```prose parallel: a = session "Task A" b = session "Task B" session "Combine results" context: { a, b } ``` This is equivalent to passing an object where each property is a variable reference. ### Complete Example ```prose agent researcher: model: sonnet prompt: "You are a research assistant" agent writer: model: opus prompt: "You are a technical writer" # Gather research let research = session: researcher prompt: "Research quantum computing developments" # Analyze findings let analysis = session: researcher prompt: "Analyze the key findings" context: research # Write the final report using both contexts const report = session: writer prompt: "Write a comprehensive report" context: [research, analysis] ``` ### Validation Rules | Check | Severity | Message | | ------------------------------- | -------- | -------------------------------------------------- | | Duplicate variable name | Error | Variable already defined | | Const reassignment | Error | Cannot reassign const variable | | Undefined variable reference | Error | Undefined variable | | Variable conflicts with agent | Error | Variable name conflicts with agent name | | Undefined context variable | Error | Undefined variable in context | | Non-identifier in context array | Error | Context array elements must be variable references | ### Flat Namespace Requirement All variable names must be **unique within a program**. No shadowing is allowed across scopes. **This is a compile error:** ```prose let result = session "Outer task" for item in items: let result = session "Inner task" # Error: 'result' already defined context: item ``` **Why this constraint:** Since bindings are stored as `bindings/{name}.md`, two variables with the same name would collide on the filesystem. Rather than introduce complex scoping rules, we enforce uniqueness. **Collision scenarios this prevents:** 1. Variable inside loop shadows variable outside loop 2. Variables in different `if`/`elif`/`else` branches with same name 3. Block parameters shadowing outer variables 4. Parallel branches reusing outer variable names **Exception:** Imported programs run in isolated namespaces. A variable `result` in the main program does not collide with `result` in an imported program (they write to different `imports/{handle}--{slug}/bindings/` directories). --- ## Composition Blocks Composition blocks allow you to structure programs into reusable, named units and express sequences of operations inline. ### do: Block (Anonymous Sequential Block) The `do:` keyword creates an explicit sequential block. All statements in the block execute in order. #### Syntax ```prose do: statement1 statement2 ... ``` #### Examples ```prose # Explicit sequential block do: session "Research the topic" session "Analyze findings" session "Write summary" # Assign result to a variable let result = do: session "Gather data" session "Process data" ``` ### Block Definitions Named blocks create reusable workflow components. Define once, invoke multiple times. #### Syntax ```prose block name: statement1 statement2 ... ``` #### Invoking Blocks Use `do` followed by the block name to invoke a defined block: ```prose do blockname ``` #### Examples ```prose # Define a review pipeline block review-pipeline: session "Security review" session "Performance review" session "Synthesize reviews" # Define another block block final-check: session "Final verification" session "Sign off" # Use the blocks do review-pipeline session "Make fixes based on review" do final-check ``` ### Block Parameters Blocks can accept parameters to make them more flexible and reusable. #### Syntax ```prose block name(param1, param2): # param1 and param2 are available here statement1 statement2 ``` #### Invoking with Arguments Pass arguments when invoking a parameterized block: ```prose do name(arg1, arg2) ``` #### Examples ```prose # Define a parameterized block block review(topic): session "Research {topic} thoroughly" session "Analyze key findings about {topic}" session "Summarize {topic} analysis" # Invoke with different arguments do review("quantum computing") do review("machine learning") do review("blockchain") ``` #### Multiple Parameters ```prose block process-item(item, mode): session "Process {item} using {mode} mode" session "Verify {item} processing" do process-item("data.csv", "strict") do process-item("config.json", "lenient") ``` #### Parameter Scope - Parameters are scoped to the block body - Parameters shadow outer variables of the same name (with warning) - Parameters are implicitly `const` within the block #### Validation Rules | Check | Severity | Message | | ----------------------- | -------- | ---------------------------------------------- | | Argument count mismatch | Warning | Block expects N parameters but got M arguments | | Parameter shadows outer | Warning | Parameter shadows outer variable | ### Inline Sequence (Arrow Operator) The `->` operator chains sessions into a sequence on a single line. This is syntactic sugar for sequential execution. #### Syntax ```prose session "A" -> session "B" -> session "C" ``` This is equivalent to: ```prose session "A" session "B" session "C" ``` #### Examples ```prose # Quick pipeline session "Plan" -> session "Execute" -> session "Review" # Assign result let workflow = session "Draft" -> session "Edit" -> session "Finalize" ``` ### Block Hoisting Block definitions are hoisted - you can use a block before it's defined in the source: ```prose # Use before definition do validation-checks # Definition comes later block validation-checks: session "Check syntax" session "Check semantics" ``` ### Nested Composition Blocks and do: blocks can be nested: ```prose block outer-workflow: session "Start" do: session "Sub-task 1" session "Sub-task 2" session "End" do: do outer-workflow session "Final step" ``` ### Context with Blocks Blocks work with the context system: ```prose # Capture do block result let research = do: session "Gather information" session "Analyze patterns" # Use in subsequent session session "Write report" context: research ``` ### Validation Rules | Check | Severity | Message | | ------------------------------- | -------- | ------------------------------------ | | Undefined block reference | Error | Block not defined | | Duplicate block definition | Error | Block already defined | | Block name conflicts with agent | Error | Block name conflicts with agent name | | Empty block name | Error | Block definition must have a name | --- ## Parallel Blocks Parallel blocks allow multiple sessions to run concurrently. All branches execute simultaneously, and the block waits for all to complete before continuing. ### Basic Syntax ```prose parallel: session "Security review" session "Performance review" session "Style review" ``` All three sessions start at the same time and run concurrently. The program waits for all of them to complete before proceeding. ### Named Parallel Results Capture the results of parallel branches into variables: ```prose parallel: security = session "Security review" perf = session "Performance review" style = session "Style review" ``` These variables can then be used in subsequent sessions. ### Object Context Shorthand Pass multiple parallel results to a session using object shorthand: ```prose parallel: security = session "Security review" perf = session "Performance review" style = session "Style review" session "Synthesize all reviews" context: { security, perf, style } ``` The object shorthand `{ a, b, c }` is equivalent to passing an object with properties `a`, `b`, and `c` where each property's value is the corresponding variable. ### Mixed Composition #### Parallel Inside Sequential ```prose do: session "Setup" parallel: session "Task A" session "Task B" session "Cleanup" ``` The setup runs first, then Task A and Task B run in parallel, and finally cleanup runs. #### Sequential Inside Parallel ```prose parallel: do: session "Multi-step task 1a" session "Multi-step task 1b" do: session "Multi-step task 2a" session "Multi-step task 2b" ``` Each parallel branch contains a sequential workflow. The two workflows run concurrently. ### Assigning Parallel Blocks to Variables ```prose let results = parallel: session "Task A" session "Task B" ``` ### Complete Example ```prose agent reviewer: model: sonnet # Run parallel reviews parallel: sec = session: reviewer prompt: "Review for security issues" perf = session: reviewer prompt: "Review for performance issues" style = session: reviewer prompt: "Review for style issues" # Combine all reviews session "Create unified review report" context: { sec, perf, style } ``` ### Join Strategies By default, parallel blocks wait for all branches to complete. You can specify alternative join strategies: #### First (Race) Return as soon as the first branch completes, cancel others: ```prose parallel ("first"): session "Try approach A" session "Try approach B" session "Try approach C" ``` The first successful result wins. Other branches are cancelled. #### Any (N of M) Return when any N branches complete successfully: ```prose # Default: any 1 success parallel ("any"): session "Attempt 1" session "Attempt 2" # Specific count: wait for 2 successes parallel ("any", count: 2): session "Attempt 1" session "Attempt 2" session "Attempt 3" ``` #### All (Default) Wait for all branches to complete: ```prose # Implicit - this is the default parallel: session "Task A" session "Task B" # Explicit parallel ("all"): session "Task A" session "Task B" ``` ### Failure Policies Control how the parallel block handles branch failures: #### Fail-Fast (Default) If any branch fails, fail immediately and cancel other branches: ```prose parallel: # Implicit fail-fast session "Critical task 1" session "Critical task 2" # Explicit parallel (on-fail: "fail-fast"): session "Critical task 1" session "Critical task 2" ``` #### Continue Let all branches complete, then report all failures: ```prose parallel (on-fail: "continue"): session "Task 1" session "Task 2" session "Task 3" # Continue regardless of which branches failed session "Process results, including failures" ``` #### Ignore Ignore all failures, always succeed: ```prose parallel (on-fail: "ignore"): session "Optional enrichment 1" session "Optional enrichment 2" # This always runs, even if all branches failed session "Continue regardless" ``` ### Combining Modifiers Join strategies and failure policies can be combined: ```prose # Race with resilience parallel ("first", on-fail: "continue"): session "Fast but unreliable" session "Slow but reliable" # Get any 2 results, ignoring failures parallel ("any", count: 2, on-fail: "ignore"): session "Approach 1" session "Approach 2" session "Approach 3" session "Approach 4" ``` ### Execution Semantics When the OpenProse VM encounters a `parallel:` block: 1. **Fork**: Start all branches concurrently 2. **Execute**: Each branch runs independently 3. **Join**: Wait according to join strategy: - `"all"` (default): Wait for all branches - `"first"`: Return on first completion - `"any"`: Return on first success (or N successes with `count`) 4. **Handle failures**: According to on-fail policy: - `"fail-fast"` (default): Cancel remaining and fail immediately - `"continue"`: Wait for all, then report failures - `"ignore"`: Treat failures as successes 5. **Continue**: Proceed to the next statement with available results ### Validation Rules | Check | Severity | Message | | ------------------------------------ | -------- | -------------------------------------------- | | Invalid join strategy | Error | Must be "all", "first", or "any" | | Invalid on-fail policy | Error | Must be "fail-fast", "continue", or "ignore" | | Count without "any" | Error | Count is only valid with "any" strategy | | Count less than 1 | Error | Count must be at least 1 | | Count exceeds branches | Warning | Count exceeds number of parallel branches | | Duplicate variable in parallel | Error | Variable already defined | | Variable conflicts with agent | Error | Variable name conflicts with agent name | | Undefined variable in object context | Error | Undefined variable in context | --- ## Fixed Loops Fixed loops provide bounded iteration over a set number of times or over a collection. ### Repeat Block The `repeat` block executes its body a fixed number of times. #### Basic Syntax ```prose repeat 3: session "Generate a creative idea" ``` #### With Index Variable Access the current iteration index using `as`: ```prose repeat 5 as i: session "Process item" context: i ``` The index variable `i` is scoped to the loop body and starts at 0. ### For-Each Block The `for` block iterates over a collection. #### Basic Syntax ```prose let fruits = ["apple", "banana", "cherry"] for fruit in fruits: session "Describe this fruit" context: fruit ``` #### With Inline Array ```prose for topic in ["AI", "climate", "space"]: session "Research this topic" context: topic ``` #### With Index Variable Access both the item and its index: ```prose let items = ["a", "b", "c"] for item, i in items: session "Process item with index" context: [item, i] ``` ### Parallel For-Each The `parallel for` block runs all iterations concurrently (fan-out pattern): ```prose let topics = ["AI", "climate", "space"] parallel for topic in topics: session "Research this topic" context: topic session "Combine all research" ``` This is equivalent to: ```prose parallel: session "Research AI" context: "AI" session "Research climate" context: "climate" session "Research space" context: "space" ``` But more concise and dynamic. ### Variable Scoping Loop variables are scoped to the loop body: - They are implicitly `const` within each iteration - They shadow outer variables of the same name (with a warning) - They are not accessible outside the loop ```prose let item = session "outer" for item in ["a", "b"]: # 'item' here is the loop variable session "process loop item" context: item # 'item' here refers to the outer variable again session "use outer item" context: item ``` ### Nesting Loops can be nested: ```prose repeat 2: repeat 3: session "Inner task" ``` Different loop types can be combined: ```prose let items = ["a", "b"] repeat 2: for item in items: session "Process item" context: item ``` ### Complete Example ```prose # Generate multiple variations of ideas repeat 3: session "Generate a creative startup idea" session "Select the best idea from the options above" # Research the selected idea from multiple angles let angles = ["market", "technology", "competition"] parallel for angle in angles: session "Research this angle of the startup idea" context: angle session "Synthesize all research into a business plan" ``` ### Validation Rules | Check | Severity | Message | | ----------------------------- | -------- | ------------------------------------ | | Repeat count must be positive | Error | Repeat count must be positive | | Repeat count must be integer | Error | Repeat count must be an integer | | Undefined collection variable | Error | Undefined collection variable | | Loop variable shadows outer | Warning | Loop variable shadows outer variable | --- ## Unbounded Loops Unbounded loops provide iteration with AI-evaluated termination conditions. Unlike fixed loops, the iteration count is not known ahead of time - the OpenProse VM evaluates conditions at runtime using its intelligence to determine when to stop. ### Discretion Markers Unbounded loops use **discretion markers** (`**...**`) to wrap AI-evaluated conditions. These markers signal that the enclosed text should be interpreted intelligently by the OpenProse VM at runtime, not as a literal boolean expression. ```prose # The text inside **...** is evaluated by the AI loop until **the poem has vivid imagery and flows smoothly**: session "Review and improve the poem" ``` For multi-line conditions, use triple-asterisks: ```prose loop until *** the document is complete all sections have been reviewed and formatting is consistent ***: session "Continue working on the document" ``` ### Basic Loop The simplest unbounded loop runs indefinitely until explicitly limited: ```prose loop: session "Process next item" ``` **Warning**: Loops without termination conditions or max iterations generate a warning. Always include a safety limit: ```prose loop (max: 50): session "Process next item" ``` ### Loop Until The `loop until` variant runs until a condition becomes true: ```prose loop until **the task is complete**: session "Continue working on the task" ``` The OpenProse VM evaluates the discretion condition after each iteration and exits when it determines the condition is satisfied. ### Loop While The `loop while` variant runs while a condition remains true: ```prose loop while **there are still items to process**: session "Process the next item" ``` Semantically, `loop while **X**` is equivalent to `loop until **not X**`. ### Iteration Variable Track the current iteration number using `as`: ```prose loop until **done** as attempt: session "Try approach" context: attempt ``` The iteration variable: - Starts at 0 - Increments by 1 each iteration - Is scoped to the loop body - Is implicitly `const` within each iteration ### Safety Limits Specify maximum iterations with `(max: N)`: ```prose # Stop after 10 iterations even if condition not met loop until **all bugs fixed** (max: 10): session "Find and fix a bug" ``` The loop exits when: 1. The condition is satisfied (for `until`/`while` variants), OR 2. The maximum iteration count is reached ### Complete Syntax All options can be combined: ```prose loop until **condition** (max: N) as i: body... ``` Order matters: condition comes before modifiers, modifiers before `as`. ### Examples #### Iterative Improvement ```prose session "Write an initial draft" loop until **the draft is polished and ready for review** (max: 5): session "Review the current draft and identify issues" session "Revise the draft to address the issues" session "Present the final draft" ``` #### Debugging Workflow ```prose session "Run tests to identify failures" loop until **all tests pass** (max: 20) as attempt: session "Identify the failing test" session "Fix the bug causing the failure" session "Run tests again" session "Confirm all tests pass and summarize fixes" ``` #### Consensus Building ```prose parallel: opinion1 = session "Get first expert opinion" opinion2 = session "Get second expert opinion" loop until **experts have reached consensus** (max: 5): session "Identify points of disagreement" context: { opinion1, opinion2 } session "Facilitate discussion to resolve differences" session "Document the final consensus" ``` #### Quality Threshold ```prose let draft = session "Create initial document" loop while **quality score is below threshold** (max: 10):