Skip to content

Releases: jaiphlang/jaiph

v0.10.0

Choose a tag to compare

@dzikowski dzikowski released this 13 Jul 10:12

What's Changed

  • Docker sandbox inplace mode (edits land live on the host while the machine stays isolated) plus jaiph run CLI flags --workspace, --inplace, --unsafe, -y/--yes.
  • Agent reliability: prompt retry with escalating backoff, a fail-fast credential pre-flight keyed to the backend, and the default sandbox timeout raised to 4 hours.
  • Prompt watchdog: subprocess backends (claude / cursor / custom) now get three independent timeout layers — completion grace, idle timeout, and an absolute wall-clock cap (JAIPH_PROMPT_* env knobs) — so a backend that streams its final answer but never exits can no longer hang a run.
  • jaiph install gains registry name resolution and commit-SHA pinning; new per-platform standalone release binaries + installer and a release-prep workflow that single-sources the CLI version. Supports Linux and MacOS, Windows requires WSL.
  • Parser/compiler simplification refactors, language features (for … in line loops, else branches, dot-notation if/match subjects, catch/recover on inline-script run), and assorted fixes.
  • Full Diátaxis docs redesign of jaiph.org: greenfield Tutorials, How-to, Reference, and Explanation pages, a regrouped nav with redirect coverage for every historical URL, and the docs/_legacy/ quarantine retired — all pinned by a machine-checkable docs-lint test harness.

Full Changelog: v0.9.4...v0.10.0

Nightly

Nightly Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Jul 17:34

Rolling nightly prerelease — standalone binaries built from the latest nightly branch.

v0.9.4

Choose a tag to compare

@dzikowski dzikowski released this 13 May 15:59

Summary

  • Feature: for <line> in <string> { ... } loop.
  • Simplifying: Sequential inbox only; stricter grammar (multiline config, no one-line braced workflows, no ; in workflow/rule bodies).
  • Hardening, test refactoring and bug fixes

Full Changelog: v0.9.3...v0.9.4

v0.9.3 Sandboxing, hardening compiler and runtime

Choose a tag to compare

@dzikowski dzikowski released this 24 Apr 15:00

Summary

  • Sandboxing: Docker is always on with read-only workspace; use jaiphlang/artifacts.save() for file persistence. Improved image and security.
  • Compiler: All identifiers are checked and immutable; clearer errors; supports inline run inside return and log.
  • Language/runtime: Adds Handle<T> for async, repair-and-retry with recover, optional module metadata.
  • Tests: Test blocks support const; must explicitly bind response values; name checking at compile time.
  • CLI: Shows workflow return values and improves output for prompts and Docker failures.

Full Changelog: v0.9.2...v0.9.3

v0.9.2

Choose a tag to compare

@dzikowski dzikowski released this 17 Apr 12:19

Summary

  • Runtime: Remove JAIPH_LIB in favor of workspace-relative paths and import script; strip inherited JAIPH_LIB so parent shells cannot inject stale library paths.
  • Runtime: Write a heartbeat file in the run directory (refreshed every 10s) so external tooling can tell whether a run is still alive.
  • Docker: Base images without jaiph now work generically: Jaiph builds a thin derived image, installs the current package into it, and writes artifacts directly to the configured host runs root.
  • Docs / E2E: Align documentation and tests with the JAIPH_LIB removal.

Full Changelog: v0.9.1...v0.9.2

v0.9.1

Choose a tag to compare

@dzikowski dzikowski released this 16 Apr 10:57

Summary

  • CLI: jaiph init now scaffolds a managed .jaiph/Dockerfile, emits a compilable bootstrap prompt, and strengthens bootstrap guidance for sandboxed projects.
  • Docker: Docker-backed runs now exit cleanly, stream live events correctly, and use a non-root managed image setup that works with stricter agent CLIs.
  • Language/CLI: recover is renamed to catch, import script adds external script-file imports, and jaiph report has been removed.

v0.9.0

Choose a tag to compare

