From 20ad76d7968479d86c7f8e07a3bd952d704ef729 Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Sat, 30 May 2026 01:00:18 -0700 Subject: [PATCH] =?UTF-8?q?feat(workflows):=20agent=20workflows=20?= =?UTF-8?q?=E2=80=94=20phase-keyed=20task=20lifecycle=20playbooks=20(#3008?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memory/MEMORY.md | 4 + .../cargo-check-vs-test-verification.md | 12 + .../memory/subagent-output-can-be-lost.md | 14 + app/src/AppRoutes.tsx | 10 + .../workflows/CreateWorkflowModal.tsx | 236 +++++++++ app/src/components/workflows/PhaseEditor.tsx | 140 ++++++ app/src/components/workflows/WorkflowCard.tsx | 128 +++++ .../workflows/WorkflowDetailDrawer.tsx | 249 +++++++++ app/src/lib/i18n/ar.ts | 47 ++ app/src/lib/i18n/bn.ts | 47 ++ app/src/lib/i18n/de.ts | 47 ++ app/src/lib/i18n/en.ts | 49 ++ app/src/lib/i18n/es.ts | 47 ++ app/src/lib/i18n/fr.ts | 47 ++ app/src/lib/i18n/hi.ts | 47 ++ app/src/lib/i18n/id.ts | 47 ++ app/src/lib/i18n/it.ts | 47 ++ app/src/lib/i18n/ko.ts | 47 ++ app/src/lib/i18n/pl.ts | 47 ++ app/src/lib/i18n/pt.ts | 47 ++ app/src/lib/i18n/ru.ts | 47 ++ app/src/lib/i18n/zh-CN.ts | 47 ++ app/src/pages/AgentWorkflows.test.tsx | 204 ++++++++ app/src/pages/AgentWorkflows.tsx | 278 ++++++++++ app/src/pages/Intelligence.tsx | 10 +- app/src/services/api/workflowsApi.test.ts | 153 ++++++ app/src/services/api/workflowsApi.ts | 194 +++++++ app/src/store/index.ts | 2 + app/src/store/workflowsSlice.test.ts | 213 ++++++++ app/src/store/workflowsSlice.ts | 159 ++++++ src/core/all.rs | 4 + src/openhuman/about_app/catalog_data.rs | 12 + src/openhuman/agent/debug/mod.rs | 1 + .../agent/harness/harness_gap_tests.rs | 1 + .../agent/harness/session/builder.rs | 25 + src/openhuman/agent/harness/session/turn.rs | 1 + src/openhuman/agent/harness/session/types.rs | 5 + .../agent/harness/subagent_runner/ops.rs | 1 + .../agent/harness/test_support_tests.rs | 1 + src/openhuman/agent/profiles.rs | 1 + src/openhuman/agent/prompts/mod.rs | 1 + src/openhuman/agent/prompts/mod_tests.rs | 15 + src/openhuman/agent/prompts/types.rs | 5 + src/openhuman/agent/tools.rs | 2 + src/openhuman/agent/tools/workflow_tools.rs | 476 ++++++++++++++++++ .../agent/tools/workflow_tools_tests.rs | 153 ++++++ src/openhuman/agent/triage/evaluator.rs | 1 + .../agent_registry/agents/archivist/prompt.rs | 1 + .../agents/code_executor/prompt.rs | 1 + .../agent_registry/agents/critic/prompt.rs | 1 + .../agents/crypto_agent/prompt.rs | 1 + .../agent_registry/agents/help/prompt.rs | 1 + .../agents/integrations_agent/agent.toml | 11 +- .../agents/integrations_agent/prompt.rs | 11 + src/openhuman/agent_registry/agents/loader.rs | 1 + .../agents/markets_agent/prompt.rs | 1 + .../agent_registry/agents/mcp_setup/prompt.rs | 1 + .../agents/morning_briefing/prompt.rs | 1 + .../agents/orchestrator/agent.toml | 6 + .../agents/orchestrator/prompt.rs | 1 + .../agent_registry/agents/planner/prompt.rs | 1 + .../agents/researcher/prompt.rs | 1 + .../agents/skill_creator/prompt.rs | 1 + .../agents/summarizer/prompt.rs | 1 + .../agents/tool_maker/prompt.rs | 1 + .../agents/tools_agent/prompt.rs | 1 + .../agents/trigger_reactor/prompt.rs | 1 + .../agents/trigger_triage/prompt.rs | 1 + src/openhuman/agent_workflows/discover.rs | 147 ++++++ .../agent_workflows/discover_tests.rs | 126 +++++ src/openhuman/agent_workflows/inject.rs | 42 ++ src/openhuman/agent_workflows/inject_tests.rs | 47 ++ src/openhuman/agent_workflows/mod.rs | 35 ++ src/openhuman/agent_workflows/ops.rs | 147 ++++++ src/openhuman/agent_workflows/ops_tests.rs | 72 +++ src/openhuman/agent_workflows/parse.rs | 69 +++ src/openhuman/agent_workflows/parse_tests.rs | 98 ++++ src/openhuman/agent_workflows/schemas.rs | 280 +++++++++++ .../agent_workflows/schemas_tests.rs | 63 +++ src/openhuman/agent_workflows/select.rs | 94 ++++ src/openhuman/agent_workflows/select_tests.rs | 119 +++++ src/openhuman/agent_workflows/types.rs | 241 +++++++++ src/openhuman/agent_workflows/workdir.rs | 84 ++++ .../agent_workflows/workdir_tests.rs | 56 +++ src/openhuman/learning/prompt_sections.rs | 1 + src/openhuman/memory_tools/prompt.rs | 1 + src/openhuman/mod.rs | 1 + src/openhuman/todos/ops.rs | 25 + src/openhuman/tools/impl/system/shell.rs | 7 +- src/openhuman/tools/ops.rs | 11 + tests/json_rpc_e2e.rs | 128 +++++ tests/personality_e2e.rs | 1 + 92 files changed, 5309 insertions(+), 3 deletions(-) create mode 100644 .claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/MEMORY.md create mode 100644 .claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/cargo-check-vs-test-verification.md create mode 100644 .claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/subagent-output-can-be-lost.md create mode 100644 app/src/components/workflows/CreateWorkflowModal.tsx create mode 100644 app/src/components/workflows/PhaseEditor.tsx create mode 100644 app/src/components/workflows/WorkflowCard.tsx create mode 100644 app/src/components/workflows/WorkflowDetailDrawer.tsx create mode 100644 app/src/pages/AgentWorkflows.test.tsx create mode 100644 app/src/pages/AgentWorkflows.tsx create mode 100644 app/src/services/api/workflowsApi.test.ts create mode 100644 app/src/services/api/workflowsApi.ts create mode 100644 app/src/store/workflowsSlice.test.ts create mode 100644 app/src/store/workflowsSlice.ts create mode 100644 src/openhuman/agent/tools/workflow_tools.rs create mode 100644 src/openhuman/agent/tools/workflow_tools_tests.rs create mode 100644 src/openhuman/agent_workflows/discover.rs create mode 100644 src/openhuman/agent_workflows/discover_tests.rs create mode 100644 src/openhuman/agent_workflows/inject.rs create mode 100644 src/openhuman/agent_workflows/inject_tests.rs create mode 100644 src/openhuman/agent_workflows/mod.rs create mode 100644 src/openhuman/agent_workflows/ops.rs create mode 100644 src/openhuman/agent_workflows/ops_tests.rs create mode 100644 src/openhuman/agent_workflows/parse.rs create mode 100644 src/openhuman/agent_workflows/parse_tests.rs create mode 100644 src/openhuman/agent_workflows/schemas.rs create mode 100644 src/openhuman/agent_workflows/schemas_tests.rs create mode 100644 src/openhuman/agent_workflows/select.rs create mode 100644 src/openhuman/agent_workflows/select_tests.rs create mode 100644 src/openhuman/agent_workflows/types.rs create mode 100644 src/openhuman/agent_workflows/workdir.rs create mode 100644 src/openhuman/agent_workflows/workdir_tests.rs diff --git a/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/MEMORY.md b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/MEMORY.md new file mode 100644 index 000000000..ac8b075f4 --- /dev/null +++ b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/MEMORY.md @@ -0,0 +1,4 @@ +# Project memory index + +- [cargo check vs test verification](cargo-check-vs-test-verification.md) — `cargo check` skips test modules; use `cargo test --no-run` to verify tests compile. +- [Subagent output can be lost](subagent-output-can-be-lost.md) — commit verified subagent work promptly; scope parallel agents to non-overlapping paths. diff --git a/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/cargo-check-vs-test-verification.md b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/cargo-check-vs-test-verification.md new file mode 100644 index 000000000..74b471a61 --- /dev/null +++ b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/cargo-check-vs-test-verification.md @@ -0,0 +1,12 @@ +--- +name: cargo-check-vs-test-verification +description: cargo check passing does NOT mean tests compile; always cargo test --no-run to verify test modules +metadata: + type: feedback +--- + +`cargo check --manifest-path Cargo.toml` compiles the **lib only** — it does NOT compile `#[cfg(test)]` modules or sibling `*_tests.rs` files. A domain can pass `cargo check` while its own test modules have compile errors (missing imports in `use super::*` test files, private-fn re-export E0364, missing struct fields in test-only `PromptContext` construction sites, etc.). + +**Why:** In this repo, `cargo check` greenlit a new domain whose `select_tests.rs` had unresolved `WorkflowPhase`/`PHASE_*` imports and whose `ops.rs` had a `pub(crate) use slugify` (E0364) — all invisible until `cargo test` compiled the test cfg. Also: adding a field to a widely-constructed struct (e.g. `PromptContext`) requires updating **every** construction site including those in `tests/*.rs` integration tests (e.g. `tests/personality_e2e.rs`), which `cargo check` won't surface. + +**How to apply:** To verify Rust work actually compiles AND tests are valid, run `cargo test --manifest-path Cargo.toml --no-run` (compiles all test targets) and then run the actual tests. Never report "N tests pass" based on a `cargo check` exit code or a `cargo test ` run that shows "0 passed; N filtered out" — that means the filter matched nothing, not success. Confirm the result line shows a non-zero passed count. See [[subagent-output-can-be-lost]]. diff --git a/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/subagent-output-can-be-lost.md b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/subagent-output-can-be-lost.md new file mode 100644 index 000000000..d5c2884dc --- /dev/null +++ b/.claude/projects/-Users-enamakel-work-tinyhumansai-openhuman-2/memory/subagent-output-can-be-lost.md @@ -0,0 +1,14 @@ +--- +name: subagent-output-can-be-lost +description: Long-running subagents can die/roll back losing uncommitted work; commit verified subagent output promptly +metadata: + type: feedback +--- + +During the agent_workflows feature build, a `codecrusher` subagent ran ~20 min, hit an API socket error, and its working-tree output was largely rolled back (only 2 of ~10 files survived) — and a separate harness subagent got stuck in a loop re-running 5-minute background `cargo test` commands without converging, requiring a manual TaskStop + `pkill -f "cargo test"`. + +**How to apply:** +- When a subagent completes a self-contained, independently-verified slice (e.g. frontend in `app/` only), **commit it promptly** as a checkpoint rather than letting it sit uncommitted while other agents run — uncommitted work is the only thing at risk of a rollback. +- Give parallel subagents **non-overlapping path scopes** (one in `src/`, one in `app/`) and tell them NOT to commit, so the main thread reconciles and commits. +- If a subagent goes quiet, check liveness via file mtimes + `TaskOutput(block:false)`; if it's looping on long background commands, `TaskStop` it and `pkill` stray `cargo` processes, then finish the work directly. +- Always independently re-verify a subagent's claimed results — see [[cargo-check-vs-test-verification]] (a subagent reported "tests pass" when the test cfg never compiled). diff --git a/app/src/AppRoutes.tsx b/app/src/AppRoutes.tsx index 62c6cb3eb..2ebc31654 100644 --- a/app/src/AppRoutes.tsx +++ b/app/src/AppRoutes.tsx @@ -7,6 +7,7 @@ import PublicRoute from './components/PublicRoute'; import HumanPage from './features/human/HumanPage'; import { getIsMobile } from './lib/platform'; import Accounts from './pages/Accounts'; +import AgentWorkflows from './pages/AgentWorkflows'; import Channels from './pages/Channels'; import Home from './pages/Home'; import Intelligence from './pages/Intelligence'; @@ -174,6 +175,15 @@ const AppRoutes = () => { } /> + + + + } + /> + } /> void; + onCreated: (workflow: Workflow) => void; +} + +export default function CreateWorkflowModal({ onClose, onCreated }: Props) { + const { t } = useT(); + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [whenToUse, setWhenToUse] = useState(''); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + const nameInputRef = useRef(null); + const previousFocusRef = useRef(null); + + const isValid = name.trim().length > 0; + + useEffect(() => { + previousFocusRef.current = document.activeElement as HTMLElement | null; + const raf = window.requestAnimationFrame(() => { + nameInputRef.current?.focus(); + }); + log('mount'); + return () => { + window.cancelAnimationFrame(raf); + previousFocusRef.current?.focus?.(); + log('unmount'); + }; + }, []); + + useEffect(() => { + const handler = (e: KeyboardEvent) => { + if (e.key === 'Escape' && !submitting) { + log('escape-key close'); + onClose(); + } + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, [onClose, submitting]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!isValid || submitting) return; + setSubmitting(true); + setError(null); + log('submit name=%s', name.trim()); + try { + const workflow = await workflowsApi.createWorkflow({ + name: name.trim(), + description: description.trim() || undefined, + when_to_use: whenToUse.trim() || undefined, + }); + log('created name=%s', workflow.name); + onCreated(workflow); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + log('create error %s', msg); + setError(msg); + setSubmitting(false); + } + }; + + return createPortal( +
{ + if (e.target === e.currentTarget && !submitting) { + log('backdrop-click close'); + onClose(); + } + }}> +