Conversation
The 0.14.0 stamp left follow-up work in Unreleased. Move the change list into 0.14.0 and keep only the Summary lines that are not already in that section. Co-authored-by: Cursor <cursoragent@cursor.com>
…ct-owner API
Invoke is POST /{name} with no /v1 prefix. Releases push ghcr.io/<owner>/jaiph-runtime.
The product-owner defs are the sole queue writers; pick prefers the first available task.
Co-authored-by: Cursor <cursoragent@cursor.com>
…rmat no-op on triple-quoted prompts. The caller-side copy is covered by the recover-path and stdin tasks; the new task locks jaiph format on a shebang const = prompt """ file. Co-authored-by: Cursor <cursoragent@cursor.com>
…queue store writable. Scripts were locking the default path on the read-only workspace mount; the start script now bind-mounts state and lock, and chowns the runs volume. Co-authored-by: Cursor <cursoragent@cursor.com>
The pages still described pre-0.14 env, match, credential, and version details. CLI --env help now matches the grant model. Co-authored-by: Cursor <cursoragent@cursor.com>
One fact has one owner page. The queue now has the four standalone cuts, and docs_parity no longer asks agents to copy inventories. Co-authored-by: Cursor <cursoragent@cursor.com>
A script step whose spawn fails now settles as a failed step instead of vanishing the run. spawnAndCapture catches a synchronous throw from spawn and settles it the same way as the asynchronous 'error' event: a status 1 step with the error on stderr, so the Promise never rejects. An E2BIG failure (argv plus env exceed the OS ARG_MAX) maps to a stable E_ARGV_TOO_LARGE marker that reports the attempted size; other spawn failures keep their existing text, including the missing-interpreter message. executeManagedStep converts a throw from the step body into a status 1 step and still writes the capture files and STEP_END, and runRoot emits RUN_END and stops the heartbeat in a finally, so a run that throws while running the def body still ends with a terminal RUN_END. Because the failed step ends status 1, a recover on that run now runs and run.recover_limit applies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Change catch (name) and recover (name) to bind name to the absolute path of the failed step's stdout capture (NNNNNN-*.out under JAIPH_RUN_DIR) instead of the merged stdout+stderr text. The capture file already exists on disk (empty when the step wrote no stdout) and stderr stays in the sibling .err with the same seq prefix, so the recovery body reads the log from the bound path rather than receiving it as argv — a multi-megabyte log can no longer overflow the next script's execve ARG_MAX. StepResult gains outFile/errFile, stamped by executeManagedStep, and runRecoverBody binds lastResult.outFile for every run target that can carry catch/recover (named script, inline script, def, async branch). The ban on `run foo() > file` and the absence of capture_to are unchanged. Updates docs (language, jaiph-skill), the in-repo callers that treated the binding as content, and the recover contract tests to assert path plus file contents. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an optional `stdin <expr>` clause to a standalone `run` of a script (named or inline), placed after `()` and before any `catch` / `recover`: `run save_string_to_file(path) stdin content`. The evaluated string is written to the child's stdin as UTF-8 instead of argv, so a payload larger than ARG_MAX (~1 MB on macOS) can be handed to a script for the first time. `stdin` is E_VALIDATE on a def / non-script target and E_PARSE on `run async`; trailing `>` / `>>` / `|` / `&` stay E_PARSE. `spawnAndCapture` grows a stdin parameter defaulting to `ignore`, so argv remains the default channel and is never auto-promoted. `.jaiph/lib_common.jh` `save_string_to_file` now reads `path = sys.argv[1]; content = sys.stdin.read()`, and its in-repo callers in `.jaiph/architect_review.jh` and `.jaiph/product_owner.jh` pass the body via stdin. Docs (language, grammar, skill) and the VS Code and Zed editor grammars highlight `stdin` as a `run` clause. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per design/0003 (one fact, one owner), grammar.md is the syntax owner and language.md the meaning owner, yet both re-hosted the same semantic tables for run/run async, catch/recover, prompt, const, return, send, log, if, match, for, interpolation, and step output — so the copies drifted and a single rule had to be edited twice. Delete those semantic tables from grammar.md, keeping every EBNF production (run_stmt, run_async_stmt, match_stmt, for_lines_stmt, param_list, return_stmt) and replacing each removed table with a one-sentence pointer to the owning language.md heading. This drops the body well under the new 380-line cap. Add integration/docs-grammar-language-split.test.ts to guard the cap, the surviving EBNF tokens, the language.md links, and that language.md still owns the match and run-async tables. Allowlist language.md in docs-structure.test.ts as the single-owner step-semantics reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per ADR 0003 (one fact, one owner), docs/mcp.md, docs/serve.md, docs/observability.md, and docs/agent-auth.md had grown into inventories that restate flag tables, MCP progress JSON dumps, OTLP/Sentry field lists, and --env essays already owned by cli.md, env-vars.md, and configuration.md. A single CLI change then forced a how-to sweep. Rewrite the four pages as recipes: goal, prerequisites, numbered step headings, Verification, and Related links. Each restated inventory is replaced by one sentence and a link to its owner. agent-auth.md keeps the credential names and `claude setup-token` literal as the recipe rather than a second env-vars table. Add integration/docs-how-to-caps. test.ts to enforce per-page body-line caps, the numbered-step and Verification headings, and an owner link, so a page that regrows an inventory fails the build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per ADR 0003 (one fact, one owner), the --env / sterile-script / use-grant rule was restated in cli.md's flag cell, why-jaiph.md commitment 2, language.md's Subprocess environment bullets, testing.md, and script-env.md. One grant-rule change forced an eight-file sweep. Collapse those pages to a sentence plus a link to env-vars.md#script-env, which stays the sole owner of the reserved-key list and the runner-allowlist essay: cli.md keeps the flag forms and E_ENV_* names only, why-jaiph.md keeps the not-a-sandbox commitment, language.md keeps the module-const-not-exported language, and script-env.md keeps the recipe. Add integration/docs-env-owner.test.ts to guard the cell length, the required links, and env-vars.md's ownership so the essay cannot grow back. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The skill is published independently for external projects, so owner pages must be absolute https://jaiph.org URLs rather than repo-relative paths. Co-authored-by: Cursor <cursoragent@cursor.com>
A first-agent file of that shape must stay bit-for-bit unchanged, including author margin and ${name} interpolation.
Co-authored-by: Cursor <cursoragent@cursor.com>
The leftover mcp/serve inventories are the last restated contracts; once they are gone, the next task lowers how-to/reference body caps. Co-authored-by: Cursor <cursoragent@cursor.com>
docs/mcp.md sections 3-7 and docs/serve.md steps 2-3/5 pasted the deriveTools rules, MCP protocol/progress/cancel/hot-reload contract, the serve endpoint catalog, and the auth-mode inventory that docs/cli.md already owns. Replace each with one sentence plus a link to cli.md#jaiph-mcp or cli.md#jaiph-serve, keeping the numbered steps, curl recipes, Verification, Related, and the explicit heading ids that cli.md and deploy.md link into. Lower the how-to caps to 80 (mcp.md) and 90 (serve.md) so a re-pasted inventory fails the test, and dequeue the completed task from QUEUE.md. Copies are now gone: a deriveTools or HTTP endpoint change edits cli.md only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The docs-structure test used a single 500-line body cap for every page, which no longer enforces the post-copy sizes the ADR 0003 cut achieved: a how-to could regrow an inventory and stay under 500. Replace it with per-Diátaxis caps — how-to 150, reference 350, and tutorial, explanation, and contributor 500 — with a fallback for pages lacking a diataxis value. Allowlist the single-owner inventory references cli.md and grammar.md alongside language.md, each with a one-line justification; no how-to is allowlisted. Trim testing.md and libraries.md back under 150 by cutting restated cli.md and env-vars.md inventories down to one sentence plus a link to the owner. Update agent-analyzability.md to describe the new caps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the run keyword and move stdin to a left-to-right connect form. Format in-repo orchestration to the current stdin clause while the compiler tasks land. Co-authored-by: Cursor <cursoragent@cursor.com>
Invoke is no longer a `run` keyword: it is a bare call — `save(path)`, `const x = save(path)`, `return save(path)`, `async save(path)`, and `` `echo hello`() ``. Stdin is the script-only connect form `stdin content -> save(path)`, not a `run` suffix or a shell pipe. Rewrite every `run`-as-keyword example and table in docs/language.md (the meaning owner) and update docs/grammar.md (the EBNF, reserved-word, and validation-catalog owner): drop `run` from the reserved-word list, add `stdin`, and define the `stdin_connect` production. Note that `run` is now an ordinary identifier while CLI `jaiph run` and `run.recover_limit` are unaffected. Catch/recover/allow_failure attach to the call or connect form rather than to a keyword. Extend the docs-split integration test to guard the reserved-word list, the stdin connect grammar, and the absence of any `run`-keyword invoke example. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the removed `run` invoke keyword with bare calls and the `stdin <expr> -> call()` connect form in every published page that still taught the old surface syntax: tutorials, how-tos, async.md, spec-async-handles.md, why-jaiph.md, architecture.md, the jaiph-skill checklist, and the index.html sample snippets. Per one-fact-one-owner, these pages point at language.md / grammar.md for the full grammar instead of restating it. CLI `jaiph run` examples and the `run.recover_limit` config key are left untouched. Regenerate the embedded copy of the skill (src/runtime/embedded-assets.ts) from the updated jaiph-skill.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the jaiph.org syntax highlighter in docs/assets/js/main.js to match the current language surface. `run` is removed from STATEMENT_KEYWORDS and `stdin` is added. The old `firstValue === "run"` callee special case is replaced with recognition of bare calls (`save(...)`), `async save(...)`, and `stdin operand -> save(...)` connects, marking the callee, operand, and arrow accordingly. CLI `jaiph run` in bash fences keeps its existing bash highlighting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the VS Code, Zed, and tree-sitter grammars so highlighting matches the current syntax. Bare call invokes (save(path), async save(path), `echo hello`()) and the stdin connect form (stdin content -> save(path)) now highlight, with stdin as a keyword and -> as the connect arrow. Drop run as a language keyword so it is no longer highlighted as keyword.control in .jh files, while leaving jaiph run as a CLI command in markdown/bash injections. Refresh grammar tests, fixtures, the tree-sitter corpus, and plugin READMEs accordingly.
Managed calls are save(path), async save(path), and `echo hi`(); stdin connect is `stdin content -> save(path)`. `run save(...)` is E_PARSE. Engineer commits without writing a format-patch. Ignore Playwright test-results and leftover root *.patch files. Co-authored-by: Cursor <cursoragent@cursor.com>
A typed prompt requires const; commit only needs the agent to make the commit, so drop the schema and unused message field. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the empty-string special case from the canonical say_hello sample so
empty and otherwise-invalid names share the single "You provided an invalid
name :(" failure from the _ arm. Update every published copy and the tests
that pinned the old message: the examples source and test, the landing page
tabs and their failure/failing-test output in docs/index.html, the inlined
greet.jh walkthrough in docs/first-agent-run.md, and the e2e output
fixtures. The intentional-miss demo test now expects "You provided an
invalid name" against the ":("-suffixed actual. Also drop the completed
task from QUEUE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend all three highlighters so the callee of any call — a name immediately followed by `(` — is painted as a function, not only at the start of a statement. This covers def calls (check_deps(...)), qualified calls (helpers.scan(), last segment is the function), and expression-position calls (const status = setup_env()). VS Code scopes the callee entity.name.function.jaiph, with a qualified callee also getting entity.name.namespace.jaiph on the alias. Zed captures the callee @function instead of @variable. The docs highlighter applies the same paren rule anywhere on a line and wraps the callee in its ralph-identifier span, and gains its first unit test driving the pure highlighter over .jh snippets under Node. run stays a non-keyword and never picks up call or function scope on its own, and a keyword before `(` such as catch (err) stays a keyword. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A script/def/prompt call now returns an output handle rather than a
string. The call still runs at its call site (lazy slurp, not lazy
execution), but its bytes stay on disk or in a pipe until a force site
reads them into a JavaScript string. Force sites are const binding, an
if/match subject, ${} interpolation, call arguments, log/logerr/logwarn,
and a prompt body that references a handle; a bare statement call,
stdin <handle-or-call> -> script(), and printing an entry def's return
keep the handle without pulling bytes through V8.
spawnAndCapture no longer concatenates each chunk into a JS string, so a
statement call or stdin stream that moves 64 MiB does not grow jaiph's
resident memory to match. StepResult gains valueFile and a streamed
flag; NodeWorkflowRuntime gains the handle registry, forceValue,
createResolvedHandle, and a disk-backed StdinSource. recover/catch
bindings are now this same handle over the failed step's stdout instead
of a leaked run-dir path string, so logerr "${failure}" slurps contents
and stdin failure -> tail_log() streams.
Documents the new value type in docs/language.md (Value types owner) with
the force/keep table, adds the one-sentence pointer to docs/jaiph-skill.md
and docs/why-jaiph.md, and adds slurp/RSS and docs-contract tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the stdin connect so a producer can be a call (def or script) and so several script stages chain with -> into a pipeline, as in stdin gen() -> upper() -> count(). The producer left of the first -> is a value or a call to a def or script; every stage after it is a script (named or inline). Each stage's output handle streams into the next stage's stdin, each stage is its own step in the progress tree, and the first non-zero stage stops the pipeline. A const or return slurps the last stage, so it should reduce. A pipeline (a call producer, or two or more -> stages) rejects recover (E_PARSE) but accepts a one-shot catch; a def in a consumer slot is E_VALIDATE, and async with stdin stays E_PARSE. The plain stdin <value> -> script() connect form is unchanged and keeps recover. Add the agent-free examples/stream.jh landing sample that generates lines, uppercases them, and reduces to a line count, wired into the docs/index.html Samples tab and pinned in e2e/tests/110_examples.sh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stream the CI recover log through stdin instead of treating the binding as a capture path. Co-authored-by: Cursor <cursoragent@cursor.com>
The script stages of a stdin pipeline (`stdin gen() -> upper() -> count()`) now run concurrently through bounded, backpressured PassThrough buffers instead of running each stage to completion, collecting its whole stdout into a JavaScript string, and only then spawning the next stage. Every script stage spawns in one tick and its live stdout is piped into the next stage's stdin, so producer and consumer overlap — a producer that prints `start`, sleeps, then prints `end` lets the consumer see `start` during the sleep. A def producer runs to completion first and streams its on-disk handle in; a value producer resolves to bytes. No uncaptured stage's body is slurped into memory or spooled to a runtime-owned temp file and reread, so a 64 MiB payload flows through without growing resident memory by the payload size, while a stage's `.out` capture is written straight to disk. A `const`/`return` still slurps the last stage (a reduce). The progress tree now nests each downstream stage under the one feeding it. `runPipelineStage` gains an `onSpawn` hook forwarded by `spawnAndCapture`, `executeScript`, and `executeInlineScript`; `StdinSource` gains a `stream` kind for a live upstream stdout. New overlap/volume e2e (153) plus runtime and slurp tests fail if the runtime concatenates the payload or serializes the stages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A stdin pipeline `stdin <producer> -> stage() -> stage()` now paints every connect arrow and every stage callee across all three highlighters, not just the first hop. The VS Code stdin-connect rule was a single match that captured only the first `->` and the first consumer, leaving the second arrow of `stdin gen() -> upper() -> count()` unstyled; it is now a begin/end block spanning the rest of the line, so every `->` scopes keyword.operator.send.jaiph, every callee scopes entity.name.function.jaiph, and `stdin` stays a command keyword. Zed and the landing tokenizer already painted later hops, so each gains a regression test pinning the multi-hop line. The one-hop `stdin status -> shout(task)`, `send "x" -> inbox`, and `channel findings -> handler` keep their existing scopes. Highlight-only: no change to the parser, validator, or runtime. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recover/catch bindings are stdout then stderr; prompt x / prompt ${x} pipe the handle file into the agent instead of slurping it.
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire each non-terminal pipeline stage's live stdout file descriptor directly into the next stage's stdin at spawn time instead of teeing through a JS-heap PassThrough. Bytes now flow kernel-to-kernel, so no stage spools its full body and peak RSS stays independent of the payload size. Update the slurp and stdin pipeline tests to assert the inherited-fd contract (intermediate stages no longer spool to .out). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gate each pipeline stage's STEP_END emit on the upstream stage's completion so the progress tree reports completions in pipeline order regardless of how child process close events race across platforms. Stages still run fully concurrently; only the STEP_END emit waits, and elapsed_ms is captured before the gate so durations stay accurate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bun cannot inherit a live stream.Readable without an underlying fd as a child's stdio[0]. Detect the bun runtime and fall back to spawning stdin as `pipe`, relaying the upstream stage's stdout into the child via a backpressured `.pipe()` (still streamed, bounded RSS). Node keeps the kernel-to-kernel fd inheritance. Also drain the upstream stream on a failed spawn so it reaches EOF and does not hang. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Middle stages were handing the pipe away so their .out stayed empty. Copy each chunk to the audit file and the next stage without holding the full body in memory. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When two runs share a source file and land in the same UTC second, the second run's dir is disambiguated to `<time>-volume.jh-<suffix>`. Widen the gen `.out` glob to allow the optional suffix so both captures are found reliably. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-liners are single quotes with no escapes so Markdown code fences stay intact; a body that needs a quote uses a block. Co-authored-by: Cursor <cursoragent@cursor.com>
Host run artifacts are not build inputs and can add hundreds of megabytes to the standalone copy. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Loopback serve matches stdio mcp. --allow-anonymous is only for off-loopback binds. Landing sample is stdin.jh. Co-authored-by: Cursor <cursoragent@cursor.com>
Format now indents triple-quoted prompts like fenced scripts and keeps
stdin name / stdin name.field instead of rewriting them to "${…}". Refresh
the skill checklist and pin its example as format-canonical.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align the homepage and agent skill around polyglot workflows, structured prompt output, CLI/MCP/HTTP serving, and durable run artifacts. Co-authored-by: Cursor <cursoragent@cursor.com>
jaiph init copies docs/jaiph-skill.md, whose title is no longer "Jaiph Skill (for Agents)". Co-authored-by: Cursor <cursoragent@cursor.com>
Clarify that Jaiph turns prompts and scripts across languages into reusable, testable workflows instead of one-off agent command chains. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
'…'and'''…''', so examples no longer break CommonMark fences. Backticks are gone.run:runis no longer a keyword. Call a workflow or script by writing its name:save(path).stdin gen() -> upper() -> count()runs the stages together and passes bytes through pipes. A large payload does not sit in RAM between steps./v1(POST /{name}). No auth unless you set a token or OIDC — same asjaiph mcp.--allow-anonymousis only needed to bind off-loopback without auth. The runtime image publishes to GHCR.