@dzikowski dzikowski released this 10 Apr 15:55

Summary

  • Language: Major syntax overhaul — triple-quoted strings ("""...""") for multiline text, backtick/fenced delimiters for scripts, explicit recover bindings, required parentheses on definitions, bare identifier arguments, export visibility on modules, match arm runtime execution, and ${...} shell expansion allowed in fenced script blocks. The if keyword was initially removed in favor of recover/match, then re-added as a conditional guard (if var == "value" { … }, if var =~ /pattern/ { … }). Seven deprecated constructs removed with migration hints.
  • CLI: jaiph install for project-scoped libraries with lockfile, jaiph format for canonical .jh formatting, jaiph init with .gitignore/bootstrap/skill scaffolding, async branch numbering in progress tree, prompt backend/model display, inbox dispatch parameter display, and file shorthand routing.
  • Runtime: codex backend (OpenAI Chat Completions API), custom agent commands, Docker sandbox fixes (tree UI, failed-step output, path remapping), model auto-detection, return run/return ensure direct managed calls, and string/script type crossing enforcement.
  • Testing: Golden AST tests for parse tree shape, txtar-based compiler test suite with 238 cases, E2E full-output comparison audit across 27 test files, and Playwright landing-page sample verification.
  • Docs: Complete documentation overhaul — all 11 pages revisited against source code, agent skill improved with examples/testing/CLI table, README audience routing, and landing page updated with all new features.

Full Changelog: v0.8.0...v0.9.0

v0.8.0

Choose a tag to compare

