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(); + } + }}> +