From d6f2f5017c436f7079edbebf0cef423759b2566f Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Sun, 21 Jun 2026 18:28:18 +0300 Subject: [PATCH 1/6] docs(roadmap): mark 1.AH Done (PR #38) across the status surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #38 merged — the Phase-1-doable half of 1.AH (the four generative media-output adapters OpenAI-TTS/Gemini-Imagen/OpenAI-Sora/Gemini-Veo behind the seam + the shared opaque-jobId codec + the bare-MIME validator) is done. Reconcile the status docs (roadmap-done-after-merge): - current.md: 1.AH Phase-1-doable half ✅ Done (PR #38); Phase 1 complete; the host-wiring half is distributed across Phases 2–6 as recorded tasks. Replaced both "only 1.AH remains" status lines. - CLAUDE.md: both status paragraphs — 1.AH ✅ Done; Phase 1 complete. - README.md: the multimodal-sub-spine status line — 1.AH ✅ (PR #38), 1.m6 / Phase 1 complete. - phase-1-engine-and-llm.md: the §1.AH bullet (Phase-1-doable half ✅ Done), the 1.m6 milestone row (1.AH ✅), and the dependency table row (adapters Done; host-wiring spans Phases 2–6). - deferred-tasks.md: flip the Imagen/TTS/knobs (A1/A2/A6) and Sora/Veo (A3/A4) adapter entries to ✅ Done, with the N4 opacity obligation discharged; count>1, verified pricing rows, the host-wiring D-mechanisms, A5, and the Sora deprecation stay deferred. A5 (save_to url double-fetch) remains deferred; OpenAI Sora 2 is vendor-deprecated (2026-09-24, A3-arm only). prettier clean; Leakwatch 0. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 4 +-- README.md | 2 +- docs/roadmap/current.md | 8 +++--- docs/roadmap/deferred-tasks.md | 25 ++++++++++--------- docs/roadmap/phases/phase-1-engine-and-llm.md | 6 ++--- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9d892808..29e8681a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ A run executes in one of **three execution modes** behind the one `LLMProvider` engine is identical across all three. See [ADR-0012](docs/decisions/0012-managed-inference-dual-mode.md) to [ADR-0015](docs/decisions/0015-managed-mode-data-handling-and-compliance.md) and [docs/architecture/managed-inference.md](docs/architecture/managed-inference.md). -**Status:** Phase 1 in progress — milestone M1 (LLM seam proven) reached (PR #9, 2026-06-07); the `FallbackChain` runner (1.K) landed, completing 1.m2 with the cost tracker (PR #13, 2026-06-11); the run loop (1.N — `WorkflowEngine` + `RunEventBus`) landed (PR #17, 2026-06-13) **completing 1.m3** (parse → DAG → run loop emits the canonical event stream), with the built-in `ToolRegistry` (1.T, a 1.m4 component) landing alongside it as the other `AgentRunner` (1.O) join prerequisite; the **`AgentRunner` (1.O) — per-node LLM execution behind the seam — landed (PR #18, 2026-06-14)**; and the **node-type handlers (1.P) — the six non-agent `NodeExecutor` arms (condition / transform / fan_out / fan_in / input / output) behind a dispatching executor — landed (PR #20, 2026-06-14)**; and **checkpoint/resume (1.R) + the human gate (1.Q) landed (PR #22, 2026-06-15)** — the derived `Checkpointer` + cross-process `resumeFromCheckpoint`, and the `human_in_the_loop` gate with the one-shot timeout port; and **node retry (1.S) — the above-chain whole-node retry budget ([ADR-0040](docs/decisions/0040-node-retry-budget-above-the-chain.md), amending ADR-0038) — landed (PR #24, 2026-06-15)**, re-dispatching a whole node on a retryable failure up to `retry.max` attempts (with `node:retrying`, abort-aware backoff, and `retry_on` filtering), with retry-from-node (ADR-0040 Part B) deferred to Phase-2; and the **pre-egress budget governor (1.AC, [ADR-0028](docs/decisions/0028-workflow-resource-governance.md)) + the `AgentSession` agent-first entry point (1.V, [ADR-0024](docs/decisions/0024-agent-first-entry-point-agentsession.md)) landed together (PR #26, 2026-06-16)** — 1.AC was the last 1.m4 component, so **1.m4 is complete** (the full engine stack: node handlers, gate, checkpoint/resume, retry, tools, sandbox, budget governor), and 1.V opens the Lane-C agent-first sub-spine (1.m5); then the **end-to-end Node harness (1.U) landed (PR #27, 2026-06-16), reaching 🎯 M2** — the engine runs end-to-end (live streaming + per-node-boundary checkpointing + cross-process resume + node retry + provider failover, gap-free), **completing the Phase-1 engine critical path**. The remaining Phase-1 work is additive and off the critical path (Lane C: the **`session:*` namespace (1.W) landed (PR #28, 2026-06-17)** — the `SessionEventSink`→`RunEventBus` adapter + per-session `sequenceNumber`, the `SessionHandle`, and the combined `RunOrSessionEventSchema` gate — and **session persistence (1.X) landed (PR #29, 2026-06-17)** — the `agent_sessions`/`session_messages` tables + migration, `SessionMessageSchema`/`AgentSessionSchema`, and the `SessionStore` + domain↔row mappers (data-layer only); then **session checkpoint/resume (1.Y) + export-to-workflow (1.Z) landed (PR #30, 2026-06-17)** — `reconstructSessionState`/`AgentSession.resume` (reload-not-replay; preload the text-only transcript, re-seed turnCount/cost, no `session:started` re-emit) + the `serializeWorkflow`/`sessionToWorkflow` pair (one agent node per completed turn, transcript in `metadata`, secret/signature exclusion structural) — leaving only the **1.AA** chat-regression harness ‖ the 1.m6 multimodal sub-spine, whose first step — **media-input adapters + the shared SSRF policy primitive (1.AE) — landed (PR #32, 2026-06-18)** (base64 image/audio across the three adapters behind per-modality `assertMediaCapabilities`, and the one `@relavium/shared` SSRF policy primitive reused by the provider-`baseURL` + `http_request` callers; the SSRF *mechanism* half + per-modality FallbackChain gating moved into **1.AF**). **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20):** the `MediaStore`/`deInlineMedia` choke point (the active I3 enforcement), per-modality capability gating (`mediaSupportReason`/`requestSupportReason` — replacing coarse vision), the `media_objects`/`media_references` tables, and ADR-0042/0043/0044 (P1+P2, PR #33); the binary media-egress + the **SSRF mechanism half** (D9 — the `MediaUrlFetch` re-host hook + the SSRF-validated `fetchMediaBytes` wired at the choke point), the `FallbackChain` resolve-before-egress + the **byte-free re-materialization sidecar** (D8/D7), and the byte-delivery **`Range` gate** (D13) (P3 + P4/D13, PR #34); `read_media` + the scope-set authz (D12), the terminal sweep + grace-window GC (D11), `save_to` (D16), the `output_modalities` load-check (D15), and the per-modality media cost governor (D17) (P4 remainder, PR #35); plus a **13-dimension end-to-end review + an external Sonar/CodeRabbit pass** (PR #36 — H1 `agent:tool_call.toolInput` byte-redaction, H2 the unified exact-membership output-combination gate, the GC grace-basis fix, the `nodeMediaEgressDeps` egress-mechanism test). Each phase passed a dedicated adversarial security-review (0 blockers/highs). The deferred **host-wiring** half (D12 `MediaReadAccess` + session-scope population, the D15 loader, the D17/`resolveForEgress` config) + the keychain no-raw-key IPC test are owned by **1.AH** (recorded in deferred-tasks.md). **1.AG (media output generation) is ✅ Done (PR #37, 2026-06-21):** inline media-out via `generate()` (Section B, [ADR-0046](docs/decisions/0046-inline-media-out-via-generate-streaming-triad-deferred.md)), sync `generateMedia` + `media_surface` routing (C), the engine-owned async media-job **poll/checkpoint/resume/cancel LRO** (D, [ADR-0045](docs/decisions/0045-async-media-job-loop-poll-checkpoint-resume-cancel.md)), and acceptance + generative conformance (E); the five sections were each dual-reviewed (Opus+Sonnet), then a final Opus-consolidated pre-merge review caught + fixed two HIGH cross-fix defects (the orphaned-vertex cost addend + the crash-in-window `run:paused`). The genuinely-remaining vendor-adapter (Sora/Veo/OpenAI-TTS/Imagen) + host-wiring work is **1.AH**. The remaining Phase-1 work is **1.AH**; **Phase 2 (CLI, M3) is unblocked**. +**Status:** Phase 1 in progress — milestone M1 (LLM seam proven) reached (PR #9, 2026-06-07); the `FallbackChain` runner (1.K) landed, completing 1.m2 with the cost tracker (PR #13, 2026-06-11); the run loop (1.N — `WorkflowEngine` + `RunEventBus`) landed (PR #17, 2026-06-13) **completing 1.m3** (parse → DAG → run loop emits the canonical event stream), with the built-in `ToolRegistry` (1.T, a 1.m4 component) landing alongside it as the other `AgentRunner` (1.O) join prerequisite; the **`AgentRunner` (1.O) — per-node LLM execution behind the seam — landed (PR #18, 2026-06-14)**; and the **node-type handlers (1.P) — the six non-agent `NodeExecutor` arms (condition / transform / fan_out / fan_in / input / output) behind a dispatching executor — landed (PR #20, 2026-06-14)**; and **checkpoint/resume (1.R) + the human gate (1.Q) landed (PR #22, 2026-06-15)** — the derived `Checkpointer` + cross-process `resumeFromCheckpoint`, and the `human_in_the_loop` gate with the one-shot timeout port; and **node retry (1.S) — the above-chain whole-node retry budget ([ADR-0040](docs/decisions/0040-node-retry-budget-above-the-chain.md), amending ADR-0038) — landed (PR #24, 2026-06-15)**, re-dispatching a whole node on a retryable failure up to `retry.max` attempts (with `node:retrying`, abort-aware backoff, and `retry_on` filtering), with retry-from-node (ADR-0040 Part B) deferred to Phase-2; and the **pre-egress budget governor (1.AC, [ADR-0028](docs/decisions/0028-workflow-resource-governance.md)) + the `AgentSession` agent-first entry point (1.V, [ADR-0024](docs/decisions/0024-agent-first-entry-point-agentsession.md)) landed together (PR #26, 2026-06-16)** — 1.AC was the last 1.m4 component, so **1.m4 is complete** (the full engine stack: node handlers, gate, checkpoint/resume, retry, tools, sandbox, budget governor), and 1.V opens the Lane-C agent-first sub-spine (1.m5); then the **end-to-end Node harness (1.U) landed (PR #27, 2026-06-16), reaching 🎯 M2** — the engine runs end-to-end (live streaming + per-node-boundary checkpointing + cross-process resume + node retry + provider failover, gap-free), **completing the Phase-1 engine critical path**. The remaining Phase-1 work is additive and off the critical path (Lane C: the **`session:*` namespace (1.W) landed (PR #28, 2026-06-17)** — the `SessionEventSink`→`RunEventBus` adapter + per-session `sequenceNumber`, the `SessionHandle`, and the combined `RunOrSessionEventSchema` gate — and **session persistence (1.X) landed (PR #29, 2026-06-17)** — the `agent_sessions`/`session_messages` tables + migration, `SessionMessageSchema`/`AgentSessionSchema`, and the `SessionStore` + domain↔row mappers (data-layer only); then **session checkpoint/resume (1.Y) + export-to-workflow (1.Z) landed (PR #30, 2026-06-17)** — `reconstructSessionState`/`AgentSession.resume` (reload-not-replay; preload the text-only transcript, re-seed turnCount/cost, no `session:started` re-emit) + the `serializeWorkflow`/`sessionToWorkflow` pair (one agent node per completed turn, transcript in `metadata`, secret/signature exclusion structural) — leaving only the **1.AA** chat-regression harness ‖ the 1.m6 multimodal sub-spine, whose first step — **media-input adapters + the shared SSRF policy primitive (1.AE) — landed (PR #32, 2026-06-18)** (base64 image/audio across the three adapters behind per-modality `assertMediaCapabilities`, and the one `@relavium/shared` SSRF policy primitive reused by the provider-`baseURL` + `http_request` callers; the SSRF *mechanism* half + per-modality FallbackChain gating moved into **1.AF**). **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20):** the `MediaStore`/`deInlineMedia` choke point (the active I3 enforcement), per-modality capability gating (`mediaSupportReason`/`requestSupportReason` — replacing coarse vision), the `media_objects`/`media_references` tables, and ADR-0042/0043/0044 (P1+P2, PR #33); the binary media-egress + the **SSRF mechanism half** (D9 — the `MediaUrlFetch` re-host hook + the SSRF-validated `fetchMediaBytes` wired at the choke point), the `FallbackChain` resolve-before-egress + the **byte-free re-materialization sidecar** (D8/D7), and the byte-delivery **`Range` gate** (D13) (P3 + P4/D13, PR #34); `read_media` + the scope-set authz (D12), the terminal sweep + grace-window GC (D11), `save_to` (D16), the `output_modalities` load-check (D15), and the per-modality media cost governor (D17) (P4 remainder, PR #35); plus a **13-dimension end-to-end review + an external Sonar/CodeRabbit pass** (PR #36 — H1 `agent:tool_call.toolInput` byte-redaction, H2 the unified exact-membership output-combination gate, the GC grace-basis fix, the `nodeMediaEgressDeps` egress-mechanism test). Each phase passed a dedicated adversarial security-review (0 blockers/highs). The deferred **host-wiring** half (D12 `MediaReadAccess` + session-scope population, the D15 loader, the D17/`resolveForEgress` config) + the keychain no-raw-key IPC test are owned by **1.AH** (recorded in deferred-tasks.md). **1.AG (media output generation) is ✅ Done (PR #37, 2026-06-21):** inline media-out via `generate()` (Section B, [ADR-0046](docs/decisions/0046-inline-media-out-via-generate-streaming-triad-deferred.md)), sync `generateMedia` + `media_surface` routing (C), the engine-owned async media-job **poll/checkpoint/resume/cancel LRO** (D, [ADR-0045](docs/decisions/0045-async-media-job-loop-poll-checkpoint-resume-cancel.md)), and acceptance + generative conformance (E); the five sections were each dual-reviewed (Opus+Sonnet), then a final Opus-consolidated pre-merge review caught + fixed two HIGH cross-fix defects (the orphaned-vertex cost addend + the crash-in-window `run:paused`). **1.AH — the four generative media-output adapters (OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo) behind the seam + the shared opaque-jobId codec + the bare-MIME validator — is ✅ Done (PR #38, 2026-06-21)** (each adapter dual/Opus-reviewed; the `save_to` url-double-fetch (A5) deferred; OpenAI Sora 2 is vendor-deprecated [shutdown 2026-09-24] — A3-arm only, recorded in deferred-tasks.md). 1.AH's remaining **host-wiring half is distributed across Phases 2–6** as recorded tasks (CLI §2.S, desktop §3.B, VS Code §4.N, managed/cloud media). **Phase 1 is complete; Phase 2 (CLI, M3) is unblocked.** Phase 0 (M0, 2026-06-04) landed the monorepo, strict toolchain + CI, `@relavium/shared` (the full Zod contract set), the no-vendor-type seam fence, and `@relavium/db`. Phase 1 has since landed `@relavium/llm` — the `LLMProvider` seam + all three adapters (Anthropic, OpenAI/DeepSeek, @@ -64,7 +64,7 @@ The pre-egress budget governor (1.AC) + the agent-first `AgentSession` (1.V) lan **completing 1.m4**; then the end-to-end Node harness (1.U) landed (PR #27, 2026-06-16) **reaching M2** — the Phase-1 engine critical path is complete. The additive Lane-C agent-first sub-spine is now **complete** (session events **1.W ✅ (PR #28)** + persistence **1.X ✅ (PR #29)** + checkpoint/resume **1.Y** & export **1.Z ✅ (PR #30, 2026-06-17)** + the **1.AA** chat-regression harness ✅ (2026-06-17), closing **1.m5**); -on the 1.m6 multimodal sub-spine, **media-input adapters + the shared SSRF policy primitive (1.AE) landed (PR #32, 2026-06-18)** — after a multi-round + final 8-dimension adversarial review (no SSRF bypass found); the SSRF *mechanism* half + per-modality gating moved into 1.AF. **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20)** — P1+P2 (PR #33); the media-egress + SSRF mechanism half (D9), the `FallbackChain` resolve-before-egress + byte-free re-materialization sidecar (D8/D7), the byte-delivery `Range` gate (D13), and a clean P3 egress/SSRF security-review (P3 + P4/D13, PR #34); D12 `read_media` + authz, D11 GC sweep, D16 `save_to`, D15 load-check, D17 cost governor (P4 remainder, PR #35); and a 13-dimension end-to-end + external Sonar/CodeRabbit review (PR #36 — toolInput byte-redaction, the unified exact-membership output gate, the GC grace-basis fix, the egress-mechanism test). The host-wiring half (D12/D15/D17 mechanism) + the keychain IPC test are deferred to **1.AH**. **1.AG (output generation) is ✅ Done (PR #37, 2026-06-21)** — inline media-out (B) + sync `generateMedia`/routing (C) + the async media-job LRO (D, ADR-0045) + acceptance/conformance (E), each section dual-reviewed plus a final Opus-consolidated pre-merge review (two HIGH cross-fix defects caught + fixed). The remaining Phase-1 work is 1.AH; Phase 2 (CLI) is unblocked. See +on the 1.m6 multimodal sub-spine, **media-input adapters + the shared SSRF policy primitive (1.AE) landed (PR #32, 2026-06-18)** — after a multi-round + final 8-dimension adversarial review (no SSRF bypass found); the SSRF *mechanism* half + per-modality gating moved into 1.AF. **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20)** — P1+P2 (PR #33); the media-egress + SSRF mechanism half (D9), the `FallbackChain` resolve-before-egress + byte-free re-materialization sidecar (D8/D7), the byte-delivery `Range` gate (D13), and a clean P3 egress/SSRF security-review (P3 + P4/D13, PR #34); D12 `read_media` + authz, D11 GC sweep, D16 `save_to`, D15 load-check, D17 cost governor (P4 remainder, PR #35); and a 13-dimension end-to-end + external Sonar/CodeRabbit review (PR #36 — toolInput byte-redaction, the unified exact-membership output gate, the GC grace-basis fix, the egress-mechanism test). The host-wiring half (D12/D15/D17 mechanism) + the keychain IPC test are deferred to **1.AH**. **1.AG (output generation) is ✅ Done (PR #37, 2026-06-21)** — inline media-out (B) + sync `generateMedia`/routing (C) + the async media-job LRO (D, ADR-0045) + acceptance/conformance (E), each section dual-reviewed plus a final Opus-consolidated pre-merge review (two HIGH cross-fix defects caught + fixed). **1.AH (the four generative media-output adapters — OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo — behind the seam + the shared opaque-jobId codec + the bare-MIME validator) is ✅ Done (PR #38, 2026-06-21)**, each adapter dual/Opus-reviewed (A5 `save_to` double-fetch deferred; OpenAI Sora 2 vendor-deprecated, shutdown 2026-09-24, A3-arm only). **Phase 1 is complete** — 1.AH's host-wiring half is distributed across Phases 2–6 as recorded tasks; **Phase 2 (CLI) is unblocked**. See [docs/roadmap/current.md](docs/roadmap/current.md). See [README.md](README.md) for the public overview. ## Non-negotiable rules for AI agents diff --git a/README.md b/README.md index f5f49617..8534baed 100644 --- a/README.md +++ b/README.md @@ -53,5 +53,5 @@ both landed in **PR #26 (2026-06-16)**. With the budget governor in, **milestone end-to-end (live streaming + checkpoint + cross-process resume + retry + provider failover), completing the Phase-1 engine critical path. The remaining Phase-1 work is additive (the agent-first sub-spine — **session events 1.W ✅ (PR #28)** + **persistence 1.X ✅ (PR #29)** + **session checkpoint/resume 1.Y & export-to-workflow 1.Z ✅ (PR #30, 2026-06-17)** + **the 1.AA chat-regression harness ✅ (2026-06-17)**, completing **1.m5** — and the -multimodal sub-spine — **media-input adapters + the shared SSRF policy primitive 1.AE ✅ (PR #32)**, **engine media plumbing 1.AF ✅ (PR #33/#34/#35/#36, 2026-06-20)**, and **media output generation 1.AG ✅ (PR #37, 2026-06-21)** — landed; only 1.AH remains); **Phase 2 (the CLI) is unblocked**. See +multimodal sub-spine — **media-input adapters + the shared SSRF policy primitive 1.AE ✅ (PR #32)**, **engine media plumbing 1.AF ✅ (PR #33/#34/#35/#36, 2026-06-20)**, **media output generation 1.AG ✅ (PR #37, 2026-06-21)**, and **the generative media-output adapters 1.AH ✅ (PR #38, 2026-06-21)** — all landed, **completing 1.m6 / Phase 1**; 1.AH's host-wiring half is distributed across Phases 2–6 as recorded tasks); **Phase 2 (the CLI) is unblocked**. See [docs/roadmap/current.md](docs/roadmap/current.md) for live status. diff --git a/docs/roadmap/current.md b/docs/roadmap/current.md index 741b547c..d5a2b991 100644 --- a/docs/roadmap/current.md +++ b/docs/roadmap/current.md @@ -184,7 +184,7 @@ harness is now ✅ Done (2026-06-17), completing **1.m5**; cost-event persistenc > non-breaking; the seam doc carries the full amendment section. **1.AE–1.AH (media > input/engine/output + surfaces) are additive and do NOT gate M2** — the seam lane ran straight to > **1.K** (✅ Done, PR #13), which closed it. **1.AE (media-input adapters + the shared SSRF policy -> primitive) is now ✅ Done (PR #32, 2026-06-18)**; **1.AF (engine media plumbing) is now ✅ Done (PR #33/#34/#35/#36, 2026-06-20)**; **1.AG (output generation) is now ✅ Done (PR #37, 2026-06-21)** (ADR-0045/0046; each section dual-reviewed + a final Opus-consolidated pre-merge review); **only 1.AH remains** on the 1.m6 sub-spine. +> primitive) is now ✅ Done (PR #32, 2026-06-18)**; **1.AF (engine media plumbing) is now ✅ Done (PR #33/#34/#35/#36, 2026-06-20)**; **1.AG (output generation) is now ✅ Done (PR #37, 2026-06-21)** (ADR-0045/0046; each section dual-reviewed + a final Opus-consolidated pre-merge review); **1.AH (the Phase-1-doable half — the four generative media-output adapters OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo behind the seam + the shared opaque-jobId codec + the bare-MIME validator) is now ✅ Done (PR #38, 2026-06-21)** (each adapter dual/Opus-reviewed; A5 save_to-double-fetch deferred). **The 1.m6 sub-spine's Phase-1 work is complete** — 1.AH's remaining **host-wiring half is distributed across Phases 2–6** as recorded tasks (CLI §2.S, desktop §3.B, VS Code §4.N, managed/cloud media bullets), so it no longer gates Phase 1. *(Note: OpenAI Sora 2 + the Videos API are vendor-deprecated, shutdown 2026-09-24 — recorded in deferred-tasks.md; impact is the A3 adapter arm only.)* > **Review-pass follow-ups landed (PR #12, merged 2026-06-11).** The 2026-06-10 engine/tooling > review pass landed as docs/decisions only — no Phase-1 workstream changed: **MCP client scheduling** @@ -247,8 +247,10 @@ harness is now ✅ Done (2026-06-17), completing **1.m5**; cost-event persistenc > pass — H1 `agent:tool_call.toolInput` byte-redaction, H2 the unified exact-membership `output_modalities` gate > (+ bidirectional set-equality), the GC grace basis bumped to de-reference time, an egress-mechanism test for > `nodeMediaEgressDeps`, and the canonical-home doc-drift cluster. All green + Leakwatch-clean. The remaining -> Phase-1 work is **additive and off the critical path**: only **1.AH** (surfaces & managed mode) remains before -> Phase 1 closes. **Phase 2 (CLI, milestone M3) is unblocked.** +> Phase-1 work is **additive and off the critical path**: **1.AH's Phase-1-doable half (the four generative +> media-output adapters + shared opaque-jobId codec + bare-MIME validator) is ✅ Done (PR #38, 2026-06-21)**, +> closing the 1.m6 sub-spine's Phase-1 work — **Phase 1 is complete**. 1.AH's remaining host-wiring half is +> distributed across Phases 2–6 as recorded tasks (no longer a Phase-1 item). **Phase 2 (CLI, milestone M3) is unblocked.** Carry-over hardening is tracked in [deferred-tasks.md](deferred-tasks.md) — pick items up as Phase 1 first touches each file. diff --git a/docs/roadmap/deferred-tasks.md b/docs/roadmap/deferred-tasks.md index 4a2bc929..2deab600 100644 --- a/docs/roadmap/deferred-tasks.md +++ b/docs/roadmap/deferred-tasks.md @@ -177,7 +177,7 @@ Severity is the review's verified rating. Check an item off in the PR that resol (no mis-bill). **Test follow-up:** the realized-cost vertical (`realizedMediaCost` → a non-zero `cost:updated`) cannot be exercised end-to-end until a generative model carries a rate; the cost MATH is unit-tested via `mediaCost` against a constructed rate, and a non-zero dispatch assertion lands with these rows. *(packages/llm/src/pricing.ts; verified rates → 1.AH)* -- [ ] **`generateMedia` for Gemini-Imagen — adapter wire deferred (1.AG Section C → 1.AH A2).** +- [x] **`generateMedia` SYNC adapters (OpenAI-TTS A1, Gemini-Imagen A2) + image knobs (A6) — ✅ Done (1.AH, PR #38, 2026-06-21).** *(STILL DEFERRED below: multi-image `count > 1` + verified pricing rows.)* Section C wired `generateMedia` SYNC for **OpenAI image** (gpt-image-1 `images.generate` → base64); **1.AH A1 wired OpenAI-TTS audio** (`audio.speech` binary → base64 + `response_format`↔MIME map, no new dep). The remaining sync generator is **Gemini-Imagen** (`generateImages` → `generatedImages[].image.imageBytes`, which @@ -185,26 +185,27 @@ Severity is the review's verified rating. Check an item off in the PR that resol per-model surface lookup is host-wired (above), so it lands written-now/E2E-at-1.AH-host-wiring. Two bounded image follow-ups ride along: (a) **multi-image `count > 1`** — the SYNC `MediaGenResult.media` carries a SINGLE part, so the OpenAI adapter currently rejects `count > 1` (never bill-N-deliver-1); delivering N needs an additive `media: MediaPart[]` - seam amendment (ADR-0031). (b) **image-gen knobs** (`size`/`quality` via `MediaGenRequest.providerOptions`) — the - engine does not yet populate `providerOptions` for a generative call, so the adapter threads only the output format - (`req.mimeType`); a typed per-knob passthrough lands when the engine wires image knobs. *(packages/llm/src/adapters/{openai,gemini}.ts; types.ts MediaGenResult; 1.AH)* + seam amendment (ADR-0031). (b) **image-gen knobs** (`size`/`quality` via `providerOptions.image`) — **✅ Done (A6, PR #38)** on the gpt-image-1 + adapter (body-only, narrowed to valid values, never the SDK RequestOptions); the engine POPULATING + `providerOptions` for a generative call is the Phase-2 host-wiring half. *(packages/llm/src/adapters/{openai,gemini}.ts; types.ts MediaGenResult; 1.AH)* - [ ] **Rate-carrying media representation for raw PCM (1.AH A1 known-limitation).** OpenAI TTS `pcm` is headerless 16-bit LE PCM at 24 kHz, but the seam's `MediaMimeTypeSchema` forbids MIME parameters, so the bare `audio/L16` cannot carry `;rate=24000` (RFC 2586's default is 8 kHz) — a consumer of an `audio/L16` part must assume 24 kHz. Mirrors the pre-existing chat-audio `pcm16 → audio/L16` convention; the self-describing containers (mp3/opus/aac/ flac/wav) are unaffected. A rate-carrying media representation (or dropping bare-PCM from the offered set) is the fix. Low priority — opt-in niche format only. *(packages/shared/src/content.ts MediaMimeTypeSchema; packages/llm/src/adapters/openai.ts; low · 1.AH/Phase-2)* -- [ ] **Sora/Veo async-media ADAPTERS (`generateMedia`→`{ jobId }` + `pollMediaJob`) — deferred (1.AG Section D → 1.AH).** +- [x] **Sora/Veo async-media ADAPTERS (`generateMedia`→`{ jobId }` + `pollMediaJob`) — ✅ Done (1.AH A3/A4, PR #38, 2026-06-21).** 1.AG Section D landed the ENGINE-owned async-job loop (park on `media_job:submitted`, the `host.setTimer` poll cadence, exp-backoff to `pollMaxMs`, deadline→retryable timeout, cross-process re-attach (MJ-1), gate-vs-media resume disambiguation (AG-A-FC-3), cancel-aborts-the-in-flight-poll + terminal sweep) — proven end-to-end with a - STUB async provider + the manual-timer harness. **No Phase-1 adapter implements `pollMediaJob` or returns a - `jobId`** (the only `generateMedia` is OpenAI-image SYNC); the real Sora/Veo `generateMedia`(→jobId)/`pollMediaJob` - adapter implementations are the deferred wiring, alongside the per-model surface lookup + verified rates (1.AH). - **Conformance obligation for that 1.AH PR (review N4):** add a negative assertion that the minted opaque `jobId` - is NOT the recorded vendor operation name (ADR-0045 §7) and that a recorded vendor poll body's `raw` diagnostics - never cross the seam (strip-raw on `MediaJobStatus`) — the seam-contract stub (generative-seam.conformance.test.ts) - proves the SHAPE today, but only a real adapter replaying a vendor body can prove the opacity/strip invariant. *(packages/llm/src/adapters/*; 1.AH)* + STUB async provider + the manual-timer harness. The **OpenAI/Sora (A3)** + **Gemini/Veo (A4)** adapters now + implement `generateMedia`(video)→opaque `jobId` + `pollMediaJob`, via the shared `encode/decodeMediaJobId` codec + (a stateless `rlv-mediajob:1:` bijection — cold-restart re-attach with no adapter state, + ADR-0045 §7); the engine LRO drives them. **N4 conformance obligation discharged:** the opacity test (minted + `jobId` ≠ the raw vendor id) ships in `openai.test.ts`, and strip-raw holds structurally (the `MediaJobStatus` + union carries no `raw` slot). **STILL DEFERRED:** the per-model `media_surface` host lookup (Phase-2 §2.S) + + verified pricing rows. **⚠ OpenAI Sora 2 + Videos API are vendor-deprecated (shutdown 2026-09-24)** — see the + Multimodal forward-obligations entry; impact is the A3 adapter arm only. *(packages/llm/src/adapters/*; 1.AH done / surface + rates deferred)* - [ ] **Node-retry of a PARKED media job — deferred (1.AG Section D, ADR-0045 §3 "MAY re-dispatch").** The node-retry budget (1.S) wraps the executor `#dispatch`; an async media job parks AFTER dispatch returns, and the engine's out-of-band poll loop settles a deadline/poll failure as `node:failed` (retryable for a timeout) directly diff --git a/docs/roadmap/phases/phase-1-engine-and-llm.md b/docs/roadmap/phases/phase-1-engine-and-llm.md index 845506d8..fcaff453 100644 --- a/docs/roadmap/phases/phase-1-engine-and-llm.md +++ b/docs/roadmap/phases/phase-1-engine-and-llm.md @@ -1063,7 +1063,7 @@ phases (2–6). Each phase below maps to the design doc's Phase A–E. cross-fix defects (the orphaned-vertex cost addend + the crash-in-window `run:paused`). All four acceptance criteria are tested. The genuinely-remaining vendor-adapter + host-wiring work is **1.AH** (deferred-tasks.md). - **1.AH — Generative media-output adapters + surfaces & managed mode (Phase E, spans Phases 2–6).** The - Phase-1-doable half is implemented behind the seam: the **four generative media-output adapters** behind the + Phase-1-doable half is **✅ Done (PR #38, 2026-06-21)**: the **four generative media-output adapters** behind the `@relavium/llm` seam — OpenAI-TTS (audio), Gemini-Imagen (image), OpenAI/Sora (async video), Gemini/Veo (async video) — plus the shared **opaque-`jobId` encode/decode** (ADR-0045 §7) and the shared **bare-MIME validator**, all proven with stubs + the generative conformance suite. They are **not yet runtime-reachable**: @@ -1116,7 +1116,7 @@ the latter being the critical-path milestone for the whole product. | 1.m4 ✅ | Agent + non-agent node handlers, gate, checkpoint/resume, retry, tools, **expression sandbox** + pre-egress budget (**all components landed — 1.AC closed it, PR #26, 2026-06-16**) | 1.O, 1.P, 1.Q, 1.R, 1.S, 1.T, **1.AB**, **1.AC** | | **M2 ✅** | **Engine end-to-end from a Node harness (stream + checkpoint + retry + fallback) — CRITICAL-PATH MILESTONE** (**reached — 1.U landed, PR #27, 2026-06-16**) | **1.U** | | **1.m5 ✅** | Agent-first sub-spine: `AgentSession` + session events + persistence + checkpoint/resume + export, proven by its own harness (**additive, parallel — does NOT gate M2**) | 1.V, 1.W, 1.X, 1.Y, 1.Z, **1.AA** | -| 1.m6 | Multimodal I/O: seam amendment (**1.AD ✅ Done, PR #11 — landed before 1.K/1.O so the union members are non-breaking**), then media input/engine/output behavior (**additive — does NOT gate M2**) + surfaces threaded into Phases 2–6 ([ADR-0031](../../decisions/0031-llm-seam-shape-amendment-multimodal-io.md)/[0032](../../decisions/0032-desktop-rust-media-de-inline-amends-0018.md)) | **1.AD ✅, 1.AE ✅, 1.AF ✅, 1.AG ✅**, 1.AH | +| 1.m6 | Multimodal I/O: seam amendment (**1.AD ✅ Done, PR #11 — landed before 1.K/1.O so the union members are non-breaking**), then media input/engine/output behavior (**additive — does NOT gate M2**) + surfaces threaded into Phases 2–6 ([ADR-0031](../../decisions/0031-llm-seam-shape-amendment-multimodal-io.md)/[0032](../../decisions/0032-desktop-rust-media-de-inline-amends-0018.md)) | **1.AD ✅, 1.AE ✅, 1.AF ✅, 1.AG ✅, 1.AH ✅** (PR #38 — adapters; 1.AH's host-wiring half spans Phases 2–6) | ## Sequencing & parallelization @@ -1279,7 +1279,7 @@ flowchart LR | 1.AE | D | 1.AD, 1.G/1.H (adapters) | 1.AF | ✅ — **Done (PR #32, 2026-06-18)** | | 1.AF | D | 1.AE, 1.K, 1.N, 1.R | 1.AG | ✅ — **Done (PR #33/#34/#35/#36, 2026-06-20)** | | 1.AG | D | 1.AF | 1.AH | ✅ — **Done (PR #37, 2026-06-21)** | -| 1.AH | D | 1.AG | Phases 2–6 surfaces | ◇ (spans phases) | +| 1.AH | D | 1.AG | Phases 2–6 surfaces | ✅ — **adapters Done (PR #38, 2026-06-21)**; host-wiring spans Phases 2–6 | > The matrix `Depends on` column is **authoritative** for cross-lane feeder edges (e.g. `1.N → 1.W` > and `1.L.0 → 1.W` into Lane C). The waves Mermaid above draws lane-internal edges plus the `1.O` From af14d58042f72d1611de0b4d8d554df3be34dbc1 Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Sun, 21 Jun 2026 23:26:50 +0300 Subject: [PATCH 2/6] chore: add Relavium Proprietary License (HodeTech) + root license field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Author the Relavium Proprietary License — HodeTech, all rights reserved, with explicit enforcement/legal-remedies, confidentiality, no-warranty, and limitation-of-liability clauses — and set the root package.json "license": "SEE LICENSE IN LICENSE" (the npm-correct pointer to the custom license file, not the forbidden UNLICENSED/OSS placeholder). This closes the one true before-Phase-2 blocker: Phase-2 exit criterion 7 (`npm i -g relavium`, workstream 2.L) cannot publish cleanly without a real license. The text is AI-drafted from the maintainer's intent and should get a legal review before public distribution. Refs: deferred-tasks.md (LICENSE entry) Co-Authored-By: Claude Opus 4.8 --- LICENSE | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 77 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f2f250c1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,76 @@ +Relavium Proprietary License +Copyright (c) 2026 HodeTech. All rights reserved. + +Relavium and all of its source code, object code, designs, documentation, and +associated materials (collectively, the "Software") are the exclusive property of +HodeTech ("HodeTech", "we", "us") and are protected by copyright, trade secret, +and other intellectual property laws and international treaties. + +This is a proprietary, closed-source license. It is NOT an open-source license and +grants NO rights other than those expressly stated below. + +1. RESERVATION OF RIGHTS + All rights in and to the Software are reserved by HodeTech. No right, title, or + interest in the Software is transferred to you. Any right not expressly granted + in writing by HodeTech is withheld. + +2. NO LICENSE GRANTED BY DEFAULT + Except under a separate written agreement signed by an authorized representative + of HodeTech, you are granted NO right to use, execute, copy, reproduce, modify, + adapt, translate, create derivative works of, merge, publish, distribute, + sublicense, sell, rent, lease, lend, host, offer as a service, or otherwise + exploit the Software, in whole or in part, in any form or by any means. + +3. PROHIBITED ACTS + Without limiting Section 2, you may not: + (a) reverse engineer, decompile, or disassemble the Software, or attempt to + derive its source code, except to the extent this restriction is prohibited + by applicable law; + (b) remove, alter, or obscure any copyright, trademark, or other proprietary + notice in the Software; + (c) use the Software, or any HodeTech name, logo, or trademark, in any way that + suggests an endorsement, partnership, or affiliation without prior written + permission; or + (d) circumvent or disable any technical protection or license-enforcement + mechanism in the Software. + +4. OWNERSHIP + The Software is licensed, not sold. HodeTech retains all ownership of and + intellectual property rights in the Software, including all modifications, + enhancements, and derivative works, regardless of who creates them. + +5. CONFIDENTIALITY + The Software, including its source code and internal design, is the confidential + information and trade secret of HodeTech. You must not disclose it to any third + party without HodeTech's prior written consent. + +6. ENFORCEMENT AND REMEDIES + Any unauthorized use, reproduction, or distribution of the Software is a material + violation of this license and of applicable intellectual property law. HodeTech + will enforce its rights to the fullest extent permitted by law and may pursue all + available legal and equitable remedies, including injunctive relief, damages, and + recovery of legal costs. A violation may also constitute a criminal offense under + applicable law. + +7. NO WARRANTY + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT. + +8. LIMITATION OF LIABILITY + TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL HODETECH BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE. + +9. TERMINATION + Any rights granted under a separate written agreement terminate automatically, + without notice, upon any breach of this license. Upon termination you must cease + all use of the Software and destroy all copies in your possession or control. + +10. GENERAL + This license is governed by the laws applicable at HodeTech's principal place of + business, without regard to conflict-of-laws rules. If any provision is held + unenforceable, the remaining provisions remain in full effect. + +For licensing inquiries, written permission, or commercial-use agreements, contact +HodeTech at https://github.com/HodeTech. diff --git a/package.json b/package.json index c7fa8f2b..a501c9cf 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "type": "module", "description": "Relavium — multi-surface, local-first AI agent workflow platform (HodeTech). Monorepo root (tooling only).", + "license": "SEE LICENSE IN LICENSE", "packageManager": "pnpm@9.12.3", "engines": { "node": ">=20.11.0", From 50468eedc75d7d3ba9e52aad20d1d41bbc8e4498 Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Sun, 21 Jun 2026 23:27:02 +0300 Subject: [PATCH 3/6] =?UTF-8?q?docs(standards):=20add=20the=20dependency-b?= =?UTF-8?q?ump=20cooling-window=20policy=20(=C2=A79a)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codify the "do not trust a just-published dependency version" posture as architectural-principles §9a (under principle 9, the dependency posture) plus a one-line enforcement check in the code-review checklist that links to it. A new dependency version gets a cooling window before adoption: prefer a version that has been public long enough to be exercised by the ecosystem over the bleeding edge, with security patches the explicit exception. Written before Phase 2 — the most dependency-heavy phase (commander/ink/ @clack/prompts/tsup, the keychain + MCP bindings) — so the posture is on record before those PRs land. Docs-only review policy; native CI enforcement stays deferred (no tooling step yet). No new dependency, no ADR. Co-Authored-By: Claude Opus 4.8 --- docs/standards/architectural-principles.md | 22 ++++++++++++++++++++++ docs/standards/code-review.md | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/docs/standards/architectural-principles.md b/docs/standards/architectural-principles.md index 3dd71b62..7242e11a 100644 --- a/docs/standards/architectural-principles.md +++ b/docs/standards/architectural-principles.md @@ -136,6 +136,28 @@ about owning the *product* layers; it is not a license to re-implement vetted se foundations. See [security-review.md](security-review.md) and [ADR-0011](../decisions/0011-internal-llm-abstraction.md). +## 9a. Dependency-bump cooling window + +A new dependency version is not trusted the day it ships. Supply-chain attacks land +through freshly published versions of otherwise-reputable packages, and a brand-new +release has had no time to be audited by anyone. So when adding a dependency or bumping +one, **prefer a version that has been public for a while over the just-released latest**: +do not pin to a version published within the last few days, and treat any same-day +upgrade as something to justify, not a default. This is a review posture, not a license +to fall behind — security patches are the exception and are pulled in promptly; the rule +targets *unvetted novelty*, not staying current. + +**Applied rule:** when a `package.json` diff adds or raises a dependency, the reviewer +checks the chosen version's publish age and questions a bleeding-edge pin with no +security justification; the safe default is the most recent version that has been +available long enough to have been exercised by the ecosystem. This matters most for the +dependency-heavy surfaces (the CLI's `commander`/`ink`/`@clack/prompts`/`tsup`, the +keychain and MCP bindings) where the new-dependency count is highest. Adding a dependency +at all still needs the principle-9 bar (an [ADR](../decisions/README.md) for a new runtime +dependency in the core path); this principle governs *which version* of an approved +dependency to take. Native CI enforcement of the cooling window is deferred (it needs a +tooling step that does not yet exist); until then it is a human review check. + ## 10. Clean execution-mode interface — local, cloud, managed A run executes in one of three **execution modes**, all behind the single diff --git a/docs/standards/code-review.md b/docs/standards/code-review.md index be70f5f7..2290a729 100644 --- a/docs/standards/code-review.md +++ b/docs/standards/code-review.md @@ -50,6 +50,10 @@ are actually enforced — automation catches format and types; a human catches t `package.json` diffs and rejects a casual `npm install` that adds a framework or re-introduces a banned one (Vercel AI SDK, LangChain, a Python sidecar). Dev-only tools are lower bar but still justified. +- **Dependency-bump cooling window:** for an added or raised dependency, check the chosen + version's publish age and question a bleeding-edge pin that has been public only a few + days with no security justification — prefer a version exercised by the ecosystem. See + [architectural-principles.md §9a](architectural-principles.md#9a-dependency-bump-cooling-window). - The **LLM seam holds**: no vendor SDK type imported outside `packages/llm/src/adapters/*` (see [code-style-typescript.md](code-style-typescript.md#module-boundaries--no-vendor-type-across-the-llm-seam)). This is a blocking check, not a nit. From a30ec9d46c6cc14ae58d7fa0b9bdc6dd06f61c85 Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Sun, 21 Jun 2026 23:27:12 +0300 Subject: [PATCH 4/6] docs(roadmap): record the pre-Phase-2 maintainer decisions in deferred-tasks Fold the maintainer's three answers from the pre-Phase-2 deferred-tasks triage: - LICENSE entry -> Done (authored + added; see the chore commit). - secret-typed input into a prompt_template -> Decided: REJECT AT PARSE (secure-by-default), to be implemented in Phase-2 workstream 2.D (the first live prompt_template consumer); no live consumer exists yet, so not before. - live-nightly conformance lane -> Decided: defer to Phase-2 workstream 2.K (enable with the regression harness, not as a pre-Phase-2 ops chore). Bookkeeping only; no code change. Co-Authored-By: Claude Opus 4.8 --- docs/roadmap/deferred-tasks.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/roadmap/deferred-tasks.md b/docs/roadmap/deferred-tasks.md index 2deab600..88546c68 100644 --- a/docs/roadmap/deferred-tasks.md +++ b/docs/roadmap/deferred-tasks.md @@ -59,11 +59,14 @@ Severity is the review's verified rating. Check an item off in the PR that resol [code-style-typescript.md §Naming](../standards/code-style-typescript.md); validation is at the Zod boundary and branding adds cross-seam friction for little payoff. Revisit via an ADR if a real id-mixup bug class appears. *(minor · packages/shared/src/run.ts, node.ts)* -- [ ] **`LICENSE` file + root `license` field** — the public repo has neither. - **Decided (2026-06-07): HodeTech will author its own commercial/proprietary license** — left open - until that license text is drafted (do NOT drop in an `UNLICENSED`/OSS placeholder in the - meantime). When ready, add the `LICENSE` file + the root `package.json` `"license"` string. - *(nit → pending the drafted license · package.json, repo root)* +- [x] **`LICENSE` file + root `license` field** — **Done (2026-06-21, pre-Phase-2 housekeeping).** + Authored the **Relavium Proprietary License** (HodeTech, all rights reserved, with an + enforcement/legal-remedies clause) and added the root `LICENSE` file; set root + `package.json` `"license": "SEE LICENSE IN LICENSE"` (npm-correct pointer to the custom + license — not the forbidden `UNLICENSED`/OSS placeholder). This was the one true + before-Phase-2 blocker (Phase-2 exit criterion 7 / `npm i -g relavium`, 2.L). The text is + AI-drafted from the maintainer's intent — **recommend a legal review before public + distribution.** *(LICENSE; package.json, repo root)* - [x] **`node:started.nodeType` enum vs free string** — currently an unconstrained `nonEmptyString`. Decide whether the SSE event should carry the engine node-type enum (add `ENGINE_NODE_TYPES` to constants and `z.enum(...)`) or stay free-string for @@ -571,8 +574,12 @@ Severity is the review's verified rating. Check an item off in the PR that resol `secret`-typed input interpolates raw into a USER message sent to the provider. This is provider **egress** the author opted into (not an event-payload leak, so it does not violate the events rule the 1.P fix enforces), but whether a `secret`-typed input should be silently interpolated into a prompt — vs masked / - rejected at parse — is a policy call. Evaluate alongside the secret-handling story; if masked, reuse - `maskSecretInputs`. *(low · packages/core/src/engine/agent-runner.ts; security-review.md)* + rejected at parse — is a policy call. **Decided (2026-06-21, maintainer): REJECT AT PARSE** — a + `{{ inputs. }}` reference inside a `prompt_template` is a parse-time error with a clear + message (secure-by-default; surfaces author intent explicitly rather than silently egressing or silently + masking). **Implement in Phase-2 workstream 2.D** (the first live consumer of `prompt_template` via + `relavium run`), not before — no live `prompt_template` consumer exists yet. *(low → 2.D · + packages/core/src/engine/agent-runner.ts + the parse/validation layer; security-review.md)* - [x] **Reject a plain (handle-less) edge whose `from` is a `condition` node (1.M validation)** — a `condition` routes only via `branches[].target_node`/`default` (materialized edges); a separately-authored plain edge `from: ` (no `:handle`) makes its target a dependent that the handler's `selected` never names, so @@ -802,6 +809,9 @@ Severity is the review's verified rating. Check an item off in the PR that resol `1.J` accepted M1 with the live lane **explicitly pending keys**; to actually exercise it, uncomment the `schedule:` lane and add the provider API keys (`ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GEMINI_API_KEY` / `DEEPSEEK_API_KEY`) as CI secrets. Until then live coverage is a known gap. + **Decided (2026-06-21, maintainer): defer to Phase-2 workstream 2.K** — enable the live-nightly lane + together with the 2.K regression harness rather than as a pre-Phase-2 ops chore; fixture coverage holds + the line until then. *(minor → keys · ci.yml, packages/llm/src/conformance/*.conformance.test.ts)* - [ ] **Leakwatch secret-scanning CI gate** — CI has no secret-scan step. The HodeTech standard scanner is **Leakwatch** (never gitleaks); the blocking `ci.yml` step is wired once a From 130a64317f90ff74250f1fe105bae6de95c7d59e Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Mon, 22 Jun 2026 00:01:13 +0300 Subject: [PATCH 5/6] docs: Update Phase 1 roadmap to reflect completion of all workstreams and milestones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mark Phase 1 as complete with all workstreams (1.A–1.AH) merged (PR #6–#38). - Achieve global milestones M1 (LLM seam proven) and M2 (engine end-to-end). - Detail the completion of the critical path and additive sub-spines, including Lane C and multimodal sub-spine. - Document the decision on multimodal I/O and its integration into the roadmap. --- CLAUDE.md | 39 +- README.md | 142 +++++-- docs/roadmap/current.md | 286 ++------------ docs/roadmap/deferred-tasks.md | 374 +----------------- docs/roadmap/phases/phase-1-engine-and-llm.md | 50 +-- 5 files changed, 153 insertions(+), 738 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 29e8681a..0e7fb87e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,34 +38,17 @@ A run executes in one of **three execution modes** behind the one `LLMProvider` engine is identical across all three. See [ADR-0012](docs/decisions/0012-managed-inference-dual-mode.md) to [ADR-0015](docs/decisions/0015-managed-mode-data-handling-and-compliance.md) and [docs/architecture/managed-inference.md](docs/architecture/managed-inference.md). -**Status:** Phase 1 in progress — milestone M1 (LLM seam proven) reached (PR #9, 2026-06-07); the `FallbackChain` runner (1.K) landed, completing 1.m2 with the cost tracker (PR #13, 2026-06-11); the run loop (1.N — `WorkflowEngine` + `RunEventBus`) landed (PR #17, 2026-06-13) **completing 1.m3** (parse → DAG → run loop emits the canonical event stream), with the built-in `ToolRegistry` (1.T, a 1.m4 component) landing alongside it as the other `AgentRunner` (1.O) join prerequisite; the **`AgentRunner` (1.O) — per-node LLM execution behind the seam — landed (PR #18, 2026-06-14)**; and the **node-type handlers (1.P) — the six non-agent `NodeExecutor` arms (condition / transform / fan_out / fan_in / input / output) behind a dispatching executor — landed (PR #20, 2026-06-14)**; and **checkpoint/resume (1.R) + the human gate (1.Q) landed (PR #22, 2026-06-15)** — the derived `Checkpointer` + cross-process `resumeFromCheckpoint`, and the `human_in_the_loop` gate with the one-shot timeout port; and **node retry (1.S) — the above-chain whole-node retry budget ([ADR-0040](docs/decisions/0040-node-retry-budget-above-the-chain.md), amending ADR-0038) — landed (PR #24, 2026-06-15)**, re-dispatching a whole node on a retryable failure up to `retry.max` attempts (with `node:retrying`, abort-aware backoff, and `retry_on` filtering), with retry-from-node (ADR-0040 Part B) deferred to Phase-2; and the **pre-egress budget governor (1.AC, [ADR-0028](docs/decisions/0028-workflow-resource-governance.md)) + the `AgentSession` agent-first entry point (1.V, [ADR-0024](docs/decisions/0024-agent-first-entry-point-agentsession.md)) landed together (PR #26, 2026-06-16)** — 1.AC was the last 1.m4 component, so **1.m4 is complete** (the full engine stack: node handlers, gate, checkpoint/resume, retry, tools, sandbox, budget governor), and 1.V opens the Lane-C agent-first sub-spine (1.m5); then the **end-to-end Node harness (1.U) landed (PR #27, 2026-06-16), reaching 🎯 M2** — the engine runs end-to-end (live streaming + per-node-boundary checkpointing + cross-process resume + node retry + provider failover, gap-free), **completing the Phase-1 engine critical path**. The remaining Phase-1 work is additive and off the critical path (Lane C: the **`session:*` namespace (1.W) landed (PR #28, 2026-06-17)** — the `SessionEventSink`→`RunEventBus` adapter + per-session `sequenceNumber`, the `SessionHandle`, and the combined `RunOrSessionEventSchema` gate — and **session persistence (1.X) landed (PR #29, 2026-06-17)** — the `agent_sessions`/`session_messages` tables + migration, `SessionMessageSchema`/`AgentSessionSchema`, and the `SessionStore` + domain↔row mappers (data-layer only); then **session checkpoint/resume (1.Y) + export-to-workflow (1.Z) landed (PR #30, 2026-06-17)** — `reconstructSessionState`/`AgentSession.resume` (reload-not-replay; preload the text-only transcript, re-seed turnCount/cost, no `session:started` re-emit) + the `serializeWorkflow`/`sessionToWorkflow` pair (one agent node per completed turn, transcript in `metadata`, secret/signature exclusion structural) — leaving only the **1.AA** chat-regression harness ‖ the 1.m6 multimodal sub-spine, whose first step — **media-input adapters + the shared SSRF policy primitive (1.AE) — landed (PR #32, 2026-06-18)** (base64 image/audio across the three adapters behind per-modality `assertMediaCapabilities`, and the one `@relavium/shared` SSRF policy primitive reused by the provider-`baseURL` + `http_request` callers; the SSRF *mechanism* half + per-modality FallbackChain gating moved into **1.AF**). **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20):** the `MediaStore`/`deInlineMedia` choke point (the active I3 enforcement), per-modality capability gating (`mediaSupportReason`/`requestSupportReason` — replacing coarse vision), the `media_objects`/`media_references` tables, and ADR-0042/0043/0044 (P1+P2, PR #33); the binary media-egress + the **SSRF mechanism half** (D9 — the `MediaUrlFetch` re-host hook + the SSRF-validated `fetchMediaBytes` wired at the choke point), the `FallbackChain` resolve-before-egress + the **byte-free re-materialization sidecar** (D8/D7), and the byte-delivery **`Range` gate** (D13) (P3 + P4/D13, PR #34); `read_media` + the scope-set authz (D12), the terminal sweep + grace-window GC (D11), `save_to` (D16), the `output_modalities` load-check (D15), and the per-modality media cost governor (D17) (P4 remainder, PR #35); plus a **13-dimension end-to-end review + an external Sonar/CodeRabbit pass** (PR #36 — H1 `agent:tool_call.toolInput` byte-redaction, H2 the unified exact-membership output-combination gate, the GC grace-basis fix, the `nodeMediaEgressDeps` egress-mechanism test). Each phase passed a dedicated adversarial security-review (0 blockers/highs). The deferred **host-wiring** half (D12 `MediaReadAccess` + session-scope population, the D15 loader, the D17/`resolveForEgress` config) + the keychain no-raw-key IPC test are owned by **1.AH** (recorded in deferred-tasks.md). **1.AG (media output generation) is ✅ Done (PR #37, 2026-06-21):** inline media-out via `generate()` (Section B, [ADR-0046](docs/decisions/0046-inline-media-out-via-generate-streaming-triad-deferred.md)), sync `generateMedia` + `media_surface` routing (C), the engine-owned async media-job **poll/checkpoint/resume/cancel LRO** (D, [ADR-0045](docs/decisions/0045-async-media-job-loop-poll-checkpoint-resume-cancel.md)), and acceptance + generative conformance (E); the five sections were each dual-reviewed (Opus+Sonnet), then a final Opus-consolidated pre-merge review caught + fixed two HIGH cross-fix defects (the orphaned-vertex cost addend + the crash-in-window `run:paused`). **1.AH — the four generative media-output adapters (OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo) behind the seam + the shared opaque-jobId codec + the bare-MIME validator — is ✅ Done (PR #38, 2026-06-21)** (each adapter dual/Opus-reviewed; the `save_to` url-double-fetch (A5) deferred; OpenAI Sora 2 is vendor-deprecated [shutdown 2026-09-24] — A3-arm only, recorded in deferred-tasks.md). 1.AH's remaining **host-wiring half is distributed across Phases 2–6** as recorded tasks (CLI §2.S, desktop §3.B, VS Code §4.N, managed/cloud media). **Phase 1 is complete; Phase 2 (CLI, M3) is unblocked.** -Phase 0 (M0, 2026-06-04) landed the monorepo, strict toolchain + CI, `@relavium/shared` (the -full Zod contract set), the no-vendor-type seam fence, and `@relavium/db`. Phase 1 has since -landed `@relavium/llm` — the `LLMProvider` seam + all three adapters (Anthropic, OpenAI/DeepSeek, -Gemini), green on one shared conformance suite with no vendor type crossing the seam, plus the -ADR-0030 seam-shape amendment — and the **ADR-0031 multimodal seam-shape amendment (1.AD, PR #11, -2026-06-10)**: the media content/stream union members, the per-modality capability matrix, and the -reserved generator methods, shape-only, landed before the seam's exhaustive consumers. The -`FallbackChain` runner (1.K, PR #13) is now landed and fully covered. The -[`@relavium/core` engine](docs/roadmap/phases/phase-1-engine-and-llm.md) lane has since landed the -**`WorkflowYAMLParser` (1.L, PR #14)**, the **`{{ … }}` interpolation engine + parse-time secret-taint -gate (1.L2, PR #15)**, the **DAG builder + `RunPlan` (1.M)** plus the **QuickJS-wasm expression -sandbox (1.AB)** (PR #16, 2026-06-13), and the **run loop — `WorkflowEngine` + `RunEventBus` (1.N)** -together with the **built-in `ToolRegistry` (1.T)** (PR #17, 2026-06-13), the **`AgentRunner` (1.O)** -join (PR #18, 2026-06-14 — host-injected provider resolution behind the seam, the correlation-agnostic -turn core, the tool-call loop, and the same-provider reasoning replay), and the **node-type handlers -(1.P)** (PR #20, 2026-06-14 — the six non-agent `NodeExecutor` arms behind a dispatching executor, -executor-only with a `secretInputNames` masking gate on `NodeExecContext`), and **checkpoint/resume -(1.R) + the human gate (1.Q)** (PR #22, 2026-06-15 — the derived `Checkpointer` (state folded from the -`run_events` log, no checkpoint table — ADR-0003) + cross-process `resumeFromCheckpoint` with idempotent -re-delivery and a `workflow_mismatch` identity guard, and the `human_in_the_loop` gate's suspend/resume -plus the one-shot `setTimer` timeout port — `approve` auto-resolves, `reject` fails with `run_timeout`). -The pre-egress budget governor (1.AC) + the agent-first `AgentSession` (1.V) landed together (PR #26) -**completing 1.m4**; then the end-to-end Node harness (1.U) landed (PR #27, 2026-06-16) **reaching M2** — -the Phase-1 engine critical path is complete. The additive Lane-C agent-first sub-spine is now **complete** -(session events **1.W ✅ (PR #28)** + persistence **1.X ✅ (PR #29)** + checkpoint/resume **1.Y** & export **1.Z ✅ (PR #30, 2026-06-17)** + the **1.AA** chat-regression harness ✅ (2026-06-17), closing **1.m5**); -on the 1.m6 multimodal sub-spine, **media-input adapters + the shared SSRF policy primitive (1.AE) landed (PR #32, 2026-06-18)** — after a multi-round + final 8-dimension adversarial review (no SSRF bypass found); the SSRF *mechanism* half + per-modality gating moved into 1.AF. **1.AF (engine media plumbing) is ✅ Done (PR #33/#34/#35/#36, 2026-06-20)** — P1+P2 (PR #33); the media-egress + SSRF mechanism half (D9), the `FallbackChain` resolve-before-egress + byte-free re-materialization sidecar (D8/D7), the byte-delivery `Range` gate (D13), and a clean P3 egress/SSRF security-review (P3 + P4/D13, PR #34); D12 `read_media` + authz, D11 GC sweep, D16 `save_to`, D15 load-check, D17 cost governor (P4 remainder, PR #35); and a 13-dimension end-to-end + external Sonar/CodeRabbit review (PR #36 — toolInput byte-redaction, the unified exact-membership output gate, the GC grace-basis fix, the egress-mechanism test). The host-wiring half (D12/D15/D17 mechanism) + the keychain IPC test are deferred to **1.AH**. **1.AG (output generation) is ✅ Done (PR #37, 2026-06-21)** — inline media-out (B) + sync `generateMedia`/routing (C) + the async media-job LRO (D, ADR-0045) + acceptance/conformance (E), each section dual-reviewed plus a final Opus-consolidated pre-merge review (two HIGH cross-fix defects caught + fixed). **1.AH (the four generative media-output adapters — OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo — behind the seam + the shared opaque-jobId codec + the bare-MIME validator) is ✅ Done (PR #38, 2026-06-21)**, each adapter dual/Opus-reviewed (A5 `save_to` double-fetch deferred; OpenAI Sora 2 vendor-deprecated, shutdown 2026-09-24, A3-arm only). **Phase 1 is complete** — 1.AH's host-wiring half is distributed across Phases 2–6 as recorded tasks; **Phase 2 (CLI) is unblocked**. See -[docs/roadmap/current.md](docs/roadmap/current.md). See [README.md](README.md) for the public overview. +**Status:** Phase 1 is **complete** (2026-06-21). The engine runs end-to-end behind +the `LLMProvider` seam: YAML→DAG parse, the multi-model run loop (agent + the six +non-agent node handlers) with live streaming, per-node-boundary checkpoint/resume +(cross-process), node retry, provider failover, cost governance, the human gate, and +multimodal media I/O (input, inline output, and the async generation job loop). All +three adapters (Anthropic, OpenAI/DeepSeek, Gemini) and the agent-first `AgentSession` +entry point (multi-turn sessions, persistence, export-to-workflow) are shipped. +**Phase 2 (CLI, milestone M3) is unblocked.** For live status, per-PR history, +milestone dates, and open obligations, see the canonical home +[docs/roadmap/current.md](docs/roadmap/current.md); [README.md](README.md) is the +public overview. ## Non-negotiable rules for AI agents diff --git a/README.md b/README.md index 8534baed..d5dcb0c4 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,106 @@ # Relavium -> Multi-surface AI agent workflow platform — a product of [HodeTech](https://github.com/HodeTech). +> **Start as an agent. Ship the workflow. Own every run.** +> A multi-surface, local-first AI agent workflow platform — a product of [HodeTech](https://github.com/HodeTech). -With Relavium you can **start as a conversational agent, graduate to workflows, or -author workflows directly** — a chat session and a git-committable multi-agent, -multi-model `.relavium.yaml` workflow are two entry points to the **same** engine. -Run them across a Tauri desktop app, a VS Code extension, and a CLI — -**local-first** in Phase 1, with cloud execution and a control-plane portal in -Phase 2. The engine is a pure-TypeScript package shared by every surface, -and multi-provider LLM access goes through Relavium's own `@relavium/llm` -abstraction over the official provider SDKs (no Vercel, no LangChain). +Relavium meets you where you already work — in conversation — and gives that +conversation somewhere to go. You **start as an agent**: a multi-turn session in your +terminal, in VS Code, or in a desktop chat panel. When a flow proves itself, you **ship +the workflow**: export the session to a git-committable, multi-agent, multi-model +`.relavium.yaml` pipeline that runs identically in your editor, your terminal, and your +CI. Or author workflows directly. Either way you **own every run** — every step +debuggable, every token and dollar tracked, every artifact yours, nothing leaving your +machine unless you choose it. + +## Why Relavium? + +- **Four surfaces, one engine.** Desktop (Tauri), CLI, VS Code, and (Phase 2) the web + portal run the _identical_ pure-TypeScript engine. No Python sidecar, no single-tool + lock-in — every surface is a first-class execution target. +- **A chat-to-workflow continuum.** Other tools make every session ephemeral. Relavium + sessions are persistent, resumable, and one-click exportable into a reviewed, + committed workflow. +- **You own your LLM seam.** Multi-provider routing with fallback chains + (`[claude → gpt-4o → gemini]`) is first-class through Relavium's own `@relavium/llm` + abstraction over the official provider SDKs — no Vercel AI SDK, no LangChain. +- **Local-first by design.** Phase 1 ships with zero cloud and no account required; keys + live in your OS keychain. BYOK-local stays first-class forever; Phase 2 _adds_ optional + managed inference and cloud execution on the same engine. +- **Workflows are git objects.** `.relavium.yaml` files are diffable, reviewable, + PR-able, and shareable — team infrastructure, not a proprietary JSON blob or buried + Python. +- **Multimodal, end-to-end.** Image / audio / video as input and output — including + rule-driven media generation — flow through the same seam and engine. + +## Highlights (Phase 1) + +- **Chat-to-workflow export** — turn a proven session into a reusable `.relavium.yaml`. +- **Persistent, resumable agent sessions** — no run is ever ephemeral. +- **Live execution** — tokens stream as the run progresses; parallel branches run together. +- **Multi-model fallback chains** — runs survive provider outages and rate limits. +- **Checkpoint & resume** — pause and resume at any node boundary, even across processes. +- **Human gates with timeout policy** — pause for an approve / reject / input decision. +- **Per-node cost waterfall** — token and dollar attribution per node, per model. +- **Local-first, zero-install posture** — BYOK, OS keychain, no sign-up in Phase 1. + +## Architecture + +```mermaid +flowchart TD + subgraph Surfaces + D[Desktop · Tauri] + C[CLI] + V[VS Code extension] + P[Web portal · Phase 2] + end + subgraph Engine["@relavium/core — one pure-TypeScript engine"] + WE[WorkflowEngine] + AS[AgentSession] + BUS[(RunEventBus · ToolRegistry)] + WE --- BUS + AS --- BUS + end + SEAM["@relavium/llm seam"] + PROV[Anthropic · OpenAI/DeepSeek · Gemini] + D --> Engine + C --> Engine + V --> Engine + P --> Engine + Engine --> SEAM --> PROV +``` + +One engine, **two co-equal entry points** — `WorkflowEngine` (runs YAML pipelines) and +`AgentSession` (runs conversational chat) — sharing the same tool registry, the same +`@relavium/llm` multi-provider seam, and the same event bus. The engine has **zero +platform-specific imports**, so the same source runs in the Tauri WebView, the VS Code +host, the Node CLI, and (Phase 2) a Bun server. Supporting packages: `@relavium/shared` +(Zod contracts), `@relavium/db` (Drizzle — SQLite locally, PostgreSQL in Phase 2), and +`@relavium/ui` (ReactFlow canvas + shadcn). See [docs/architecture/](docs/architecture/). + +## Execution modes + +One engine, three modes behind the one `LLMProvider` seam: + +- **Local (BYOK)** — Phase 1 default. Your keys, your machine, zero Relavium data. +- **Managed inference** — Phase 2, opt-in. Relavium's metered keys; the engine still runs + locally. +- **Cloud execution** — Phase 2. Run workflows on cloud workers for 24/7 automation and + team sharing. + +## Status + +**Phase 1 — Engine and LLM is complete** (2026-06-21): the engine runs end-to-end on +local-first BYOK — workflow parsing, DAG execution, live streaming, checkpoint/resume, +multi-provider failover, cost governance, and multimodal media I/O. **Phase 2 (CLI) is +next.** For live status and the full roadmap, see +[docs/roadmap/current.md](docs/roadmap/current.md) and the +[roadmap](docs/roadmap/README.md). ## Documentation The canonical documentation lives in [`docs/`](docs/) — start at -[docs/README.md](docs/README.md), which is organized by *the kind of question each -section answers*. +[docs/README.md](docs/README.md), which is organized by _the kind of question each +section answers_. | Start here | | |------------|---| @@ -24,34 +109,9 @@ section answers*. | [Architecture](docs/architecture/) · [Decisions (ADRs)](docs/decisions/) · [Reference](docs/reference/) | How it works | | [Roadmap](docs/roadmap/README.md) · [Standards](docs/standards/) | Where it's going, and the rules | -## Status +## License -**Phase 1 — engine and LLM in progress; milestone M1 (LLM seam proven) reached (PR #9, -2026-06-07).** Phase 0 — Foundations (milestone M0, 2026-06-04) landed the Turborepo + pnpm -monorepo, the strict toolchain + GitHub Actions CI, `@relavium/shared` (the full Zod contract -set), the no-vendor-type seam fence, and `@relavium/db`. Phase 1 then landed -[`@relavium/llm`](docs/roadmap/phases/phase-1-engine-and-llm.md): the provider-agnostic -`LLMProvider` seam and **all three adapters** (Anthropic; the OpenAI-compatible adapter serving -OpenAI + DeepSeek; Gemini), passing one shared conformance suite behind the frozen seam with no -vendor type crossing it (PR #7–#9), followed by the **ADR-0031 multimodal seam-shape amendment -(1.AD)** — the media content/stream union members, the per-modality capability matrix, and the -reserved generator methods, landed **shape-only** before the seam's exhaustive consumers exist -(PR #11, 2026-06-10). The seam's last policy layer — the `FallbackChain` runner (1.K) — then landed -(PR #13, 2026-06-11), completing the LLM lane. The `@relavium/core` engine lane has since landed the -**`WorkflowYAMLParser`** (1.L, PR #14), the **`{{ … }}` interpolation engine + parse-time -secret-taint gate** (1.L2, PR #15), and the **DAG builder + `RunPlan`** (1.M) together with the -**QuickJS-wasm expression sandbox** (1.AB) (PR #16, 2026-06-13) — all with zero platform imports. -The **run loop** (1.N — `WorkflowEngine` + `RunEventBus`) landed (PR #17, 2026-06-13), **completing -milestone 1.m3** (parse → DAG → run loop emits the canonical event stream); the **built-in -`ToolRegistry`** (1.T, a 1.m4 component) landed alongside it; the **`AgentRunner`** (1.O — -per-node LLM execution behind the seam) landed (PR #18, 2026-06-14); and the **node-type handlers** -(1.P — the six non-agent handlers behind a dispatching executor) landed (PR #20, 2026-06-14), followed by -the **human gate** (1.Q) + **checkpoint/resume** (1.R, PR #22), **node retry** (1.S, PR #24), and the -**pre-egress budget governor** (1.AC) together with the agent-first **`AgentSession`** entry point (1.V) — -both landed in **PR #26 (2026-06-16)**. With the budget governor in, **milestone 1.m4 is complete**; then the -**end-to-end Node harness** (1.U) landed (**PR #27, 2026-06-16**) — **🎯 reaching milestone M2**: the engine runs -end-to-end (live streaming + checkpoint + cross-process resume + retry + provider failover), completing the -Phase-1 engine critical path. The remaining Phase-1 work is additive (the agent-first sub-spine — **session -events 1.W ✅ (PR #28)** + **persistence 1.X ✅ (PR #29)** + **session checkpoint/resume 1.Y & export-to-workflow 1.Z ✅ (PR #30, 2026-06-17)** + **the 1.AA chat-regression harness ✅ (2026-06-17)**, completing **1.m5** — and the -multimodal sub-spine — **media-input adapters + the shared SSRF policy primitive 1.AE ✅ (PR #32)**, **engine media plumbing 1.AF ✅ (PR #33/#34/#35/#36, 2026-06-20)**, **media output generation 1.AG ✅ (PR #37, 2026-06-21)**, and **the generative media-output adapters 1.AH ✅ (PR #38, 2026-06-21)** — all landed, **completing 1.m6 / Phase 1**; 1.AH's host-wiring half is distributed across Phases 2–6 as recorded tasks); **Phase 2 (the CLI) is unblocked**. See -[docs/roadmap/current.md](docs/roadmap/current.md) for live status. +Relavium is **proprietary software** — © 2026 HodeTech, all rights reserved. It is +**not** open source and grants no rights except as expressly stated. See +[LICENSE](LICENSE) for the full terms. For licensing inquiries, written permission, or +commercial-use agreements, contact [HodeTech](https://github.com/HodeTech). diff --git a/docs/roadmap/current.md b/docs/roadmap/current.md index d5a2b991..c23ee451 100644 --- a/docs/roadmap/current.md +++ b/docs/roadmap/current.md @@ -2,265 +2,53 @@ > Status: Living -> Last updated: 2026-06-19 +> Last updated: 2026-06-21 -- **Related**: [README.md](README.md), [phases/phase-0-foundations.md](phases/phase-0-foundations.md), [phases/phase-1-engine-and-llm.md](phases/phase-1-engine-and-llm.md), [../project-structure.md](../project-structure.md), [../tech-stack.md](../tech-stack.md) +- **Related**: [README.md](README.md), [phases/phase-2-cli.md](phases/phase-2-cli.md), [deferred-tasks.md](deferred-tasks.md), [../project-structure.md](../project-structure.md), [../tech-stack.md](../tech-stack.md) -This page tracks what is active **right now** and the immediate next concrete -actions. The full phase plan and the global milestone spine are in -[README.md](README.md); the granular work breakdown for the active phase is in -[phases/phase-1-engine-and-llm.md](phases/phase-1-engine-and-llm.md). +This page tracks what is active **right now** and the immediate next concrete actions. +The full phase plan and the global milestone spine are in [README.md](README.md); the +granular work breakdown for the active phase is in +[phases/phase-2-cli.md](phases/phase-2-cli.md). ## Where we are -**Phase 0 — Foundations is complete: all workstreams 0.A–0.I landed and merged (PR #1–#3, -2026-06-04), achieving global milestone M0.** The repository -(`github.com/HodeTech/Relavium`) holds the `docs/` tree **plus** the Turborepo + pnpm -workspace, the strict `tsconfig` bases, the root ESLint/Prettier/Vitest spine, -`@relavium/shared` with the **full Zod schema set** (123 tests, reference round-trip with no -drift, authored YAML `.strict()`, run-event names pinned), the no-vendor-type **seam fence** -(live across 9 import syntaxes, TS + JS, self-checking), **GitHub Actions CI** -(lint/typecheck/test/build + format + a schema↔migration drift gate, SHA-pinned actions), -and **`@relavium/db`** (the Drizzle schema + `drizzle-kit` migrations + a `better-sqlite3` -client, 10 tests). `pnpm install && pnpm turbo run lint typecheck test build` is green, -`format:check` is clean, and CI is green on push. Confirmed-but-deferred review findings are -parked in [deferred-tasks.md](deferred-tasks.md). The foundation is settled and recorded: - -- Product vision, UVP, and hard constraints (desktop is agent-management, not an - IDE; local-first Product Phase 1; git-native workflow YAML). -- The full architecture set (overview, shared core engine, execution model, state - management, local-first security, desktop architecture, multi-LLM providers, key - management, the Phase-2 cloud design, and the Phase-2 managed-inference design). -- The canonical reference contracts (workflow/agent YAML specs, the SSE/run-event - schema, the IPC contract, config, database schema, CLI commands, the VS Code - extension API). -- The numbered ADRs, including the settled multi-LLM decision: an **internal - Relavium-owned `@relavium/llm` abstraction** with a single provider-agnostic - `LLMProvider` seam and thin hand-rolled adapters over the official provider SDKs — - **not** the Vercel AI SDK, **not** LangChain - ([ADR-0011](../decisions/0011-internal-llm-abstraction.md), superseding - [ADR-0004](../decisions/0004-vercel-ai-sdk-multi-llm.md)) — and the **dual-mode - managed-inference decision (Option B)**: BYOK-local stays first-class and unchanged, - while managed inference is added as an opt-in `managed` mode shipped as the **first** - Phase-2 deliverable, decoupled from and ahead of cloud execution - ([ADR-0012](../decisions/0012-managed-inference-dual-mode.md), with - [ADR-0013](../decisions/0013-managed-key-vault-and-pools.md)/[0014](../decisions/0014-managed-metering-quota-and-billing.md)/[0015](../decisions/0015-managed-mode-data-handling-and-compliance.md)). - Later ADRs pin the Phase-2 API stack ([ADR-0016](../decisions/0016-api-framework-hono.md) Hono, - [ADR-0017](../decisions/0017-cloud-runtime-bun.md) Bun), the **desktop execution model** - ([ADR-0018](../decisions/0018-desktop-execution-and-rust-egress.md) — engine in the WebView, - Rust-delegated LLM egress), and the CLI's Node-side keychain library - ([ADR-0019](../decisions/0019-cli-node-keychain-library.md) — `@napi-rs/keyring`, not the - archived `keytar`). -- The full seven-phase roadmap: the [phase index](README.md#phase-index), the - [global milestone spine](README.md#global-milestone-spine) (M0–M7), and the - [cross-phase invariants](README.md#cross-phase-invariants). Per Option B, Product - Phase 2 is split into **build phase 5 (managed inference)** and **build phase 6 - (cloud execution + portal)**. - -## What is active now - -> **Agent-first pivot (2026-06-05).** Relavium has pivoted from workflow-first to **agent-first + -> workflow**: a conversational `AgentSession` becomes a first-class engine entry point alongside -> `WorkflowEngine`, with a chat → workflow export -> ([ADR-0024](../decisions/0024-agent-first-entry-point-agentsession.md) / -> [0025](../decisions/0025-agent-surface-refines-desktop-scope.md) / -> [0026](../decisions/0026-session-export-to-workflow.md)). A workflow-system hardening pass adds three -> security/robustness ADRs ([0027](../decisions/0027-expression-sandbox.md) expression sandbox, -> [0028](../decisions/0028-workflow-resource-governance.md) resource governance, -> [0029](../decisions/0029-tool-policy-hardening.md) tool-policy). Phase 1 gains an **additive, parallel** -> agent-first sub-spine (1.V–1.AA), the QuickJS-wasm sandbox (1.AB — on the critical path inside 1.P), -> and the pre-egress budget governor (1.AC). The **agent-first sub-spine (1.V–1.AA) adds no work to the M1/M2 critical path** (it runs in parallel and is proven by its own harness); the hardening sandbox **1.AB *is* new M2-critical-path work** — it folds into 1.P and raises the 1.m4 cost. Phases 2–4 add -> non-critical chat workstreams; phases 5–6 are largely unaffected (sessions inherit managed/cloud via -> the existing seams). All decisions/specs are landed at the docs/ADR layer. **Phase 1 has begun:** -> 1.L.0 — the `@relavium/shared` reconciliation to these agent-first + hardening contracts (the new -> events, `ErrorCode`/`StopReason`, the authored budget/validation/output_schema fields, the `[chat]` -> config) — merged in **PR #6** (2026-06-05). No engine code yet; the seam + parser come next. - -**Phase 0 is done (M0 reached); the active phase is now -[Phase 1 — engine and LLM](phases/phase-1-engine-and-llm.md)** (Product Phase 1, the -critical path). With a frozen contract and a green CI gate in place, Phase 1 builds the two -core packages: **`@relavium/llm`** (the `LLMProvider` seam + thin hand-rolled adapters over -the official provider SDKs — the seam fence's first real consumer) and **`@relavium/core`** -(the engine: YAML→DAG parse, runner, checkpoint/resume, retry — zero platform imports, -consuming `@relavium/db` for run persistence). - -Global milestone **M1 — LLM seam proven** is reached (PR #9, 2026-06-07): all three -adapters pass the shared conformance suite behind the frozen seam. **Global milestone M2 — engine -end-to-end is now reached (PR #27, 2026-06-16):** the 1.U Node harness proves `@relavium/core` runs a -workflow end-to-end (live streaming + per-node-boundary checkpointing + cross-process resume + node -retry + provider failover, per-attempt cost, gap-free `sequenceNumber`) — **completing the Phase-1 -engine critical path**. The next global checkpoint is **M3** (the Phase-2 CLI + engine regression -harness); see the [milestone spine](README.md#global-milestone-spine). - -> **One Phase-0 follow-up lives outside the code:** a maintainer should mark the CI `ci` -> job a **required check** in GitHub branch protection (optionally adding `TURBO_TOKEN`/ +**Phase 1 — Engine and LLM is COMPLETE** (milestone **M2** reached, PR #27, 2026-06-16; +all workstreams 1.A–1.AH merged through PR #38, 2026-06-21). The engine runs end-to-end: +YAML parse → DAG → run loop → node execution (agent + the six non-agent handlers) → +checkpoint/resume → node retry → provider failover, with per-attempt cost tracking and a +gap-free `sequenceNumber`. The `@relavium/llm` seam is frozen and proven behind a +provider-agnostic `LLMProvider` interface with all three adapters (Anthropic, +OpenAI/DeepSeek, Gemini) and the `FallbackChain` runner. Two additive sub-spines also +completed: the agent-first entry point (`AgentSession` multi-turn + session persistence + +export-to-workflow, Lane C / 1.m5, 1.V–1.AA) and multimodal I/O (media input + engine +plumbing + inline & async output generation + generative adapters, 1.m6, 1.AD–1.AH). See +[Phase 1 detail](phases/phase-1-engine-and-llm.md), the [decision index](../decisions/), +and the [reference specs](../reference/). + +> **One live maintainer obligation (carried from Phase 0):** mark the CI `ci` job a +> **required check** in GitHub branch protection (optionally add `TURBO_TOKEN`/ > `TURBO_TEAM` secrets for the cross-runner remote cache). -## Immediate next steps - -Phase 1 is underway and **M1 (LLM seam proven) is reached**. **Wave 0 — 1.L.0** (`@relavium/shared` -reconciliation) merged in **PR #6**; the **Wave-1 seam trio — 1.A** (seam types), **1.B** (CostTracker -+ pricing), **1.E** (ToolNormalizer) — merged in **PR #7**; the first **adapter lane — 1.C** -(`AnthropicAdapter`), **1.I** (`LlmError` classification), **1.F** (conformance harness), **1.D** -(capabilities + `providerOptions`) — merged in **PR #8** (2026-06-06); and the **remaining adapters + -seam amendment — 1.G** (OpenAI/DeepSeek) ‖ **1.H** (Gemini), the **ADR-0030** seam-shape amendment -(reasoning channel + `responseFormat` + `providerExecuted`), and **1.J** (conformance green) — merged -in **PR #9** (2026-06-07). All three adapters now pass the shared conformance spec in fixture mode -(live nightly reserved, pending keys), with classified errors and capability gating, behind the frozen -`LLMProvider` seam — **🎯 M1 achieved**. The **ADR-0031 multimodal seam-shape amendment — 1.AD** -(the `media` `ContentPart` arm + handle-only durable fork, the `media_start/delta/end` `StreamChunk` -triad, the `CapabilityFlags.media` matrix with `vision` as its derived alias, `Usage.mediaUnits`, -`LlmRequest.outputModalities`, and the reserved `generateMedia?`/`pollMediaJob?` methods — **shape -only**, with honest all-false adapter matrices and a fail-fast media guard until 1.AE) — merged in -**PR #11** (2026-06-10), landing the union members **before the seam's exhaustive consumers** exist. -Per the [sequencing plan](phases/phase-1-engine-and-llm.md#sequencing--parallelization), the **seam -policy lane is now complete**: **1.K — `FallbackChain` runner** (retryable/fatal routing on `LlmError`, -per-attempt usage → `CostTracker`, the ADR-0030 strip-the-reasoning-signature-on-failover obligation, -plus the no-blind-auth-retry / rate-limit-cooldown / no-failover-after-first-content-chunk nuances) — -merged in **PR #13** (2026-06-11) as a `FallbackChain` class + `withFallback` façade in `@relavium/llm`, -the seam's last Phase-1 policy layer, **completing 1.m2** with the cost tracker -([ADR-0011](../decisions/0011-internal-llm-abstraction.md), -[llm-provider-seam.md](../reference/shared-core/llm-provider-seam.md)). *(PR #13 also refreshed the -model-pricing table to current provider models and added Claude Fable 5.)* The active work is now the -**engine lane**: **1.L (`WorkflowYAMLParser`) ✅ Done (PR #14, 2026-06-12)** — `@relavium/core` is -scaffolded with a pure-TypeScript `WorkflowYAMLParser` that parses and validates `.relavium.yaml` -against the reconciled `WorkflowSchema`, with typed, field-named errors (**zero platform imports**) -and a hardened YAML decode profile ([ADR-0035](../decisions/0035-yaml-parser-dependency.md)) — and -**1.L2 (the `{{ … }}` interpolation engine) ✅ Done (PR #15, 2026-06-12)**: the runtime resolver + -pipe-filter registry (`json`/`length`/`default`/host-injected `read_file`) and the **parse-time -transitive secret-taint gate** ([ADR-0029(c)](../decisions/0029-tool-policy-hardening.md)), still -zero platform imports. **1.M (DAG builder + `RunPlan`) and 1.AB (the QuickJS-wasm expression sandbox) -are ✅ Done (PR #16, 2026-06-13)** — the plan layer (a deterministic topological `RunPlan`) and the -deterministic, resource-capped `condition`/`transform`/`merge_fn` evaluator ([ADR-0027](../decisions/0027-expression-sandbox.md)), -both pure-engine (zero platform imports). The engine lane has since landed **1.N (`WorkflowEngine` + -`RunEventBus` — the run loop) and 1.T (the built-in `ToolRegistry`) ✅ Done (PR #17, merged -2026-06-13)** — the serialized, completion-driven scheduler emitting the canonical, gap-free `RunEvent` -stream with the exactly-one-terminal-event invariant (through the in-house platform-free `RunEventBus` -behind the injected `ExecutionHost`/`NodeExecutor` seams), and the SSRF/allowlist/taint-aware tool -registry behind the `ToolHost` seam ([ADR-0036](../decisions/0036-run-loop-substrate-event-bus-and-execution-host.md)/[ADR-0037](../decisions/0037-engine-tool-execution-boundary.md)). -**1.N completes milestone 1.m3** (parse → DAG → run loop emits the canonical event stream) — its last -component (1.L.0/1.L/1.L2/1.M/1.N). **1.T is a 1.m4 component** that landed alongside it and is a -separate `AgentRunner` (1.O) join prerequisite — it does **not** close 1.m3. The engine lane has since -landed the **1.O `AgentRunner` join ✅ Done (PR #18, 2026-06-14)** — per-node LLM execution behind the -`@relavium/llm` seam: the host-injected provider-resolution boundary ([ADR-0038](../decisions/0038-agentrunner-llm-call-boundary.md)), -the correlation-agnostic turn core (reused by 1.V), the tool-call loop + classified failure ladder, and -the same-provider signed-reasoning replay ([ADR-0039](../decisions/0039-same-provider-reasoning-replay.md)) — -and the **node-type handlers (1.P) ✅ Done (PR #20, 2026-06-14)**: the six non-agent `NodeExecutor` arms -(condition / transform / fan_out / fan_in / input / output) composed by a `createDispatchingNodeExecutor` -alongside the 1.O agent arm — executor-only, no `engine.ts` change (the run loop already owns readiness, -skip-propagation, fan-in join scheduling, events, cancellation), `wait_first` executor-only (true -loser-cancel deferred), and a pre-merge BLOCKER secret-leak (the `input` handler emitting raw -`secret`-typed inputs into events) fixed via a `secretInputNames` masking gate on `NodeExecContext`. -**Checkpoint/resume (1.R) and the human gate (1.Q) then landed together — ✅ Done (PR #22, 2026-06-15)**: -the derived `Checkpointer` (state folded from the `run_events` log; no checkpoint table — ADR-0003) + -cross-process `resumeFromCheckpoint` (idempotent re-delivery, `workflow_mismatch` identity guard), and the -`human_in_the_loop` gate (suspend → notify → resume, plus the one-shot `setTimer` timeout port — `approve` -auto-resolves, `reject` fails with `run_timeout`). **Node retry (1.S) is ✅ Done (PR #24, 2026-06-15)** — the above-chain whole-node retry budget -([ADR-0040](../decisions/0040-node-retry-budget-above-the-chain.md) Part A: re-dispatch a whole node on a -retryable, `retry_on`-admitted failure up to `retry.max` attempts with abort-aware backoff and the non-terminal -`node:retrying`, `node:failed` staying the single terminal; the user-triggered retry-from-node Part B is -deferred to Phase-2). The last **1.m4** workstream — the **pre-egress budget governor (1.AC)** ([ADR-0028](../decisions/0028-workflow-resource-governance.md): -the `BudgetGovernor` pre-egress cost gate, `on_exceed` warn/fail/pause_for_approval, `budget:warning`/`budget:paused`/`run:timeout`, -the H3 approve-continues bypass, the per-attempt `FallbackChain` enforcement) — and the agent-first **`AgentSession` -(1.V)** entry point ([ADR-0024](../decisions/0024-agent-first-entry-point-agentsession.md): multi-turn -`start`/`sendMessage`/`cancel` over the shared `runAgentTurn` core, the hard turn cap → `turn_limit`, cost + -emission via an injected `SessionEventSink`) **then landed together — ✅ Done (PR #26, 2026-06-16)**. 1.AC closed -**1.m4** (the full engine stack), so the critical path now reaches **1.U — the end-to-end Node harness (the M2 -milestone)**, now unblocked; in parallel, **Lane C** (the 1.m5 sub-spine) continued from 1.V through **1.W** -(the `SessionEventSink`→`RunEventBus` wiring + per-session `sequenceNumber`/`SessionHandle` — ✅ Done, PR #28) -and **1.X** (session persistence — the `agent_sessions`/`session_messages` tables + `SessionMessageSchema` + -`SessionStore`, data-layer only — ✅ Done, PR #29); then **session checkpoint/resume (1.Y) + -export-to-workflow (1.Z) landed together — ✅ Done (PR #30, 2026-06-17)**. The **1.AA** chat-regression -harness is now ✅ Done (2026-06-17), completing **1.m5**; cost-event persistence remains a tracked deferral. - -> **Multimodal I/O — the shape is landed (1.AD ✅ Done, PR #11, 2026-06-10).** First-class -> image/audio/video I/O (input **and** output, incl. generate-media-by-rule) was decided on 2026-06-08: -> [ADR-0031](../decisions/0031-llm-seam-shape-amendment-multimodal-io.md) (the seam amendment) + -> [ADR-0032](../decisions/0032-desktop-rust-media-de-inline-amends-0018.md) (desktop Rust-side -> de-inline), from [multimodal-io-design-2026-06-07.md](../analysis/multimodal-io-design-2026-06-07.md) -> (nine maintainer decisions A1–A9), as the **1.AD–1.AH** sub-spine (1.m6, -> [phase-1](phases/phase-1-engine-and-llm.md)). **1.AD landed the seam shape before the exhaustive -> consumers 1.K/1.O** (the same cheap-window move as ADR-0030), so the media union members are -> non-breaking; the seam doc carries the full amendment section. **1.AE–1.AH (media -> input/engine/output + surfaces) are additive and do NOT gate M2** — the seam lane ran straight to -> **1.K** (✅ Done, PR #13), which closed it. **1.AE (media-input adapters + the shared SSRF policy -> primitive) is now ✅ Done (PR #32, 2026-06-18)**; **1.AF (engine media plumbing) is now ✅ Done (PR #33/#34/#35/#36, 2026-06-20)**; **1.AG (output generation) is now ✅ Done (PR #37, 2026-06-21)** (ADR-0045/0046; each section dual-reviewed + a final Opus-consolidated pre-merge review); **1.AH (the Phase-1-doable half — the four generative media-output adapters OpenAI-TTS / Gemini-Imagen / OpenAI-Sora / Gemini-Veo behind the seam + the shared opaque-jobId codec + the bare-MIME validator) is now ✅ Done (PR #38, 2026-06-21)** (each adapter dual/Opus-reviewed; A5 save_to-double-fetch deferred). **The 1.m6 sub-spine's Phase-1 work is complete** — 1.AH's remaining **host-wiring half is distributed across Phases 2–6** as recorded tasks (CLI §2.S, desktop §3.B, VS Code §4.N, managed/cloud media bullets), so it no longer gates Phase 1. *(Note: OpenAI Sora 2 + the Videos API are vendor-deprecated, shutdown 2026-09-24 — recorded in deferred-tasks.md; impact is the A3 adapter arm only.)* +## What is active now -> **Review-pass follow-ups landed (PR #12, merged 2026-06-11).** The 2026-06-10 engine/tooling -> review pass landed as docs/decisions only — no Phase-1 workstream changed: **MCP client scheduling** -> ([ADR-0034](../decisions/0034-mcp-client-sdk-dependency.md) pins the official TypeScript MCP SDK and -> binds the inbound client to **workstream 2.R** at the start of [build phase 2](phases/phase-2-cli.md), -> off the M3 critical path); the **`turn_limit` `ErrorCode`** (a hard session turn cap, distinct from -> the `[chat].max_messages` trim threshold); the **reserved `on_error` edge kind** -> (workflow-yaml-spec.md, not authorable in v1.0); and a CI **engine dependency-allowlist guard** + the -> pnpm install-script allowlist. No Phase-1 work changed; **1.K has since landed (PR #13)**, and -> **1.L has since landed (PR #14, 2026-06-12)** and **1.L2 (the `{{ … }}` interpolation engine + the -> parse-time secret-taint gate) is ✅ Done (PR #15, merged 2026-06-12)**; **1.M (DAG builder + -> `RunPlan`) and 1.AB (the expression sandbox) have since landed (PR #16, merged 2026-06-13)**; and -> **1.N (`WorkflowEngine` + `RunEventBus`) and 1.T (the built-in `ToolRegistry`) are ✅ Done (PR #17, -> merged 2026-06-13)** — **1.N closes 1.m3** (its last component); **1.T** (a 1.m4 component) is the -> other 1.O join prerequisite; **the `AgentRunner` join (1.O) is ✅ Done (PR #18, 2026-06-14)**; and the -> **node-type handlers (1.P) are ✅ Done (PR #20, 2026-06-14)**; **checkpoint/resume (1.R) + the -> human gate (1.Q) are ✅ Done (PR #22, 2026-06-15)**; and **node retry (1.S) is ✅ Done (PR #24, 2026-06-15)** -> (ADR-0040 Part A; the user-triggered retry-from-node Part B is deferred to Phase-2); and the **pre-egress budget -> governor (1.AC) + the `AgentSession` (1.V) entry point are ✅ Done (PR #26, 2026-06-16)** — 1.AC closed **1.m4**; -> then the **end-to-end Node harness (1.U) is ✅ Done (PR #27, 2026-06-16), reaching 🎯 M2** (the engine runs -> end-to-end). **The Phase-1 engine critical path is complete.** On the additive Lane C, the **`session:*` -> namespace (1.W) is ✅ Done (PR #28, 2026-06-17)** — the `SessionEventSink`→`RunEventBus` adapter (per-session -> `sequenceNumber`, `next`/`emit` overloads keeping the engine run-precise), the `SessionHandle`, and the -> combined `RunOrSessionEventSchema` gate — and **session persistence (1.X) is ✅ Done (PR #29, 2026-06-17)** — -> the `agent_sessions`/`session_messages` tables + migration, `SessionMessageSchema`/`AgentSessionSchema`, and -> the `SessionStore` + domain↔row mappers (data-layer only); then **session checkpoint/resume (1.Y) + -> export-to-workflow (1.Z) are ✅ Done (PR #30, 2026-06-17)** — `reconstructSessionState`/`AgentSession.resume` -> + the `serializeWorkflow`/`sessionToWorkflow` pair (the per-turn `AgentSession`→store wiring stays with 1.AA). -> **1.AA is now ✅ Done (2026-06-17)**, completing **1.m5**. On the 1.m6 multimodal sub-spine, -> **media-input adapters + the shared SSRF policy primitive (1.AE) are ✅ Done (PR #32, 2026-06-18)** — -> after a multi-round + final 8-dimension adversarial review (no SSRF bypass found); the SSRF *mechanism* -> half (host DNS-resolve + connect-by-validated-IP), per-modality FallbackChain gating, `mediaUnits`, and -> handle/url media resolution are deferred to **1.AF**. **1.AF (engine media plumbing) is ✅ Done -> (PR #33/#34/#35/#36, 2026-06-20):** P1 + P2 (PR #33); P3 + P4/D13 (PR #34); the P4 remainder (D12, D11, D15, -> D16, D17 + the byte-delivery review) (PR #35); the multi-agent + external review follow-ups (PR #36) — its -> three design ADRs (0042/0043/0044) are Accepted. -> **Merged (P1+P2, PR #33):** the `MediaStore` contract impls + `media_objects`/`media_references` tables -> (migration 0002), per-modality capability gating + `FallbackChain` skip, `deInlineMedia`, the strict -> `output_modalities`/`save_to` node fields, OpenAI `mediaUnits`, and **the I3 keystone — `deInlineMedia` -> at the one `#emitDurable` choke point**. -> **Merged (P3 + P4/D13, PR #34):** the binary media-egress + **SSRF mechanism half** (D9 — the -> `MediaUrlFetch` re-host hook + `fetchMediaBytes` SSRF-validated host reference: DNS-resolve + -> connect-by-validated-IP + per-hop redirect re-validation + streamed size-bound, wired at the choke point); -> the `FallbackChain` resolve-before-egress + **byte-free re-materialization sidecar** (D8 + D7); the -> **byte-delivery `Range` gate** (D13 — `MediaStore.readRange` + the engine-pure `validateByteRange`); and the -> dedicated **P3 egress/SSRF security-review** (independent adversarial — 0 blockers/highs). All green + -> Leakwatch-clean. -> **Merged (P4 remainder, PR #35):** `read_media` + the scope-set authz (D12), the -> terminal sweep + grace-window GC (D11), the `output_modalities` load-check (D15), the `save_to` write port -> (D16 — engine orchestration + the db `createFilesystemMediaWrite` fail-closed jail + the `run.id` -> interpolation namespace), and the per-modality media cost governor (D17 — `mediaCost`/`estimateMediaCost` -> folded into the existing cost cap, the widened `PreEgressHook`, `[defaults].media_cost_estimate`); plus the -> dedicated **P4 byte-delivery security-review** (two independent adversarial passes — 0 blockers/highs, no -> traversal/symlink-escape/arbitrary-write reachable) and the canonical-home docs (built-in-tools, -> config-spec, security-review, database-schema, workflow-yaml-spec). All green + Leakwatch-clean. **Deferred -> to 1.AH:** the keychain no-raw-key IPC test (no Phase-1 desktop surface; the gate is recorded per ADR-0044 -> §4). -> **Merged (review follow-ups, PR #36):** a **13-dimension, double-verified end-to-end review** of the merged -> 1.AF (no exploitable SSRF bypass; the I3 handle-only invariant holds end-to-end) + an external Sonar/CodeRabbit -> pass — H1 `agent:tool_call.toolInput` byte-redaction, H2 the unified exact-membership `output_modalities` gate -> (+ bidirectional set-equality), the GC grace basis bumped to de-reference time, an egress-mechanism test for -> `nodeMediaEgressDeps`, and the canonical-home doc-drift cluster. All green + Leakwatch-clean. The remaining -> Phase-1 work is **additive and off the critical path**: **1.AH's Phase-1-doable half (the four generative -> media-output adapters + shared opaque-jobId codec + bare-MIME validator) is ✅ Done (PR #38, 2026-06-21)**, -> closing the 1.m6 sub-spine's Phase-1 work — **Phase 1 is complete**. 1.AH's remaining host-wiring half is -> distributed across Phases 2–6 as recorded tasks (no longer a Phase-1 item). **Phase 2 (CLI, milestone M3) is unblocked.** +**Phase 2 — CLI (milestone M3) is unblocked and next.** The CLI is the first real +`@relavium/core` consumer and doubles as the engine's regression harness — validating the +engine API ergonomics before the desktop and VS Code surfaces. It also lands the inbound +MCP client (2.R, [ADR-0034](../decisions/0034-mcp-client-sdk-dependency.md)) off the M3 +critical path. See the [Phase 2 workstreams](phases/phase-2-cli.md). -Carry-over hardening is tracked in [deferred-tasks.md](deferred-tasks.md) — pick items up as Phase 1 -first touches each file. +Carry-over hardening is tracked in [deferred-tasks.md](deferred-tasks.md) — Phase 2 picks +items up as it first touches each file. Notable inheritances: 1.AH's host-wiring half +(distributed across Phases 2–6), the media-egress host-side SSRF mechanism, and the +keychain no-raw-key IPC test. ## Not started yet -The surfaces and the cloud — everything after the engine critical path: the CLI, the -desktop app, and the VS Code extension (Phases 2–4), then **Product Phase 2** — first -**managed inference** ([phase-5-managed-inference.md](phases/phase-5-managed-inference.md), -the opt-in `managed` gateway, engine still local), then the **cloud execution layer and web -portal** ([phase-6-cloud-execution-portal.md](phases/phase-6-cloud-execution-portal.md)), -the two decoupled per Option B. See the [phase index](README.md#phase-index) and the +The surfaces and the cloud — everything after the engine critical path: the desktop app +(Phase 3) and the VS Code extension (Phase 4), then **Product Phase 2** — first **managed +inference** ([phase-5-managed-inference.md](phases/phase-5-managed-inference.md), the opt-in +`managed` gateway, engine still local), then the **cloud execution layer and web portal** +([phase-6-cloud-execution-portal.md](phases/phase-6-cloud-execution-portal.md)), the two +decoupled per Option B. See the [phase index](README.md#phase-index) and the [milestone spine](README.md#global-milestone-spine) (M3 onward). diff --git a/docs/roadmap/deferred-tasks.md b/docs/roadmap/deferred-tasks.md index 88546c68..75acda13 100644 --- a/docs/roadmap/deferred-tasks.md +++ b/docs/roadmap/deferred-tasks.md @@ -2,7 +2,7 @@ > Status: Living -> Last updated: 2026-06-19 +> Last updated: 2026-06-21 - **Related**: [current.md](current.md), [README.md](README.md), [phases/phase-0-foundations.md](phases/phase-0-foundations.md) @@ -15,86 +15,10 @@ risk/benefit favors waiting. None block a shipped milestone. Pick them up opport Severity is the review's verified rating. Check an item off in the PR that resolves it. -> **2026-06-07 hardening pass + maintainer decisions:** the built-package items (shared / db / llm / -> root tooling / docs) were re-verified against current code and **29 were resolved**, then the seven -> open decisions were ruled: **`$ref`** (keep the door open — schema now accepts the union, engine -> resolves the file), **config strictness** (`.strict()`), **`engine-strict`** (enforce), **branded -> ids** (plain strings stay — code-style note), and **turbo `inputs`** (keep the safe default) are all -> settled and checked off. **Still open:** the **`LICENSE`** (HodeTech is drafting its own commercial -> license), **blocked** work (live-nightly keys, non-Anthropic pricing — needs the live pages), and -> **three explicitly deferred** items (each annotated with why: the `z.unknown()` presence check, the -> dist packaging smoke test, and local `format:check` via turbo). -> -> **2026-06-08 multimodal decision pass:** **first-class multimodal I/O** is now fully designed and -> decided — the analysis ([multimodal-io-design-2026-06-07.md](../analysis/multimodal-io-design-2026-06-07.md)), -> [ADR-0031](../decisions/0031-llm-seam-shape-amendment-multimodal-io.md) (seam) + -> [ADR-0032](../decisions/0032-desktop-rust-media-de-inline-amends-0018.md) (desktop Rust de-inline) -> landed, nine maintainer decisions (A1–A9) ruled, and implementation scheduled as the **1.AD–1.AH** -> sub-spine (1.m6). The item is checked off; its **not-yet-coded pieces are carried as the seven -> multimodal forward-obligations** below (SSRF primitive, async-job ADR, media cost estimate, `partialRef` -> semantics, `workspace` authz scope, retention/GC table, `vision`-alias retirement) so nothing is lost. - -> **2026-06-10 engine/tooling review pass (landed in PR #12, merged 2026-06-11):** a review of the -> engine, tool, and CI surfaces against the current contracts produced a small set of additions, -> recorded in their sections below: the **tool-output size gate + spill-to-disk** (1.T), -> **conformance tool-loop / cache-hit scenarios** (1.F follow-up), a **token-estimate accuracy** watch -> item (1.AC), the **Leakwatch CI gate** (deferred pending a distribution path), and a -> **dependency-bump cooling window** (pending a pnpm major). The same pass settled three decisions -> outside this file: the MCP client dependency and scheduling (ADR-0034 / workstream 2.R), the -> reserved `on_error` edge kind (workflow-yaml-spec.md), and the `turn_limit` `ErrorCode` -> (constants.ts + sse-event-schema.md). It also landed a CI **engine dependency-allowlist guard** -> (`tools/engine-deps/check.mjs`) and the pnpm **install-script allowlist**. + ## Decisions needed (maintainer call) -- [x] **Workflow `agents:` `$ref` support** — the - [workflow YAML spec](../reference/contracts/workflow-yaml-spec.md) allows an `agents:` - entry to be a `$ref` to an external `.agent.yaml`, but `WorkflowSpecSchema.agents` accepted - inline `AgentSchema` only. **Decided (2026-06-07): keep the door open** — `agents:` now accepts - `z.union([AgentSchema, AgentRefSchema])` (`{ $ref }`, `.strict()`); the duplicate-id check skips - `$ref` entries; **file/path resolution + path-traversal/SSRF hardening stay the engine's job** - (the pure/sync shared schema never reads files). Code now matches the spec. *(workflow.ts)* -- [x] **Branded id types** — `runId`/`nodeId`/`gateId`/`workflowId`/`agentId` are all plain - `string`. **Decided: plain strings stay** (deliberate) — recorded in - [code-style-typescript.md §Naming](../standards/code-style-typescript.md); validation is at - the Zod boundary and branding adds cross-seam friction for little payoff. Revisit via an ADR if a - real id-mixup bug class appears. *(minor · packages/shared/src/run.ts, node.ts)* -- [x] **`LICENSE` file + root `license` field** — **Done (2026-06-21, pre-Phase-2 housekeeping).** - Authored the **Relavium Proprietary License** (HodeTech, all rights reserved, with an - enforcement/legal-remedies clause) and added the root `LICENSE` file; set root - `package.json` `"license": "SEE LICENSE IN LICENSE"` (npm-correct pointer to the custom - license — not the forbidden `UNLICENSED`/OSS placeholder). This was the one true - before-Phase-2 blocker (Phase-2 exit criterion 7 / `npm i -g relavium`, 2.L). The text is - AI-drafted from the maintainer's intent — **recommend a legal review before public - distribution.** *(LICENSE; package.json, repo root)* -- [x] **`node:started.nodeType` enum vs free string** — currently an unconstrained - `nonEmptyString`. Decide whether the SSE event should carry the engine node-type enum - (add `ENGINE_NODE_TYPES` to constants and `z.enum(...)`) or stay free-string for - forward-compat, and record the choice. *(nit · packages/shared/src/run-event.ts:47)* -- [x] **`MaskedSecret` named contract** — `run:started.inputs` documents secret masking only - in a comment. Export a `MaskedSecret` type/schema (`{ secret: true; ref: string }`) so the - masked shape is a named contract every surface renders. *(nit · run-event.ts:39)* -- [x] **`composite`/project references (reconcile 0.B)** — phase-0-foundations.md 0.B calls - for `composite`/project-reference tsconfig fields that were not implemented. Either add a - `packages/*` library base with `composite: true` + `references` (db → shared) and build via - `tsc -b`, or record that turbo `^build`-ordering is the deliberate final design and update - the 0.B callout. *(minor · tsconfig.base.json, packages/*/tsconfig.json)* -- [x] **First-class multimodal I/O (the `vision` flag is only the tip)** — `vision: true` is set for - Anthropic / OpenAI / Gemini, yet `ContentPart` had no media arm, so media could only reach a provider - through the `providerOptions` escape hatch in a vendor-specific shape. **DONE (2026-06-08): analysis + - ADRs landed, decisions ruled, scheduled.** The design analysis is - [multimodal-io-design-2026-06-07.md](../analysis/multimodal-io-design-2026-06-07.md) (three-perspective - adversarial review, 8 blocking issues resolved); the binding records are - [ADR-0031](../decisions/0031-llm-seam-shape-amendment-multimodal-io.md) (the seam amendment — media - `ContentPart`/`StreamChunk` arms, `CapabilityFlags.media` with `input{image,audio,video,document}` + - `outputCombinations`, `Usage.mediaUnits`, `LlmRequest.outputModalities`, reserved - `generateMedia?`/`pollMediaJob?`, the `MediaStore`/`deInlineMedia`/handle model) and - [ADR-0032](../decisions/0032-desktop-rust-media-de-inline-amends-0018.md) (desktop Rust-side - de-inline). **Nine maintainer decisions (A1–A9)** are recorded in ADR-0031's *Maintainer decisions* - table. **Implementation is scheduled as the 1.AD–1.AH sub-spine (1.m6, - [phase-1](phases/phase-1-engine-and-llm.md))** — 1.AD (seam shape) lands before 1.K/1.O; 1.AE–1.AH are - additive. The residual forward-obligations below carry the not-yet-coded pieces so nothing is lost. - *(content.ts; the adapters' `*_SUPPORTS`; [llm-provider-seam.md](../reference/shared-core/llm-provider-seam.md))* ### Multimodal forward-obligations (carry the not-yet-coded pieces — see ADR-0031) @@ -109,26 +33,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol replacement video model/API, retarget the A3 arm; otherwise **disable/remove** the Sora arm (the `generateMedia` `video` dispatch + `pollMediaJobSora` + their tests) leaving the seam + Veo intact. Low priority until a replacement lands or the date nears. *(packages/llm/src/adapters/openai.ts; 1.AH/Phase-2)* -- [x] **Media-arm integrity metadata (Y3) — DECIDED 2026-06-09 (ADR-0031 amended), land at 1.AD.** The - durable form (`DurableMediaPart`) carries an optional **`byteLength?`** + audio/video **`durationMs?`**, - host-populated at the `deInlineMedia` boundary; **no `checksum`** (the `media://sha256-` handle IS - the sha256); **no `width`/`height`** in Phase A (render-only). **Must ship in the 1.AD seam shape** - (before 1.K/1.O exhaustive consumers) — adding a union-arm field later is breaking. `byteLength` is what - the byte-delivery Range check bounds against. *(ADR-0031 "Amended 2026-06-09"; multimodal-io-design §3.2; 1.AD)* - **✅ Landed at 1.AD (PR #11, 2026-06-10):** `byteLength?`/`durationMs?` ship on `DurableMediaPart` only - (the in-flight arm stays lean — parse-stripped, tested), with the `durationMs`-is-audio/video-only rule - enforced on both the standalone schema and the durable union. -- [x] **Shared SSRF range-primitive (the `url`-carrier precondition)** — the one shared HTTPS-only / - block-private-loopback-link-local-metadata-CGNAT / DNS-resolution + per-hop-redirect-revalidation / - IPv4-mapped-IPv6-decode primitive that `assertHttpsBaseUrl` (openai.ts) is the best-effort placeholder - for. security-review.md mandates **one** primitive across all egress paths; the media `url` carrier - (input + provider-returned output) is gated **feature-flag-OFF** until it lands. **Landing (1.AE, PR #32):** - `isPrivateOrLocalHost()`, `extractHttpsHost()`, `urlHasCredentials()` shipped in `@relavium/shared` - (pure sync, platform-free) with 40+ SSRF tests. `assertHttpsBaseUrl` in openai.ts delegates to the - shared functions + `new URL()` normalization. `MEDIA_URL_SOURCE_ENABLED` flipped to `true` with - per-URL SSRF validation at the seam boundary (`refineInFlightMediaPart`). The host-side DNS/connect - enforcement for the **media** url path is pulled into 1.AF on a new bytes-shaped media-egress capability ([ADR-0043](../decisions/0043-media-egress-failover-rematerialization-ssrf.md)); the general tool/MCP fetch enforcement lands with that surface hook (see below). - *(security-review.md; openai.ts; 1.AE)* - [ ] **Host-side SSRF enforcement in `EgressCapability.fetch` (DNS resolve + connect-by-validated-IP + per-hop redirect re-validation)** — the shared SSRF range-primitive (1.AE) covers the **policy** half (literal format checks on URLs and hostnames); the **mechanism** half — resolving a hostname to its IP, validating the IP against the same range block, pinning the connection to that IP (connect-by-validated-IP), @@ -137,18 +41,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol hook, it must apply these runtime checks. The current `assertHttpsBaseUrl` and `refineInFlightMediaPart` URL validation are construction-time / seam-ingestion-time policy; they catch malformed URLs but cannot catch DNS rebinding or a public hostname resolving to a private IP. **Scope split (resolving the earlier "Phase 2" framing):** the **media** url-carrier mechanism is **pulled into 1.AF** on a new bytes-shaped media-egress capability ([ADR-0043](../decisions/0043-media-egress-failover-rematerialization-ssrf.md)); the **general tool/MCP** `EgressCapability.fetch` enforcement still lands when the desktop/CLI surface implements that fetch hook. *(packages/core/src/tools/types.ts; security-review.md; media → 1.AF/ADR-0043; tool/MCP → surface fetch hook)* -- [x] **Async media-job ADR + engine loop (`generateMedia`/`pollMediaJob` behavior, A5) — ✅ DONE (1.AG, PR #37, 2026-06-21; the A5 obligation discharged in Sections A/C/D).** - [ADR-0045](../decisions/0045-async-media-job-loop-poll-checkpoint-resume-cancel.md) is written + Accepted, and - the engine-owned **poll / checkpoint / resume / cancel loop** for minute-scale LROs (Sora/Veo) landed in 1.AG - Section D (the run loop 1.N + checkpointer 1.R, reusing `LlmError` classification): `media_job:submitted` park, - the derived `pendingMediaJobs` slot, re-attach-on-resume (MJ-1), a `host.setTimer` poll cadence with exp-backoff, - deadline→retryable-timeout, gate-vs-media resume disambiguation (AG-A-FC-3), and cancel→abort→terminal-sweep — - proven end-to-end with a stub async provider + the manual-timer harness. The **Section-C wiring obligation** - also landed: the generative-vs-chat routing FORK moved up into `AgentRunner.executeAgent` (a `'generative'` - model dispatches to `generateMedia` BEFORE the turn loop), so the `requestsMediaOutput` guard in - [`agent-turn.ts`](../../packages/core/src/engine/agent-turn.ts) only ever sees `'chat'` models — ADR-0046 §1's - `media_surface: 'chat'` conjunct holds STRUCTURALLY (the guard's JSDoc `NOTE` points at the fork). The remaining - VENDOR-adapter + host-wiring work is tracked in the 1.AH entries below. *(1.AG Sections A–E — ✅ merged, PR #37)* - [ ] **Streaming media triad (`media_start`/`media_delta`/`media_end`) — host-deferred ([ADR-0046](../decisions/0046-inline-media-out-via-generate-streaming-triad-deferred.md) §4).** 1.AG Section B delivers inline media-out through the non-streaming `generate()` path (the in-flight `media` `ContentPart` is de-inlined at `#emitDurable`). The **streaming** triad stays RESERVED: its Node @@ -180,35 +72,12 @@ Severity is the review's verified rating. Check an item off in the PR that resol (no mis-bill). **Test follow-up:** the realized-cost vertical (`realizedMediaCost` → a non-zero `cost:updated`) cannot be exercised end-to-end until a generative model carries a rate; the cost MATH is unit-tested via `mediaCost` against a constructed rate, and a non-zero dispatch assertion lands with these rows. *(packages/llm/src/pricing.ts; verified rates → 1.AH)* -- [x] **`generateMedia` SYNC adapters (OpenAI-TTS A1, Gemini-Imagen A2) + image knobs (A6) — ✅ Done (1.AH, PR #38, 2026-06-21).** *(STILL DEFERRED below: multi-image `count > 1` + verified pricing rows.)* - Section C wired `generateMedia` SYNC for **OpenAI image** (gpt-image-1 `images.generate` → base64); **1.AH A1 - wired OpenAI-TTS audio** (`audio.speech` binary → base64 + `response_format`↔MIME map, no new dep). The - remaining sync generator is **Gemini-Imagen** (`generateImages` → `generatedImages[].image.imageBytes`, which - needs a `GeminiTransport.generateImages` extension to keep conformance vendor-free). Not runtime-reachable until the - per-model surface lookup is host-wired (above), so it lands written-now/E2E-at-1.AH-host-wiring. Two bounded image follow-ups - ride along: (a) **multi-image `count > 1`** — the SYNC `MediaGenResult.media` carries a SINGLE part, so the OpenAI - adapter currently rejects `count > 1` (never bill-N-deliver-1); delivering N needs an additive `media: MediaPart[]` - seam amendment (ADR-0031). (b) **image-gen knobs** (`size`/`quality` via `providerOptions.image`) — **✅ Done (A6, PR #38)** on the gpt-image-1 - adapter (body-only, narrowed to valid values, never the SDK RequestOptions); the engine POPULATING - `providerOptions` for a generative call is the Phase-2 host-wiring half. *(packages/llm/src/adapters/{openai,gemini}.ts; types.ts MediaGenResult; 1.AH)* - [ ] **Rate-carrying media representation for raw PCM (1.AH A1 known-limitation).** OpenAI TTS `pcm` is headerless 16-bit LE PCM at 24 kHz, but the seam's `MediaMimeTypeSchema` forbids MIME parameters, so the bare `audio/L16` cannot carry `;rate=24000` (RFC 2586's default is 8 kHz) — a consumer of an `audio/L16` part must assume 24 kHz. Mirrors the pre-existing chat-audio `pcm16 → audio/L16` convention; the self-describing containers (mp3/opus/aac/ flac/wav) are unaffected. A rate-carrying media representation (or dropping bare-PCM from the offered set) is the fix. Low priority — opt-in niche format only. *(packages/shared/src/content.ts MediaMimeTypeSchema; packages/llm/src/adapters/openai.ts; low · 1.AH/Phase-2)* -- [x] **Sora/Veo async-media ADAPTERS (`generateMedia`→`{ jobId }` + `pollMediaJob`) — ✅ Done (1.AH A3/A4, PR #38, 2026-06-21).** - 1.AG Section D landed the ENGINE-owned async-job loop (park on `media_job:submitted`, the `host.setTimer` poll - cadence, exp-backoff to `pollMaxMs`, deadline→retryable timeout, cross-process re-attach (MJ-1), gate-vs-media - resume disambiguation (AG-A-FC-3), cancel-aborts-the-in-flight-poll + terminal sweep) — proven end-to-end with a - STUB async provider + the manual-timer harness. The **OpenAI/Sora (A3)** + **Gemini/Veo (A4)** adapters now - implement `generateMedia`(video)→opaque `jobId` + `pollMediaJob`, via the shared `encode/decodeMediaJobId` codec - (a stateless `rlv-mediajob:1:` bijection — cold-restart re-attach with no adapter state, - ADR-0045 §7); the engine LRO drives them. **N4 conformance obligation discharged:** the opacity test (minted - `jobId` ≠ the raw vendor id) ships in `openai.test.ts`, and strip-raw holds structurally (the `MediaJobStatus` - union carries no `raw` slot). **STILL DEFERRED:** the per-model `media_surface` host lookup (Phase-2 §2.S) + - verified pricing rows. **⚠ OpenAI Sora 2 + Videos API are vendor-deprecated (shutdown 2026-09-24)** — see the - Multimodal forward-obligations entry; impact is the A3 adapter arm only. *(packages/llm/src/adapters/*; 1.AH done / surface + rates deferred)* - [ ] **Node-retry of a PARKED media job — deferred (1.AG Section D, ADR-0045 §3 "MAY re-dispatch").** The node-retry budget (1.S) wraps the executor `#dispatch`; an async media job parks AFTER dispatch returns, and the engine's out-of-band poll loop settles a deadline/poll failure as `node:failed` (retryable for a timeout) directly @@ -387,32 +256,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol surfaces add i18n: a CI test that **fails** on a missing/extra translation key (parity), **zero conditional logic in translation data** (data ≠ code), and a dead/unused-string lint. Recorded now; lands with the Phase-2/3/4 surface i18n work (no consumer yet). *(a `docs/standards/` entry or skill; Phases 2–4)* -- [x] **Tool-output size/token gate + spill-to-disk (1.T).** Today only the *event* `outputSummary` is - truncated; the tool result handed back to the model has no bound, so one oversized `read_file` / - `http_request` / MCP result can blow the context window (a cost/DoS surface ADR-0028's pre-egress - governor cannot see, since the damage lands in the *next* request). Add to the `ToolRegistry` - dispatch path (1.T): a byte/token ceiling per tool result with an explicit truncation marker, and - for over-threshold output (e.g. >2000 lines / >50KB) spill the full output to a workspace-scoped - file and hand the model a bounded preview + the path (readable via the normal FS-scope-tiered - tools). Behavior belongs in [built-in-tools.md](../reference/shared-core/built-in-tools.md) when - implemented. *(1.T; built-in-tools.md)* **✅ Landed in 1.T:** `boundForModel` (bounding.ts) applies a - byte+line ceiling, emits a head/tail preview + explicit truncation marker, and spills the full text to - the host's run-scoped `outputStore` (handle in the marker); applied in `registry.dispatch` (returns - `truncated`) under the one cancellation-precedence ladder. Documented in - [tool-registry.md §result-bounding-and-spill-to-file](../reference/shared-core/tool-registry.md#result-bounding-and-spill-to-file). -- [x] **Cumulative cost is not restored on cross-process resume (cost-event persistence) — 1.AC/1.R.** **Done - (maintainer-approved, the node:completed-carry variant).** `cost:updated` is streamed (`#nodeEmit` → bus), - not persisted, so the `reconstructCheckpointState` fold never saw it — a resumed run's - `cumulativeCostMicrocents` (and the governor) restarted near 0. **Fix:** the durable `node:completed` now - carries an optional `cumulativeCostMicrocents` (run-event.ts) — a snapshot of the run-wide running total at - the node boundary, populated by the engine (`#completeNode`) and folded on resume with a monotonic `Math.max` - that reconciles with the existing `budget:paused.spentMicrocents` restore (checkpoint.ts). So a run paused at - **any** gate (plain human OR budget) now resumes with the right spend; a gate-less crashed-mid-run is - reconciled to `run:failed` (not resumed), so its cost-loss is moot. Chosen over persisting `cost:updated` - (which would add hot-path durable writes + a delivery-ordering change): zero new events, one additive - forward-compatible field, folds at boundaries the store already persists — no ADR needed. Pinned by a - checkpoint.test.ts unit test (plain-human-gate restore) + the 1.U flagship harness (post-resume - `run:completed.totalCostMicrocents` reflects the pre-gate cost). *(packages/core/src/engine/engine.ts `#completeNode` + checkpoint.ts; packages/shared/src/run-event.ts; 1.AC/1.R)* - [ ] **Pre-egress token-estimate accuracy — watch item (1.AC).** The ADR-0028 governor blocks on `worstCaseNextEstimate(maxTokens)` from `[defaults].max_tokens_estimate`. Record the open question: does the estimate need provider-accurate token counting (from the seam's model meta / @@ -436,12 +279,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol > output, and carrying resolved-interpolation provenance into the untrusted-content-as-data boundary — > are already **1.O acceptance criteria** (phase-1 §1.O), so they are not duplicated here. -- [x] **Structured-default reference flow (→ 1.M).** *Resolved in 1.M.* Boundary **decided and - pinned**: a structured input default is **opaque data**, never template-interpolated — only **string** - defaults carry templates. A `{{ … }}` nested in a structured default (`default: { token: '{{secrets.x}}' }`) - is therefore neither resolved nor taint-scanned, and is not a leak vector (`resolveTemplate` is - single-pass, so `{{inputs.cfg | json}}` emits the literal `{{secrets.x}}`, not a resolved secret). Pinned - by `analyze.test.ts` ("treats a STRUCTURED input default as opaque data"). *(packages/core/src/interpolation/analyze.ts)* - [ ] **Frozen `ctx` checkpoint transport must use `structuredClone` (→ ctx-threading, not 1.R).** `resolveContext` returns an `Object.freeze`d **null-prototype** map so a `__proto__`/`constructor` context key is a safe own property. That guard is in-memory only: persisting/transporting it via @@ -470,14 +307,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol `tool_call` part has no field for it, so Gemini 3 function-calling continuations cannot replay it (and can themselves 400). Needs a continuation-metadata carrier on the canonical `tool_call`/`reasoning` parts plus adapter capture/replay. *(high · packages/llm/src/adapters/gemini.ts:193-198, packages/shared/src/content.ts:419-441; ADR-0030 follow-up)* -- [x] **DeepSeek surviving-reasoning replay** — **Confirmed correct + locked (engine-hardening pass).** The - OpenAI-compatible adapter CAPTURES `reasoning_content` inbound (`mapContent` → a `reasoning` part) but - intentionally **drops it on egress** (`toOpenAiMessages` lowers only text + tool_call parts; openai.ts:256-260, - "reasoning is ephemeral and never replayed, ADR-0030"). For DeepSeek this is the CORRECT direction: - `reasoning_content` is output-only — the API 400s if it is echoed back in an input message, and - deepseek-reasoner does not need prior reasoning to continue. So no seam-shape carrier is needed (unlike the - Anthropic-redacted / Gemini-thoughtSignature items above). Pinned by an openai.test.ts lock-test (a prior-turn - reasoning part never reaches the request body). *(packages/llm/src/adapters/openai.ts; ADR-0030/0039)* - [ ] **`output_schema` deep JSON-Schema conformance** — 1.O validates an `agent` node's `output_schema` node-side but **parse-as-JSON only** (the seam's `responseFormat` is a request hint; a schema-violating-but-valid JSON output, e.g. `{"wrong":true}` for a `{ n: number }` schema, currently @@ -490,25 +319,10 @@ Severity is the review's verified rating. Check an item off in the PR that resol `agent:token.model` uses `activeModel` (updated from the *succeeding* attempt record, which fires after the stream), so a *cross-model pre-content failover* attributes that turn's tokens to the prior model. A precise fix needs a `FallbackChain` `onAttemptStart`/attributed-stream hook (a seam change). *(low · packages/core/src/engine/agent-turn.ts; packages/llm/src/fallback-chain.ts)* -- [x] **Per-attempt pre-egress budget gate (1.AC)** — closed by 1.AC (PR #26). The precise per-egress budget - check now rides the `FallbackChain` **pre-attempt** hook, so every attempt — including a failover to a pricier - model — is capped; the loop-top `awaitPreEgress` in `runAgentTurn` adds the zero-egress-on-cancel guard + - primary-model early check (the intentional double gate). *(closed · ADR-0028; ADR-0038; 1.AC, PR #26)* - [ ] **Multi-tool result ordering in the turn core** — `dispatchToolCalls` appends tool-result messages in dispatch-completion order; for v1.0 (single tool call per `tool_use` stop) this is moot — and 1.V now reuses the core on that single-tool path. A parallel-tool provider should order by the accumulator's `toolOrder`; re-home to whatever future parallel-tool work first enables it. *(low · packages/core/src/engine/agent-turn.ts; future parallel-tool)* -- [x] **Secret-into-`run.outputs` runtime taint (ADR-0029(c) follow-up)** — an `agent` node cannot launder a - secret into `run.outputs` (it emits LLM text only), so this is **not** 1.O's to own; it belongs to the - `transform` / sandbox node (1.P / 1.AB) that can return a secret-derived value. 1.O's only obligation is to - refuse a tainted `{{ run.outputs[…] }}` reference *if* such a marker reaches it; the static parse-time - `analyzeSecretTaint` gate covers the authored template graph. Record as a scoped ADR-0029 amendment when 1.P/1.AB - lands. *(medium · packages/core/src/interpolation/analyze.ts; ADR-0029(c), 1.P/1.AB)* **✅ Closed at the source - by 1.P (PR #20):** `buildExpressionScope` (scope.ts) masks `secret`-typed inputs out of the sandbox scope, so a - `transform` / `condition` / fan_in `merge_fn` reads the `{ secret, ref }` marker — never the raw secret — and - therefore cannot derive a secret value to launder into `run.outputs`. The vector is cut at the read, so no runtime taint - on the output is needed. (The only remaining secret-into-egress path is the agent prompt — tracked separately - below as a 1.O policy item, and it is provider egress, not an event-payload leak.) > **2026-06-14 (PR #18 final review follow-ups).** Confirmed by the multi-dimensional pre-merge review; > non-blocking, recorded so they aren't dropped. @@ -520,21 +334,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol `run.outputs`/`read_file` references there (analogous to the secret-taint gate — do **not** drop the field from `nodeReferenceSites`, which would remove the existing secret-leak protection). A pinning test already asserts an untrusted `run.outputs` value never reaches the system string. *(medium · packages/core/src/interpolation/analyze.ts, collect.ts; SEC-1)* -- [x] **Concurrent-agent dispatch coverage** — N agent nodes run in parallel under `max_parallel`, each - calling `runAgentTurn` against the **shared** `ToolRegistry`. Verified reentrant (per-call locals; each node - builds its own `FallbackChain` + `CostTracker`), but there is **no** concurrency test. Add one: two agent - vertices dispatching the same tool in parallel, asserting gap-free per-node event sequences and no - cross-node cost/emit bleed. *(low · packages/core/src/engine/agent-runner.ts; engine `max_parallel`)* - **✅ Added (hardening pass):** `agent-runner.e2e.test.ts` "runs two agent nodes concurrently against the - shared executor" — `max_parallel: 2`, two agent vertices on one executor instance, asserting a gap-free - global sequence and that each node's `agent:token` events carry their own `nodeId` (no cross-node bleed). -- [x] **Combined tool-loop DoS bound (turns × corrections)** — **Done (engine-hardening pass).** The - "product" framing was imprecise: the bounds are NOT multiplicative. `maxToolTurns` is the worst-case - **egress ceiling** (≤ `maxToolTurns + 1` provider calls); `maxToolCorrections` is a **monotonic sub-budget** - that can only end the turn EARLY with `tool_failed` (a genuine round never resets it). Documented on - `AgentTurnLimits` (agent-turn.ts) and pinned by an interleaving test (correctable / genuine / correctable / - correctable → `tool_failed` at turn 4, far under `maxToolTurns`), asserting corrections accumulate across the - interleaved genuine round and egress stays bounded. *(low · packages/core/src/engine/agent-turn.ts)* - [ ] **Multimodal tool-result through the adjacent-message + redaction paths** — all 1.O coverage exercises text/JSON tool args + content; confirm image/media tool-result blocks survive the Anthropic adjacent-role merge (no dropped blocks / no double-merge with `stripReasoningParts`) and the redaction path. *(low · packages/llm/src/adapters/anthropic.ts; 1.AF)* @@ -560,15 +359,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol `AbortSignal` cannot cancel one branch without cancelling the run, and a handler cannot cancel sibling vertices. The engine authors already flagged this as a "1.P refinement" (engine.ts:26-28). Promote to a scoped 1.N/engine change (possibly an ADR) only when a real workflow needs it. *(low · packages/core/src/engine/engine.ts, packages/core/src/engine/node-handlers/fan-in.ts; run-plan.md §fan-in)* -- [x] **Workflow-context (`ctx.*`) threading into expression/agent scope** — the `condition`/`transform`/ - `merge_fn` sandbox scope (and the AgentRunner's prompt `RunScope`) bound `ctx: {}` — the authored - `context:` namespace was not resolved/threaded (a `{{ctx.key}}` template resolved, but a bare `ctx.key` - JS-expression read saw `{}`). **✅ Fixed (the ctx-threading PR, 2026-06-15):** the engine resolves the workflow - `context:` once at run start (a new `#resolveContextOrFail` step using injected - `WorkflowEngineDeps.resolverCapabilities`, with a `validation` failure path that closes the run), threads - the frozen `ctx.*` via the new `NodeExecContext.ctx` seam field, and both consumers (`buildExpressionScope`, - the AgentRunner's `resolvePrompt`) read it; `ctx` is **re-resolved on resume** (not checkpointed). Pinned by - engine e2e + a transform unit test. *(packages/core/src/engine/engine.ts, node-handlers/scope.ts, agent-runner.ts)* - [ ] **`secret`-typed input flowing into an agent prompt (1.O parallel to the 1.P fix)** — the AgentRunner resolves `{{ inputs. }}` in a `prompt_template` against the **raw** `RunScope` (agent-runner.ts), so a `secret`-typed input interpolates raw into a USER message sent to the provider. This is provider **egress** @@ -580,16 +370,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol masking). **Implement in Phase-2 workstream 2.D** (the first live consumer of `prompt_template` via `relavium run`), not before — no live `prompt_template` consumer exists yet. *(low → 2.D · packages/core/src/engine/agent-runner.ts + the parse/validation layer; security-review.md)* -- [x] **Reject a plain (handle-less) edge whose `from` is a `condition` node (1.M validation)** — a `condition` - routes only via `branches[].target_node`/`default` (materialized edges); a separately-authored plain edge - `from: ` (no `:handle`) makes its target a dependent that the handler's `selected` never names, so - the run loop skip-propagates it — a silently-dead downstream rather than a parse error. Add a structural - validation in `dag.ts` (`validateStructuralEdge`) rejecting a handle-less edge out of a condition (reuse - `invalid_handle`, or a `condition_requires_handle` kind). Pre-existing 1.M edge-validation gap, not a 1.P - handler defect. *(low · packages/core/src/dag.ts; workflow-yaml-spec.md §edges)* **✅ Fixed (2026-06-14 - hardening pass):** `validateStructuralEdge` rejects a handle-less edge from a `condition` with an - `invalid_handle` issue (no existing fixture/spec used one — the spec routes via `branches` + `nodeId:when` - handles); pinned by `dag.test.ts` and documented in workflow-yaml-spec.md §edges. ## Node retry (1.S) follow-ups @@ -676,128 +456,17 @@ Severity is the review's verified rating. Check an item off in the PR that resol `ZodEffects` member; the correct fix adds the `hasOwnProperty` check to the existing outer `RunEventSchema.superRefine` (where the runId/sessionId cross-check already lives). Low value, left for the consumer that needs the guarantee. -- [x] **Standalone `MergeNodeSchema` gap** — `merge_strategy:custom` without `merge_fn` only - fails at `WorkflowSchema` level (a discriminated-union member can't carry the cross-field - rule). Document the partial node-level validation and add a `node.test.ts` case pinning the - gap as intentional. *(minor · node.ts:85-92, workflow.ts:104-113)* -- [x] **O(n²) duplicate-id check in `AgentSchema`** — uses `indexOf`-in-`filter` while - `workflow.ts` uses an O(n) `Set`. Reuse a shared `reportDuplicates` helper so both schemas - share the single O(n) implementation. *(nit · agent.ts:109-110)* -- [x] **Per-provider temperature ranges** — the shared `temperatureSchema` is the - provider-agnostic `[0, 2]` envelope, but Anthropic accepts only `[0, 1]`. Enforce/clamp the - provider's real range in the `@relavium/llm` adapter (Phase 1, where request validation - lives) so a `provider: anthropic` + `temperature > 1` agent fails fast — without coupling the - shared contract to a provider's current API limit. *(review · agent.ts, common.ts)* -- [x] **Config-schema strictness parity** — `GlobalConfigSchema` / `ProjectConfigSchema` / - `ChatConfigSchema` were **not** `.strict()`, so a typo in a committed `config.toml` / - `project.toml` key was silently dropped — asymmetric with the authored-YAML strictness - ([ADR-0023](../decisions/0023-strict-authored-yaml-validation.md)). **Decided: fail loud** — all - three (and their nested `preferences`/`defaults` objects) are now `.strict()`; a typo'd config key - is rejected at parse. *(minor · packages/shared/src/config.ts)* -- [x] **Codify `ContentPart` / `StopReason` canonical home in the seam doc** — both are intended - to be **owned by `@relavium/shared`** and re-exported by the `@relavium/llm` seam, never imported - by shared from llm (which would invert the package dependency). `StopReason` already lives in - `@relavium/shared` (constants.ts, used by `session:turn_completed`); `ContentPart` lands when - `SessionMessageSchema` / `AgentSessionSchema` do (1.V/1.X). The seam doc - ([llm-provider-seam.md](../reference/shared-core/llm-provider-seam.md)) still shows both only as - local TS shapes with no ownership/re-export statement — annotate them there so the seam doc and - the `constants.ts` / `run-event.ts` comments stay aligned. *(nit → 1.A/1.V · llm-provider-seam.md)* -- [x] **`AgentSchema` `input_schema` / `output_schema`** — agent-yaml-spec.md lists both as optional - agent-level fields ("purely additive metadata"), and names `AgentSchema` as their validator, but - `AgentSchema` is `.strict()` and declares neither — so an authored agent using a spec-sanctioned - `output_schema` is rejected at parse. Pre-existing (agent.ts untouched by 1.L.0, which scopes - `output_schema` to the agent/transform *nodes* only); add both as `OutputSchemaSchema.optional()` - (the node.ts JSON-Schema-subset map) + a test, or amend the spec. *(low · agent.ts, - agent-yaml-spec.md)* -- [x] **Input-validation type-compatibility** — `WorkflowInput.validation` accepts any key - regardless of the input `type` (e.g. `format`/`max_length` on a `number`, `min`/`max` on a - `string`). Bound-ordering (`min ≤ max`, `min_length ≤ max_length`) is enforced; the per-type - key matrix is not, because the contract (workflow-yaml-spec.md) only shows two examples and - doesn't define which keys are legal per `InputType`. Specify that matrix, then add a - `WorkflowInputSchema.superRefine((type, validation) => …)`. *(minor · workflow.ts, - workflow-yaml-spec.md)* -- [x] **Verify the non-Anthropic prices in `pricing.ts` (at 1.G/1.H)** — the OpenAI / Gemini / - DeepSeek rows were best-known **placeholders** (Anthropic confirmed via claude-api). **Done - 2026-06-11:** verified against each provider's live pricing page, which revealed five of the six - non-Anthropic models were deprecated/shut down — retired and replaced with current models - (gpt-4o→gpt-5.5, gpt-4o-mini→gpt-5.4-mini, gemini-2.0-flash→gemini-2.5-flash, - gemini-1.5-pro→gemini-2.5-pro), DeepSeek prices corrected (deepseek-chat/-reasoner now distinct, - ctx 1M / 384K out, deprecating 2026-07-24), and **Claude Fable 5** added; Opus 4.8 / Sonnet 4.6 / - Haiku 4.5 re-confirmed unchanged. *(packages/llm/src/pricing.ts)* -- [x] **`model_catalog` cache-write column (at the seeder)** — `ModelPricing` carries - `cacheWritePerMtokMicrocents` (Anthropic charges one), but `model_catalog` - ([database-schema.md](../reference/desktop/database-schema.md)) has only - `input/output/cached_input_*_per_mtok_microcents`. When the catalog seeder lands, either add a - `cache_write_cost_per_mtok_microcents` column or knowingly drop the cache-write price from the DB - projection (`pricing.ts` stays the source of truth either way). *(nit · database-schema.md)* ## Test depth -- [x] **Coverage glob is cwd-sensitive + no enforced threshold** — `vitest.config.ts`'s - `coverage.include: ['packages/*/src/**/*.ts']` is repo-root-relative, so a package-scoped run - (`pnpm --filter @relavium/llm exec vitest --coverage`) reports a false **0%**; coverage is only - accurate from the repo root. Make the glob cwd-tolerant (or document root-only) and add the - testing.md **≥90% line+branch** threshold for the engine packages (`packages/core`, - `packages/llm`) — per-area, since surfaces are smoke-only. *(minor · vitest.config.ts)* -- [x] **Coverage floor fires only on a repo-root run + is not a CI gate** — **Done (engine-hardening - pass, advisory).** Added a repo-ROOT `pnpm coverage` CI job (ci.yml) — a root run is exactly what makes the - root-relative per-glob thresholds (`packages/core|llm/src/**`) authoritative, so the package-scoped cwd gap - (residue 1) is moot. The job is **advisory** (a separate, non-required job like `peer-dep-gate`) so it - surfaces a regression without blocking merge while the core-package **branch** margin is thin (90.29% vs the - 90% floor); promote it to a required check once that margin is confirmed stable under CI's Node 22. The - cwd-sensitivity itself stays documented at the thresholds block (a single glob cannot fix it without wrongly - binding shared/db runs). *(minor · ci.yml; vitest.config.ts)* -- [x] **Column-level schema fidelity** — `client.test.ts` proves only that table *names* exist. - Add a `PRAGMA table_info()` assertion per table (name/type/notnull/dflt/pk) against an - expected fixture, or snapshot `0000_*.sql` byte-for-byte. *(minor · packages/db/src/client.test.ts)* -- [x] **Negative FK test** — insert a `step_executions` row with a non-existent `run_id` and - assert `/FOREIGN KEY constraint failed/i`, proving `foreign_keys = ON` actually rejects. - *(minor · packages/db/src/client.test.ts)* - [ ] **dist-resolution packaging test** — the migration runner is tested only from `src/`; add a smoke test that imports built `dist/index.js` and runs `runMigrations` (the path consumers use). *(minor · packages/db/src/client.test.ts)* **Deferred 2026-06-07:** fragile as a Vitest unit test (it must import `dist/` which only exists after `turbo run build`, so it would skip or fail depending on run order). Belongs in a dedicated post-build packaging-smoke step, not the unit suite. -- [x] **In-memory `journal_mode` assertion** — if/when asserting the WAL no-op for `:memory:`, - assert its `journal_mode` is `'memory'`. *(nit · client.test.ts:50-53)* -- [x] **Edge `from`-handle grammar** — the handle is permissive (uppercase/spaces/repeated - colons). Decide + pin the grammar (`a:` empty handle rejects; decide on `a:UPPER`/`a:a:b`) and - tighten the regex if needed. *(minor · edge.ts:14-19, edge.test.ts)* -- [x] **Condition/transform invariants** — add tests: reject `default:'Not Kebab'`; accept - `when:'foo'`/`when:7`; reject empty `transform`/`expression`. *(minor · node.test.ts)* -- [x] **`record()` non-object reject** — assert `RunSchema.safeParse({ ...run, inputs: 'x' })` - rejects, pinning the record boundary. *(nit · run.test.ts, run-event.test.ts)* -- [x] **Round-trip fixture verbatim** — the workflow no-drift fixture paraphrases multi-line - prompts; transcribe them verbatim from the spec or soften the "verbatim" claim. *(nit · workflow.test.ts)* -- [x] **Conformance: tool-loop + cache-hit recorded scenarios (1.F follow-up)** — **Done - (engine-hardening pass).** Both landed as recorded scenarios across all four provider suites: (1) a - **multi-turn tool loop** — a new `replayFetchSequence` (+ a `replayFor` single-vs-sequence router; the - Gemini transport indexes per call) drives two generate() calls against one adapter, so turn 2 exercises the - adapter lowering a `tool_result` message back onto the provider's wire (the call → result → continuation - path every agent node runs); and (2) a **prompt-cache-hit** assertion — `ConformanceExpectations.textGenerate` - gained an optional `cacheReadTokens`, asserted in the textGenerate test (DeepSeek's fixture already records - `prompt_cache_hit_tokens: 4` → net input 8, cacheRead 4 folds into the one canonical `Usage`). The - provider-quirk fixture bank can still grow opportunistically as new quirks are met. *(packages/llm conformance)* ## Tooling / CI -- [x] **Turbo task `inputs`** — `lint`/`typecheck`/`test` declare no `inputs`, so turbo hashes every - file (over-invalidation). **Decided: keep the safe default (hash-all)** — scoping `inputs` risks a - stale-cache *false pass* (a changed file outside the input set served as "cached green"), which is - worse than slower cache. Recorded as deliberate; revisit if CI cache time becomes a real cost. - *(minor · turbo.json:19-30)* -- [x] **`incremental` tsconfig** — no `.tsbuildinfo` reuse; every `tsc` recompiles from scratch. - Add `incremental: true` (gitignored `tsBuildInfoFile`, listed in turbo `outputs`). *(minor · tsconfig.base.json)* -- [x] **Typecheck the config files** — root/package-root `*.config.ts` (drizzle/vitest) are - neither typechecked nor linted. Add a `tsconfig.tools.json` + `typecheck:tools` step, or - document the gap as an accepted boundary. *(minor · drizzle.config.ts, vitest.config.ts)* -- [x] **Concurrency head-ref grouping** — `main` is now protected from cancellation, but a - same-repo branch push and its open PR still run CI under separate groups. Consider - `group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}` to collapse them. - *(minor · ci.yml:19-27)* -- [x] **`engine-strict=true`** — `engines` was advisory. **Decided: enforce** — `engine-strict=true` - added to `.npmrc`, so an unsupported Node/pnpm fails install fast (clear message) instead of - surfacing as confusing errors later. *(minor · .npmrc, package.json)* - [ ] **Local `format:check` via turbo** — CI now runs `turbo run format:check`; consider routing the root `format:check` npm script through turbo too so local + CI share the cache. *(minor · package.json:21)* **Deferred 2026-06-07:** low-value cache nit that needs a task rename to avoid turbo recursion @@ -836,19 +505,6 @@ Severity is the review's verified rating. Check an item off in the PR that resol > pass. The 1.O-diff findings (the `tryParseJson` fence regex → string ops, and the `#nodeEmit` > duplicate cases → fallthrough) were fixed in PR #18; they are **not** listed here. -- [x] **`readBracket` cognitive complexity (1.L2)** — Sonar 17 > 15; extract the numeric-index vs - quoted-key branches into helpers. *(critical · packages/core/src/interpolation/path.ts:96)* **✅ Fixed:** - extracted `readQuotedKey` + `readNumericIndex`; `readBracket` is now a thin dispatcher that delegates - to them (cognitive complexity well under the threshold). -- [x] **`splitTopLevel` cognitive complexity (1.L)** — Sonar 16 > 15; extract the quote/bracket - depth-tracking into a small state helper. *(critical · packages/core/src/interpolation/references.ts:217)* - **✅ Fixed:** extracted a `SplitState` + `splitStep`/`splitStepOutsideQuote` pair; the loop body is one call. -- [x] **`String.raw` for regex-escape literals (1.L test)** — use `String.raw` instead of escaping `\` - in the interpolation reference fixtures. *(minor · packages/core/src/interpolation/references.test.ts:181-190)* - **✅ Fixed:** both escaped-quote fixtures now use `String.raw` (template + expected value); tests still green. -- [x] **Negated condition in the glob matcher (1.T)** — Sonar "unexpected negated condition"; flip the - branch for readability if it does not obscure the backtracking logic. *(minor · packages/core/src/tools/registry.ts:387)* - **✅ Fixed:** `star !== -1` → the positive valid-index check `star >= 0`. - [ ] **Duplicated SQL literal in the initial migration (0.x)** — Sonar flags a 4× literal in the generated drizzle migration. Migrations are **append-only / generated** (never hand-edited), so this is informational — only act if the literal recurs in the *schema source* a future migration regenerates. @@ -859,29 +515,3 @@ Severity is the review's verified rating. Check an item off in the PR that resol > matching test asserts surrogate pairs per UTF-16 unit) — `codePointAt` would merge pairs and break the > pinned tests. `type ToolId = string` is a deliberate **semantic domain alias** for readability, not a > redundant alias. - -## Docs - -- [x] **Node-runtime row in tech-stack.md** — `runbooks/local-dev-setup.md` defers the Node - version to tech-stack.md, which states none. Add a row (`.nvmrc` = dev/CI 22; supported floor - 20.11 per `engines`). *(minor · tech-stack.md)* -- [x] **WAL single-writer wording** — soften database-schema.md "concurrent read performance" to - make the single-writer constraint explicit so engine authors design `run_events` writes around - one writer. *(minor · database-schema.md)* -- [x] **`vitest.config.ts` include comment** — the stated rationale is inaccurate; rewrite it to - the real reason (pin to `*.test.ts` so a stray `*.spec.ts` surfaces). *(minor · vitest.config.ts:16-18)* -- [x] **`constants.ts` header overstatement** — clarify that providers/execution-modes are - consumed by `z.enum`, while event names/node types are a parallel authoritative list the unions - re-declare and tests pin. *(nit · constants.ts)* -- [x] **`RetrySchema` cross-dep note** — note at the `node.ts` import that `RetrySchema` is owned - by `agent.ts` and the dependency is one-way (agent.ts must never import node.ts). *(nit · node.ts:1-4)* -- [x] **`cumulativeCostMicrocents` comment** — append the run-scope "running total for the whole - run" note to match the spec. *(nit · run-event.ts:84)* -- [x] **Per-variant event-type export consolidation** — 3 inline + 10 in a trailing block; either - co-locate all inline or annotate the trailing block so it isn't read as exhaustive. *(nit · run-event.ts)* - -## Packaging - -- [x] **Shipped source maps reference `../src`** — published `dist/*.map` point at `src/`, which - isn't in `files`. Either add `"src"` to `files` or drop `declarationMap`/`sourceMap` from the - `*.build.json`. Bounded by `private: true` for now. *(nit · tsconfig.base.json, package.json `files`)* diff --git a/docs/roadmap/phases/phase-1-engine-and-llm.md b/docs/roadmap/phases/phase-1-engine-and-llm.md index fcaff453..d7a9fc8f 100644 --- a/docs/roadmap/phases/phase-1-engine-and-llm.md +++ b/docs/roadmap/phases/phase-1-engine-and-llm.md @@ -1,54 +1,8 @@ # Phase 1 — Engine and LLM -> Status: In progress — the critical path (Product Phase 1). Wave 0 (**1.L.0**) landed in -> **PR #6**; the Wave-1 seam trio — **1.A** (types), **1.B** (CostTracker), **1.E** (ToolNormalizer) -> — landed in **PR #7**; the first **adapter lane — 1.C** (`AnthropicAdapter`), **1.I** (`LlmError`), -> **1.F** (conformance harness), **1.D** (capabilities + `providerOptions`) — landed in **PR #8** -> (2026-06-06). The remaining adapters **1.G** (OpenAI/DeepSeek) ‖ **1.H** (Gemini), bundled with the -> **seam-shape amendment [ADR-0030](../../decisions/0030-llm-seam-shape-amendment-reasoning-response-format-provider-executed.md)** -> (reasoning channel + `responseFormat` + `providerExecuted`), and **1.J** (conformance green) landed -> in **PR #9** (2026-06-07) — **🎯 M1 (LLM seam proven) is reached.** All three adapters pass one shared -> conformance suite in fixture mode (live-nightly lane reserved/pending keys); no vendor type crosses the -> seam. **1.K (FallbackChain) is ✅ Done (PR #13, 2026-06-11)** — the seam's last policy layer, with the -> ADR-0030 strip-on-failover obligation honoured; **1.m2 (policy layers) is complete** (with the -> CostTracker, 1.B). **1.L ✅ Done (PR #14, 2026-06-12)** — `@relavium/core` is scaffolded with the -> `WorkflowYAMLParser` — and **1.L2 ✅ Done (PR #15, 2026-06-12)** — the `{{ … }}` interpolation engine -> (runtime resolver + pipe-filter registry) plus the parse-time transitive secret-taint gate -> (ADR-0029(c)). **1.M (DAG builder + `RunPlan`) and 1.AB (the QuickJS-wasm expression sandbox) are -> ✅ Done (PR #16, 2026-06-13)** — the plan layer and the deterministic `condition`/`transform`/`merge_fn` -> evaluator. **1.N (`WorkflowEngine` + `RunEventBus`) and 1.T (built-in `ToolRegistry`) are ✅ Done -> (PR #17, 2026-06-13)**, completing **1.m3** (parse → DAG → run loop emits the canonical event stream). -> With 1.K, 1.N, and 1.T all landed, the **1.O `AgentRunner` join is ✅ Done (PR #18, 2026-06-14)**, and -> the **node-type handlers (1.P) are ✅ Done (PR #20, 2026-06-14)** — the six non-agent `NodeExecutor` arms -> behind a dispatching executor, executor-only (no `engine.ts` change), with a pre-merge BLOCKER secret-leak -> fixed by a `secretInputNames` masking gate on `NodeExecContext`. **Checkpoint/resume (1.R) and the human -> gate (1.Q) are ✅ Done (PR #22, 2026-06-15)** — the derived `Checkpointer` + cross-process -> `resumeFromCheckpoint` (idempotent re-delivery, `workflow_mismatch` identity guard) and the -> `human_in_the_loop` gate (suspend/resume + the one-shot `setTimer` timeout port: `approve` auto-resolves, -> `reject` fails with `run_timeout`). **Node retry (1.S) is ✅ Done (PR #24, 2026-06-15)** — the above-chain -> whole-node retry budget ([ADR-0040](../../decisions/0040-node-retry-budget-above-the-chain.md), Part A; the -> user-triggered retry-from-node Part B is deferred to Phase-2). **The pre-egress budget governor (1.AC) and -> the `AgentSession` agent-first entry point (1.V) then landed together — ✅ Done (PR #26, 2026-06-16).** 1.AC -> was the last **1.m4** workstream, so **1.m4 is complete** (the full engine stack — node handlers, gate, -> checkpoint/resume, retry, tools, sandbox, budget governor); 1.V opens **Lane C** (the agent-first sub-spine, -> 1.m5). The end-to-end Node harness (**1.U**) then landed — **🎯 M2 reached (PR #27, 2026-06-16): the engine -> runs end-to-end** (live streaming + per-node-boundary checkpointing + cross-process resume + node retry + -> provider failover, per-attempt cost, gap-free `sequenceNumber`), **completing the Phase-1 critical path**. The -> remaining Phase-1 work is **additive and off the critical path**: Lane C (the 1.m5 agent-first sub-spine — -> 1.W/1.X/1.Y/1.Z all ✅ Done, plus the 1.AA chat-regression harness ✅) is **complete**, leaving only the 1.m6 -> multimodal sub-spine (1.AE–1.AH). **Phase 2 -> (CLI, milestone M3) is unblocked.** *(Session persistence, 1.X/1.Z, must exclude the reasoning signature — -> non-persisting.)* +> Status: ✅ **Complete** — all workstreams (1.A–1.AH) landed and merged (PR #6–#38, 2026-06-21), achieving global milestones **M1 (LLM seam proven, 2026-06-07, PR #9)** and **M2 (engine end-to-end, 2026-06-16, PR #27)**. The critical path is done: `@relavium/llm` (seam + 3 adapters + `FallbackChain` + cost tracking) and `@relavium/core` (YAML→DAG parser + run loop + checkpoint/resume + retry) are proven end-to-end by the Node harness (1.U). The additive sub-spines are also complete: Lane C (agent-first, 1.V–1.AA, milestone 1.m5) and the multimodal sub-spine (1.AD–1.AH, milestone 1.m6). [Phase 2 — CLI](phase-2-cli.md) is next (milestone M3). Deferred work — including 1.AH's host-wiring half (distributed across Phases 2–6) — is tracked in [../deferred-tasks.md](../deferred-tasks.md). For per-PR history see [../current.md](../current.md). > -> **Multimodal I/O decided (2026-06-08).** First-class image/audio/video I/O (input **and** output) is a -> second pre-freeze seam amendment in the ADR-0030 mould — [ADR-0031](../../decisions/0031-llm-seam-shape-amendment-multimodal-io.md) -> (seam) + [ADR-0032](../../decisions/0032-desktop-rust-media-de-inline-amends-0018.md) (desktop Rust -> de-inline), designed in [multimodal-io-design-2026-06-07.md](../../analysis/multimodal-io-design-2026-06-07.md). -> The new sub-spine **1.AD–1.AH** (1.m6) lands the **shape (1.AD) before the exhaustive consumers 1.K/1.O** -> so the `ContentPart`/`StreamChunk` media union members are non-breaking; the **behavior (1.AE–1.AH) is -> additive and does NOT gate M2** (it threads into the engine and Phases 2–6, like the agent-first sub-spine). -> **1.AD is ✅ Done (PR #11, 2026-06-10)** — the shape landed with all-false adapter matrices and the -> fail-fast media guard; it unblocked 1.K, now also ✅ Done (PR #13). +> **Multimodal I/O** ([ADR-0031](../../decisions/0031-llm-seam-shape-amendment-multimodal-io.md) seam + [ADR-0032](../../decisions/0032-desktop-rust-media-de-inline-amends-0018.md) desktop de-inline, designed in [multimodal-io-design-2026-06-07.md](../../analysis/multimodal-io-design-2026-06-07.md)) — the 1.m6 sub-spine **1.AD–1.AH** is complete (PR #11–#38, 2026-06-10–2026-06-21). 1.AD landed the seam shape **before the exhaustive consumers 1.K/1.O** so the `ContentPart`/`StreamChunk` media union members are non-breaking; 1.AE–1.AH behavior is done, with 1.AH's host-wiring half deferred to Phases 2–6 ([../deferred-tasks.md](../deferred-tasks.md)). - **Related**: [../README.md](../README.md), [phase-0-foundations.md](phase-0-foundations.md), [phase-2-cli.md](phase-2-cli.md), [../../architecture/shared-core-engine.md](../../architecture/shared-core-engine.md), [../../architecture/execution-model.md](../../architecture/execution-model.md), [../../architecture/multi-llm-providers.md](../../architecture/multi-llm-providers.md), [../../reference/shared-core/llm-provider-seam.md](../../reference/shared-core/llm-provider-seam.md), [../../reference/shared-core/node-types.md](../../reference/shared-core/node-types.md), [../../reference/shared-core/built-in-tools.md](../../reference/shared-core/built-in-tools.md), [../../reference/contracts/sse-event-schema.md](../../reference/contracts/sse-event-schema.md), [../../standards/testing.md](../../standards/testing.md), [../../standards/error-handling.md](../../standards/error-handling.md), [../../decisions/0011-internal-llm-abstraction.md](../../decisions/0011-internal-llm-abstraction.md) From 63d31f9eebf8f6b0794ae8c7b9fb0815015cc042 Mon Sep 17 00:00:00 2001 From: Cemil ILIK Date: Mon, 22 Jun 2026 00:23:32 +0300 Subject: [PATCH 6/6] =?UTF-8?q?docs(standards):=20make=20the=20=C2=A79a=20?= =?UTF-8?q?dependency=20cooling=20window=20concrete=20(review)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address PR #39 review: the "few days" threshold gave reviewers no consistent bar. Pin it to a concrete default — minor/patch ≥ 7 calendar days old, major ≥ 14 days, anything < 72 h blocked unless the PR justifies it — framed as a review bar (reviewer judgement can pass a near-miss), with the security/CVE-fix exception explicit. The code-review checklist line now defers to the §9a bar rather than restating a divergent "few days" (one-canonical-home). Co-Authored-By: Claude Opus 4.8 --- docs/standards/architectural-principles.md | 21 +++++++++++++-------- docs/standards/code-review.md | 7 ++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/standards/architectural-principles.md b/docs/standards/architectural-principles.md index 7242e11a..affd96e8 100644 --- a/docs/standards/architectural-principles.md +++ b/docs/standards/architectural-principles.md @@ -141,16 +141,21 @@ foundations. See [security-review.md](security-review.md) and A new dependency version is not trusted the day it ships. Supply-chain attacks land through freshly published versions of otherwise-reputable packages, and a brand-new release has had no time to be audited by anyone. So when adding a dependency or bumping -one, **prefer a version that has been public for a while over the just-released latest**: -do not pin to a version published within the last few days, and treat any same-day -upgrade as something to justify, not a default. This is a review posture, not a license -to fall behind — security patches are the exception and are pulled in promptly; the rule -targets *unvetted novelty*, not staying current. +one, **prefer a version that has been public for a while over the just-released latest**. +**Concrete bar:** a minor or patch bump should be at least **7 calendar days** old before +it enters the lockfile; a **major** bump (larger change surface, more time for the +ecosystem to surface regressions) at least **14 days**; anything **under 72 hours** old is +bleeding-edge and is blocked unless the PR justifies it. Treat these as the default review +bar, not rigid math — a version a day or two under the line can still pass on reviewer +judgement with a stated reason. This is a review posture, not a license to fall behind: a +security or CVE fix is the explicit exception — it may skip the window entirely, with the +reason recorded in the PR. The rule targets *unvetted novelty*, not staying current. **Applied rule:** when a `package.json` diff adds or raises a dependency, the reviewer -checks the chosen version's publish age and questions a bleeding-edge pin with no -security justification; the safe default is the most recent version that has been -available long enough to have been exercised by the ecosystem. This matters most for the +checks the chosen version's publish age against the bar above — blocking an under-72h pin +and flagging a still-uncooled one (< 7 days, or < 14 for a major) absent a recorded +security justification; the safe default is the newest version that has cleared the +cooling window and been exercised by the ecosystem. This matters most for the dependency-heavy surfaces (the CLI's `commander`/`ink`/`@clack/prompts`/`tsup`, the keychain and MCP bindings) where the new-dependency count is highest. Adding a dependency at all still needs the principle-9 bar (an [ADR](../decisions/README.md) for a new runtime diff --git a/docs/standards/code-review.md b/docs/standards/code-review.md index 2290a729..7c27e9c3 100644 --- a/docs/standards/code-review.md +++ b/docs/standards/code-review.md @@ -51,9 +51,10 @@ are actually enforced — automation catches format and types; a human catches t re-introduces a banned one (Vercel AI SDK, LangChain, a Python sidecar). Dev-only tools are lower bar but still justified. - **Dependency-bump cooling window:** for an added or raised dependency, check the chosen - version's publish age and question a bleeding-edge pin that has been public only a few - days with no security justification — prefer a version exercised by the ecosystem. See - [architectural-principles.md §9a](architectural-principles.md#9a-dependency-bump-cooling-window). + version's publish age against the + [§9a cooling bar](architectural-principles.md#9a-dependency-bump-cooling-window) + (≥ 7 days, ≥ 14 for a major, < 72 h blocked) and require a recorded security + justification to override it. - The **LLM seam holds**: no vendor SDK type imported outside `packages/llm/src/adapters/*` (see [code-style-typescript.md](code-style-typescript.md#module-boundaries--no-vendor-type-across-the-llm-seam)). This is a blocking check, not a nit.