@dzikowski dzikowski released this 03 Apr 11:36
  • Breaking — Language: Match — keyword always first, no dollar prefix — match now always precedes the subject in both statement and expression positions, and the subject is a bare identifier (no $ or ${}). The old postfix expression form (${var} match { … }) is removed. The new uniform syntax is match var { … } for statements, const x = match var { … } for expressions, and return match var { … } for return position. Using $var or ${var} as the match subject is a hard parse error with guidance: match subject should be a bare identifier: match varName { ... }. The AST MatchExprDef.subject now stores a plain identifier string instead of a bash-style $var/${var} reference. The runtime resolves the bare identifier against the variable scope directly instead of interpolating a bash expression. Migration: rewrite match $var { … }match var { … } and $var match { … } / ${var} match { … }match var { … }. Implementation: validateMatchSubject() in src/parse/match.ts; extractPostfixMatchSubject() removed; prefix-form detection in src/parse/const-rhs.ts and src/parse/workflow-brace.ts; emitter updated in src/format/emit.ts; direct identifier lookup in src/runtime/kernel/node-workflow-runtime.ts. All .jh sources, compiler tests, golden AST fixtures, E2E tests, and docs updated.
  • Breaking — Language: Channel declarations with inline routing — Route declarations (->) now belong at the top level on channel declarations, not inside workflow bodies. channel findings -> analyst declares both the channel and its route target in one line; channel events -> handler_a, handler_b supports multiple comma-separated targets. A -> route inside a workflow body is now a hard parse error with guidance: route declarations belong at the top level: channel <name> -> <targets>. The runtime reads routes from ChannelDef.routes instead of workflow-level route statements, and the validator checks that every route target is an existing workflow with exactly 3 declared parameters (rules and scripts are rejected). The formatter emits channel name -> target in canonical output. Migration: move all channel_ref -> workflow lines from workflow bodies to their corresponding top-level channel declarations. All .jh sources, E2E fixtures, compiler tests, examples, and docs updated. Implementation: ChannelDef.routes in src/types.ts; parseChannelLine extended in src/parse/channels.ts; -> rejected in src/parse/workflows.ts; route map built from ChannelDef in src/runtime/kernel/node-workflow-runtime.ts; route target validation in src/transpile/validate.ts; formatter in src/format/emit.ts.
  • Breaking — Language: Remove ${argN} positional parameter access — Named workflow/rule parameters are now the only way to access argument values in orchestration strings. The positional ${arg1}, ${arg2}, … ${arg9} slots are removed from both the validator and the runtime. If a workflow declares workflow greet(name), ${name} is the only binding; ${arg1} is now E_VALIDATE: unknown identifier. CLI positional arguments map to declared parameter names by position. Inbox route targets must declare exactly 3 parameters — fewer or more is E_VALIDATE; the runtime binds message, channel, and sender to whatever names the target declares (e.g. workflow handler(msg, ch, who)${msg}, ${ch}, ${who}). Exception: ${arg1} is still injected in ensure … recover blocks as the merged stdout+stderr from the failed rule execution — this is a special runtime variable, not a positional parameter. Migration: all .jh sources, E2E fixtures, compiler tests, examples, and docs updated to use named parameters exclusively. Implementation: /^arg([1-9])$/ check and maxPositionalSlots removed from validateSimpleInterpolationIdentifiers() in src/transpile/validate-string.ts; arg1arg9 injection removed from NodeWorkflowRuntime orchestration scope in src/runtime/kernel/node-workflow-runtime.ts; inbox dispatch switched from arg1/arg2/arg3 to positional binding against declared parameter names; CLI arg injection updated in src/runtime/kernel/workflow-launch.ts.
  • Language: Optional parentheses at call sites — run, ensure, and if call sites now allow omitting parentheses when passing zero arguments. run setup is equivalent to run setup(), ensure check is equivalent to ensure check(), and if not run file_exists { ... } works without parens. This applies uniformly to all call-site positions: standalone steps, const x = run ref, return run ref, return ensure ref, channel <- run ref, and brace if/else if conditions. When arguments are present, parentheses remain required: run deploy(env, version). Definitions always require ()workflow setup { ... } without parens is still a parse error. Arity checking works identically with the bare form (zero arguments). jaiph format normalizes zero-arg calls to the parenthesized form (ref()) for unambiguous output. Implementation: parseCallRef fallback in src/parse/core.ts, call-path routing in src/parse/steps.ts, src/parse/const-rhs.ts, and src/parse/workflow-brace.ts. Parser tests cover bare-identifier call sites in all positions; all existing ref() tests pass unchanged.
  • Language: String/script type crossing enforcement — string and script are now enforced as structurally distinct primitive types at every call site. The validator tracks which names are scripts vs. strings in the symbol table and rejects four invalid crossings: (1) prompt scriptNameE_VALIDATE: scripts are not promptable; use a string const instead; (2) run constName() where constName is a string → E_VALIDATE: strings are not executable; use a script instead; (3) const x = scriptNameE_VALIDATE: scripts are not values; (4) ${scriptName} interpolation → E_VALIDATE: scripts cannot be interpolated. Valid usage (prompt stringName, run scriptName()) is unchanged. Enforcement applies in both workflow and rule bodies, including capture forms (const x = run …, const x = prompt …). A new "Types" section in docs/grammar.md explains the string/script distinction as the conceptual foundation of the type system. Compiler tests cover all four invalid crossings plus valid usage. Implementation: localScripts set threaded through validateReferences() in src/transpile/validate.ts, localScripts parameter added to validateSimpleInterpolationIdentifiers() in src/transpile/validate-string.ts.
  • Breaking — Language: Triple-quoted strings ("""...""") — """...""" is now the universal multiline string form. "..." is single-line only; a double-quoted string that spans multiple lines is a hard parse error with guidance pointing to triple quotes. Triple-quoted strings work everywhere strings appear: top-level const, workflow/rule const, log, logerr, fail, return, and send (channel <- """..."""). ${...} interpolation works inside triple-quoted blocks, same rules as single-line strings. This completes the four-delimiter system: " single-line string, """ multiline string, ` single-line script, ``` multiline script. Parser: shared parseTripleQuoteBlock() utility in src/parse/triple-quote.ts reused across env.ts, const-rhs.ts, prompt.ts, send-rhs.ts, and workflow-brace.ts. tripleQuoteBodyToRaw() normalizes triple-quoted content to the internal quoted-string format. Formatter: src/format/emit.ts detects newlines in string values and re-emits as """...""" blocks. Error messages: unclosed multiline double-quotes produce guidance like multiline strings use triple quotes: const name = """...""". Migration: all .jh sources, fixtures, compiler tests, golden AST, E2E tests, and docs updated. Implementation: src/parse/triple-quote.ts, src/parse/env.ts, src/parse/const-rhs.ts, src/parse/send-rhs.ts, src/parse/workflow-brace.ts, src/format/emit.ts.
  • Breaking — Language: Prompt triple-quoted blocks — Prompt multiline bodies now use triple-quote delimiters ("""...""") instead of triple backticks. Triple backticks are reserved exclusively for scripts. The delimiter structurally identifies content type: natural language (quotes) vs. executable code (backticks). prompt """...""" parses as a block prompt with bodyKind: "triple_quoted". Using triple backticks in prompt context is a hard parse error with guidance: prompt blocks use triple quotes: prompt """..."""; triple backticks are for scripts. ${...} interpolation works inside triple-quoted blocks. returns "{ ... }" works after the closing """ (same line or next line). Single-line prompt "text" and bare identifier prompt myVar are unchanged. AST: bodyKind replaces "fenced" with "triple_quoted" for prompt steps; script bodyKind keeps "fenced". Parser: parseTripleQuoteBlock() in src/parse/prompt.ts handles """...""" blocks; parseFencedBlock() remains for scripts only. Formatter updated to emit """ delimiters for prompts. Migration: all .jh sources, fixtures, compiler tests, golden AST, E2E tests, examples, and docs updated. Implementation: src/parse/prompt.ts, src/types.ts, src/format/emit.ts.
  • Breaking — Language: Script delimiters — backtick for inline, triple-backtick fence for block. Named scripts now use single backtick (script name = `body`) for single-line and triple-backtick fence (script name = ```lang ... ```) for multi-line bodies. The script keyword is kept for named declarations only. The previous double-quoted string body (script name = "body") and bare identifier body (script name = varName) forms are removed — both produce parse errors with guidance to use backtick delimiters. Anonymous inline scripts use run `body`(args) and run ```lang...body...```(args) — the old run script() "body" form is removed. All scripts use...
Read more

v0.7.0

Choose a tag to compare

@dzikowski dzikowski released this 02 Apr 12:56
  • Language: Strict scope enforcement for bare args and ${…} interpolation — both bare identifier arguments (run greet(name)) and braced interpolation (log "hello ${name}") now enforce the same scope rules: the identifier must be a known const, capture, named parameter, or positional arg1arg9. Unknown names are rejected at compile time in both forms. The bare-argument error message no longer suggests ${name} as a workaround, since braced interpolation applies the same strict check. Compiler tests cover both unknown-bare-arg and unknown-${ident} cases.
  • E2E: Exercise examples/*.jh with strict assertions — e2e/tests/110_examples.sh now runs every example file through the full CLI pipeline with strict e2e::expect_stdout and artifact assertions, replacing the previous substring checks. Examples are tracked via three arrays (COVERED_RUN, COVERED_TEST, EXCLUDED) forming an example matrix; an orphan guard at the bottom fails CI if any examples/*.jh or examples/*.test.jh file is unaccounted for. ensure_ci_passes.jh is excluded (requires real npm run test:ci); async.jh is excluded (requires real agent backends). New coverage: ensure_ci_passes.test.jh via jaiph test. See Contributing — Example matrix guard.
  • Docs: Revisit docs/jaiph-skill.md — add match to the Concepts bullets (Rules and Workflows) and to the Steps reference section so the agent skill accurately describes all supported constructs. Cross-checked against docs/grammar.md, docs/architecture.md, docs/index.html, and src/cli/shared/usage.ts; no other drift found.
  • E2E: Detect and remove stale e2e samples — five .jh fixtures under e2e/ that were not referenced by any test script (e2e/tests/*.sh, e2e/test_all.sh) have been deleted: log_keyword.jh, log_keyword.test.jh, say_hello_json.jh, say_hello_json.test.jh, and sleepsort.jh. These files duplicated functionality already covered by wired-in tests and examples/, causing confusion about which samples were canonical. A new guard script e2e/check_orphan_samples.sh scans for unreferenced .jh and .test.jh files under e2e/ (checking both direct references in test scripts and indirect references via other .jh files). Run it manually with bash e2e/check_orphan_samples.sh or wire it into CI to prevent stale samples from accumulating. See Contributing — Orphan sample guard.
  • Docs: Single architecture source — remove root ARCHITECTURE.md; canonical architecture is docs/architecture.md only. README gains a short Core components section with a link to the full doc. Updated AGENT.md, QUEUE.md, e2e/lib/common.sh, and .jaiph/*.jh / .jaiph/*.sh orchestration prompts to reference docs/architecture.md.
  • Docs: Split concerns — docs/architecture.md focuses on how Jaiph is built (components, pipelines, contracts, diagrams, durable artifact layout as a runtime contract). JS kernel bullet now names individual modules (run-step-exec.ts, stream-parser.ts, schema.ts, mock.ts). E2E testing philosophy, normalization rules, and TypeScript test layout live in docs/contributing.md. Cross-links updated in docs/testing.md, docs/inbox.md, and README.md.
  • Testing: Golden AST tests for parse tree shape — a new test layer that locks in the AST structure produced by successful parses, so refactors cannot silently change tree shape. Each .jh fixture in golden-ast/fixtures/ is parsed and serialized to deterministic JSON (locations and file paths stripped, keys sorted) via serializeAstForTest() in src/golden-ast-runner.ts. The result is compared against a checked-in .json golden file in golden-ast/expected/. Initial fixtures cover 8 language features: brace-if, imports, log, match, params, prompt-capture, run-ensure, and script-defs. Run with npm run test:golden-ast; also wired into npm test. To regenerate goldens after an intentional parser change: UPDATE_GOLDEN=1 npm run test:golden-ast. See Testing — Golden AST tests for details.
  • Breaking — Language: Bare identifier arguments required; standalone "${identifier}" rejected — Call sites for run, ensure, if conditions, return run/return ensure, send … <- run, and const x = run … now require bare identifiers for passing in-scope variables: run greet(name) instead of run greet("${name}"). The compiler rejects a standalone "${identifier}" in call arguments with E_VALIDATE: do not use "${name}" in call arguments; use a bare identifier: ...(name). Quoted strings with additional text around the interpolation (e.g. "prefix_${name}") remain allowed since they cannot be expressed as bare identifiers. Bare identifier arguments must reference a known variable (const, capture, named parameter, or positional arg1arg9); unknown names produce E_VALIDATE. Jaiph keywords cannot be used as bare identifiers. log and logerr also accept a bare identifier form: log status expands to log "${status}" — the variable's value is logged. Migration: all .jh files, E2E fixtures, compiler tests, examples, and docs updated to use bare identifiers. Implementation: validateNoQuotedSingleInterpolation() in src/transpile/validate.ts, log bare identifier support in src/parse/steps.ts and src/format/emit.ts.
  • Breaking — Language: Parentheses required on all workflow and rule definitions — Every workflow and rule definition must include () before {, even when parameterless: workflow default() { … }, rule check() { … }. Omitting () is a parse error with a fix hint. Named parameters go inside the parentheses: workflow implement(task, role) { ... }, rule gate(path) { ... }. Parameter names follow identifier rules, must not be reserved keywords, and must be unique. At runtime, named parameters are bound alongside positional arg1arg9 (e.g. ${task} and ${arg1} both work). The compiler validates call-site arity when the callee declares parameters — a mismatch produces E_VALIDATE with an actionable message naming the expected parameters. When the callee has no declared parameters, no arity check is performed. The formatter emits () for empty parameter lists. Named parameters are the preferred style for new code. Migration: all .jh sources, E2E fixtures, compiler tests, examples, and docs updated to use () on every definition. Implementation: parseParamList() in src/parse/core.ts, params field on RuleDef and WorkflowDef in src/types.ts, collectKnownVars() and validateArity() in src/transpile/validate.ts, runtime binding in src/runtime/kernel/node-workflow-runtime.ts, formatter in src/format/emit.ts.
  • Breaking — Language: Scripts and run: braces out; strings or fences in — Named script definitions now use script name = <rhs> with three body forms: (1) single-line string script name = "body" (default shell runtime); (2) bare identifier script name = varName referencing an existing binding whose string value becomes the body; (3) fenced block script name = ``` ... ``` with optional lang tag (opening ``` on the same line as =). The { ... } brace-delimited script bodies, script:lang prefix forms, and the INTERPRETER_TAGS hardcoded allowlist are removed. Language selection now uses fence lang tags exclusively: ```<tag> maps to #!/usr/bin/env <tag> — any tag is valid (no hardcoded allowlist). If both a fence tag and a manual #! shebang are present, it is an error. Anonymous inline scripts now use body-after-parens syntax: run script() "body" or run script() ``` ... ``` — the previous body-inside-parens form run script("body") is removed. Inline scripts support fenced blocks with lang tags for polyglot one-liners (e.g. run script() ```python3 ... ```). const capture with inline scripts works with both body forms: const x = run script() "echo val" and const x = run script() ``` ... ```. The ScriptDef AST type replaces commands: string[] with body: string + optional lang?: string and gains bodyKind ("string" | "identifier" | "fenced") and bodyIdentifier fields. The ConstRhs inline script capture variant (kind: "run_inline_script_capture") accepts the new body position (body follows parens, not inside them). The formatter emits the new forms. Implementation: src/parse/scripts.ts, src/parse/inline-script.ts, src/parse/steps.ts, src/parse/const-rhs.ts, src/types.ts, src/format/emit.ts, src/transpile/emit-script.ts. All fixtures, compiler tests, E2E tests, and docs migrated.
  • Breaking — Language: Prompt body: single-line string, identifier, or fenced block — prompt now accepts three body forms: (1) single-line string literal prompt "text" with ${...} interpolation; (2) bare identifier prompt myVar referencing an existing binding; (3) fenced block prompt ``` ... ``` for multiline text (opening ``` on the same line as prompt). Multiline double-quoted strings are removed — a " with no closing quote on the same line now produces E_PARSE: "multiline prompt strings are no longer supported; use a fenced block instead". All three forms work in standalone, assignment capture (answer = prompt ...), and const capture (const x = prompt ...) positions. returns is recognized as a keyword only after a complete body form. AST types (WorkflowStepDef, ConstRhs) gain bodyKind ("string" | "identifier" | "fenced") and bodyIdentifier fields. The formatter emits identifier bodies as bare names and fenced bodies as ``` blocks. Fenced blocks use parseFencedBlock from src/parse/fence.ts. Implementation: src/parse/prompt.ts, src/parse/const-rhs.ts, src/types.ts, src/format/emit.ts. Compiler tests, E2E tests, and all internal .jh fixtur...
Read more

v0.6.0

Choose a tag to compare

@dzikowski dzikowski released this 30 Mar 20:00

Summary

  • Runtime: jaiph run and jaiph test execute through the Node workflow runtime (NodeWorkflowRuntime) only — no bash workflow transpilation on the execution path; Docker uses the same Node runner; jaiph build is no longer a user-facing command; jaiph test uses a pure Node test runner. Legacy jaiph_stdlib.sh workflow orchestration and user-facing workflow .sh output are gone from the runtime path.
  • Scripts vs orchestration: Bash, polyglot shebangs, and arbitrary shell live only in top-level script { } blocks (opaque bodies; the compiler does not parse them as Jaiph steps). Workflow and rule bodies are Jaiph steps only — no raw shell lines; rules may run scripts only (not workflows).
  • Breaking changes: Call sites require parentheses; definitions use rule / script / workflow without empty () before {; orchestration strings allow only ${identifier} interpolation; mock functionmock script; .jph removed — see the detailed entries below.

All changes

  • Fix: Treat script bodies as opaque bash — The compiler no longer parses script { … } bodies as Jaiph steps, rejects keywords like const or fail, strips outer quotes from commands, or validates cross-script calls line-by-line. All script bodies — bash and polyglot — are now opaque text to the compiler, which means embedded node -e heredocs, inline Python, const assignments in JS, and any other valid shell construct compile without interference. For bash scripts (no shebang or #!/usr/bin/env bash), the emitter still applies lightweight transforms: return normalization, local/export/readonly spacing, and import alias resolution (alias.namesymbol::name). Non-bash scripts (custom shebang) emit the body verbatim. Previously, line-based rules such as "const is not allowed in script bodies" caused false positives for real scripts where a line began with const inside embedded JS/Python.
  • CI: Verify landing-page samples (Playwright) — The docs-local CI job now builds Jaiph, installs Playwright (Chromium), and runs npx playwright test against the locally served Jekyll site. The Playwright suite (tests/e2e-samples/landing-page.spec.ts) extracts sample source and expected output from the landing page using data-sample DOM attributes, compares source blocks against checked-in examples/*.jh files, and runs deterministic samples through the CLI to verify that displayed output matches actual behavior. A new npm run test:samples script provides the same check locally. The landing page (docs/index.html) now carries stable data-sample, data-sample-file, data-sample-source, and data-sample-output attributes on each tab panel for reliable extraction.
  • CI: Getting started (local) — Jekyll smoke-check job — New CI job docs-local ("Getting started (local)") builds and serves the Jekyll documentation site locally on 127.0.0.1:4000 using Ruby 3.2 with bundler-cache, waits up to 30 seconds for the server to respond, then asserts HTTP 200 on / and /getting-started via curl. Runs on ubuntu-latest only (no OS matrix). The job has no dependency on jaiph.org — it validates the docs site entirely from the repository docs/ directory. Future tasks will extend this job with sample verification against the served HTML.
  • E2E: verify *.test.jh testing end-to-end (105_test_jh_verification.sh) — New dedicated E2E test that exercises jaiph test across four scenarios: (1) a representative passing test file using import, mock prompt, mock rule, mock workflow, mock script, and expectContain with full output verification; (2) a deliberately failing test asserting non-zero exit and the expected failure report format (expectEqual mismatch diff); (3) rejection of the deprecated mock function syntax with a migration error containing "mock function" is no longer supported; use "mock script"; (4) directory discovery (jaiph test <dir>) finding and running multiple *.test.jh files. Acts as a regression gate for the test runner contract after the mock functionmock script rename.
  • Fix: JAIPH_LIB not inherited from parent shellresolveRuntimeEnv now deletes JAIPH_LIB from the inherited environment before launching jaiph run or jaiph test, matching the existing treatment of JAIPH_SCRIPTS and JAIPH_RUN_STEP_MODULE. The Node runtime defaults JAIPH_LIB to <JAIPH_WORKSPACE>/.jaiph/lib when unset. Previously, a parent shell exporting JAIPH_LIB (pointing to a different workspace) would silently override the per-workspace default, causing source "$JAIPH_LIB/…" in scripts to resolve against the wrong project.
  • Fix: add JAIPH_WORKSPACE to unit test env for NodeWorkflowRuntime — The node-workflow-runtime.artifacts.test.ts recover-payload test now includes JAIPH_WORKSPACE in the env map passed to NodeWorkflowRuntime, matching the contract that resolveRuntimeEnv establishes for real CLI runs. Without it, workspace-dependent defaults (e.g. JAIPH_LIB) could resolve incorrectly in test isolation.
  • Breaking: definition/call syntax — no parens on defs, call parens required — Top-level definitions (rule, script, workflow) must not use parentheses after the identifier: rule name { … }, script name { … }, workflow name { … }. Forms with () on definitions (e.g. rule foo() { … }) are rejected at parse time with E_PARSE and a fix hint. Call sites now always require parentheses — either empty () for zero-argument calls or comma-separated arguments inside (...): ensure check(), run helper("arg1", "arg2"), run other_workflow(). This applies uniformly to run, ensure, run async, brace if conditions, inline capture interpolation (${run ref()}, ${ensure ref()}), and send RHS (channel <- run fmt()). The previous syntax that allowed bare calls without parentheses (e.g. ensure my_rule, run my_script "arg") is no longer accepted. This change makes arity visible at every call site and removes the misleading empty () on definitions that never declared real parameters. All internal .jh sources, fixtures, E2E tests, and documentation are migrated to the new shape. Supersedes the earlier "rule, script, and workflow require () before {" entry below.
  • Feat: inline capture interpolation ${run ...} / ${ensure ...} — Orchestration strings (log, logerr, fail, prompt, return, send literals, const RHS expressions) now support inline managed captures. Write log "Got: ${run some_script}" or log "Status: ${ensure check_ok}" instead of extracting one-time values into temporary variables. At runtime, each inline capture executes as a managed call and the result replaces the interpolation expression; regular ${var} interpolation applies after all captures resolve. If any inline capture fails, the enclosing step fails immediately. Validation: inline capture references follow the same rules as standalone run / ensure steps (correct target kind, no shell redirection). Nested inline captures (e.g. ${run foo ${run bar}}) and invalid references (e.g. ${run 123bad}) are rejected at compile time with E_PARSE. Covered by unit tests (validate-string.test.ts) and E2E tests (100_inline_capture_interpolation.sh) across runtime execution, failure propagation, mixed interpolation, jaiph test integration, and compile-time rejection of nested/invalid forms.
  • E2E: verify custom shebang polyglot scripts for Python and Nodee2e/tests/92_custom_shebang_polyglot.sh now exercises both #!/usr/bin/env python3 and #!/usr/bin/env node shebangs in separate sections. Each interpreter gets its own test environment and fixture (polyglot.jh for Python, polyglot_node.jh for Node), asserting full CLI stdout and artifact file counts. When an interpreter is not in PATH, its section is skipped with e2e::skip — the test no longer exits early on a missing python3, so both interpreters are tested independently.
  • E2E tests: migrate to full-output and full-artifact contracts — Converted 28 e2e::assert_contains calls across 8 test files (30_filesystem_side_effects.sh, 74_live_step_output.sh, 91_inbox_dispatch.sh, 92_log_logerr.sh, 93_inbox_stress.sh, 97_step_output_contract.sh, 98_ensure_recover_value.sh, 101_script_isolation.sh) to e2e::assert_equals with full expected content. Side-effect files, .out/.err artifacts, and inbox file contents now use exact-match assertions instead of substring checks. Remaining assert_contains usages are categorized as justified exceptions (nondeterministic output, unbounded logs, platform-dependent text) with inline comments, or tracked as convertible in docs/contributing.md. The default E2E contract — full equality on CLI stdout, artifact files, and inbox contents — is documented in ARCHITECTURE.md, docs/contributing.md, docs/testing.md, e2e/lib/common.sh, and the .jaiph/engineer.jh / .jaiph/qa.jh orchestration workflows.
  • Remove workflow bash transpilation and source maps — The compiler no longer emits workflow-level bash, build(), transpileFile(), or .jaiph.map sidecars. The only build output is buildScripts() → per-script files under scripts/ via emitScriptsForModule. Prompt returns schema validation (unsupported types, returns without capture) lives in validateReferences (validate-prompt-schema.ts). Tests and docs updated accordingly.
  • Breaking: rule, script, and workflow require () before { — Top-level definitions must use an empty parameter list and braces: rule name() { … }, script name() { … }, workflow name() { … }. Forms without () (e.g. rule foo { … }, workflow default { … }, or script bar { … }) are rejected at parse time with E_PARSE and a fix hint (e.g. rule declarations require parentheses: rule foo() { … }). Forms with () but with...
Read more