docs: add Ideation Studio user guide - #2
Conversation
Covers the full end-to-end journey from ideation session to merged code: - Session modes (Solo / Research Team / Debate Team) with cost guidance - The orchestrator workflow phases (RECOVER → UNDERSTAND → EXPLORE → PLAN → CONFIRM → PROPOSE → FINALIZE) - Team Activity panel and user-to-teammate messaging - Plan artifact review including Debate mode side-by-side layout - CONFIRM gate and proposal editing - Accepting the plan, task creation, and Active Plan - Downstream journey: execution → review → merge pipeline - End-to-end flow diagram with plan branch hierarchy - Troubleshooting and configuration reference Co-authored-by: lazabogdan <6580668+lazabogdan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive user documentation for the Ideation Studio, which serves as the entry point for feature development in RalphX. The guide complements the existing merge pipeline documentation by documenting the ideation-to-task workflow.
Changes:
- Added complete Ideation Studio user guide covering session modes (Solo/Research Team/Debate Team), orchestrator workflow phases, team collaboration features, plan artifacts, and downstream integration
- Documented the full journey from idea to merged code, including Active Plan management, task creation, and integration with the execution and merge pipelines
- Included comprehensive troubleshooting section and configuration reference with YAML examples
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | Mode | Who runs the research | Best for | Est. token cost | | ||
| |------|-----------------------|----------|-----------------| | ||
| | **Solo** | 1 orchestrator + up to 3 parallel Explore subagents | Simple features, bug fix ideation, quick tasks | ~100K (~$0.80) | | ||
| | **Research Team** ★ | 1 team lead (Opus) + up to 5 dynamic specialist teammates | Complex features touching 2+ layers (frontend + backend + DB) | ~350–400K (~$2.50–3.50) | | ||
| | **Debate Team** ★ | 1 team lead (Opus) + up to 5 dynamic advocate teammates (including a devil's advocate) | Architecture decisions, new subsystems, high-stakes design | ~500–600K (~$4.00–5.00) | | ||
|
|
There was a problem hiding this comment.
The token cost estimates and pricing calculations should include a reference or note explaining how these costs are calculated or which pricing model is being used. Without context about the underlying model pricing (e.g., Claude Opus/Sonnet/Haiku rates per token), users cannot verify these estimates or understand how they might change with different model selections.
| | Mode | Who runs the research | Best for | Est. token cost | | |
| |------|-----------------------|----------|-----------------| | |
| | **Solo** | 1 orchestrator + up to 3 parallel Explore subagents | Simple features, bug fix ideation, quick tasks | ~100K (~$0.80) | | |
| | **Research Team** ★ | 1 team lead (Opus) + up to 5 dynamic specialist teammates | Complex features touching 2+ layers (frontend + backend + DB) | ~350–400K (~$2.50–3.50) | | |
| | **Debate Team** ★ | 1 team lead (Opus) + up to 5 dynamic advocate teammates (including a devil's advocate) | Architecture decisions, new subsystems, high-stakes design | ~500–600K (~$4.00–5.00) | | |
| | Mode | Who runs the research | Best for | Est. token cost* | | |
| |------|-----------------------|----------|------------------| | |
| | **Solo** | 1 orchestrator + up to 3 parallel Explore subagents | Simple features, bug fix ideation, quick tasks | ~100K (~$0.80) | | |
| | **Research Team** ★ | 1 team lead (Opus) + up to 5 dynamic specialist teammates | Complex features touching 2+ layers (frontend + backend + DB) | ~350–400K (~$2.50–3.50) | | |
| | **Debate Team** ★ | 1 team lead (Opus) + up to 5 dynamic advocate teammates (including a devil's advocate) | Architecture decisions, new subsystems, high-stakes design | ~500–600K (~$4.00–5.00) | | |
| \* Token and USD estimates are based on typical Claude Opus/Sonnet pricing per 1K tokens as of early 2025 and assume a mix of models as configured in RalphX; actual costs will vary with your model choices, prompt size, and current provider pricing. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…etion gate The task-execution success/AgentExit finalizers in `handle_stream_success` / `handle_stream_error` gated the `executing → pending_review` transition on `all_steps_completed()` (every task_step Completed/Skipped) while passing `task_step_repo.is_some()` (always true in prod) as "steps tracked". Two false-negative completions resulted, each driving the deterministic 3x auto-retry loop into `max_retries_exceeded` and parking the task in `failed`: 1. Zero-step runs: a worker that validates and calls `execution_complete` without registering steps was marked "Agent ended without completing all task steps" (the `!steps.is_empty()` guard can never pass with 0 steps). 2. Lingering terminal `failed` step: a single immutable `failed` step (step tools return 400 for any move out of `failed`) vetoed completion forever, even when `execution_complete` had captured a green, HEAD-matched validation cache proving the work was actually done. Fix (completion gate only): - `has_tracked_steps()` reports whether ≥1 step exists; the gate now falls back to `has_output` for genuinely zero-step runs instead of failing them. - `validation_cache_proves_completion()` (pure, unit-tested) + the async `validated_completion_override()` route a would-be `Failed` transition to `PendingReview` when the task's `validation_cache` matches the current worktree HEAD AND `tests_ran && tests_passed`. A `tests_ran=false` cache (e.g. a self-blocked no-op) is deliberately NOT rescued. - Both gate sites (success finalizer + AgentExit override) share the logic. Tests: pure-function coverage for every branch of the new predicates plus end-to-end finalizer tests over a real git worktree (green cache rescues a failed-step gate; tests_ran=false cache does not; AgentExit honors the cache). Root-cause analysis for all failing dev-DB tasks is in docs/handoffs/failing-tasks-2026-06-26/. This commit addresses issues #2 and #3 (the completion-gate defect); the worktree-provisioning (#1) and dependency-deadlock (#4) issues are documented there as follow-ups.
…etion gate (#494) * fix: honor green validation cache + zero-step runs in execution completion gate The task-execution success/AgentExit finalizers in `handle_stream_success` / `handle_stream_error` gated the `executing → pending_review` transition on `all_steps_completed()` (every task_step Completed/Skipped) while passing `task_step_repo.is_some()` (always true in prod) as "steps tracked". Two false-negative completions resulted, each driving the deterministic 3x auto-retry loop into `max_retries_exceeded` and parking the task in `failed`: 1. Zero-step runs: a worker that validates and calls `execution_complete` without registering steps was marked "Agent ended without completing all task steps" (the `!steps.is_empty()` guard can never pass with 0 steps). 2. Lingering terminal `failed` step: a single immutable `failed` step (step tools return 400 for any move out of `failed`) vetoed completion forever, even when `execution_complete` had captured a green, HEAD-matched validation cache proving the work was actually done. Fix (completion gate only): - `has_tracked_steps()` reports whether ≥1 step exists; the gate now falls back to `has_output` for genuinely zero-step runs instead of failing them. - `validation_cache_proves_completion()` (pure, unit-tested) + the async `validated_completion_override()` route a would-be `Failed` transition to `PendingReview` when the task's `validation_cache` matches the current worktree HEAD AND `tests_ran && tests_passed`. A `tests_ran=false` cache (e.g. a self-blocked no-op) is deliberately NOT rescued. - Both gate sites (success finalizer + AgentExit override) share the logic. Tests: pure-function coverage for every branch of the new predicates plus end-to-end finalizer tests over a real git worktree (green cache rescues a failed-step gate; tests_ran=false cache does not; AgentExit honors the cache). Root-cause analysis for all failing dev-DB tasks is in docs/handoffs/failing-tasks-2026-06-26/. This commit addresses issues #2 and #3 (the completion-gate defect); the worktree-provisioning (#1) and dependency-deadlock (#4) issues are documented there as follow-ups. * fix: harden execution completion and setup recovery * test: make zero-step completion assertion robust to review-pipeline downstream The zero-step-with-output finalizer test asserted the task lands exactly in PendingReview|Reviewing, but in AppState::new_test() the auto-transition chain (PendingReview → Reviewing → …) escalates because no real reviewer is wired, yielding Escalated. The fix's contract is only that the run escapes the stuck-Failed loop, so assert it is neither Failed nor Executing instead of pinning an exact downstream review state. * test: cover execution setup failure branches * fix: harden execution completion gate * chore: remove execution gate investigation docs
- global-persona Refine gated on standalone_conversations at both Settings and the Persona artifact tab (disabled + tooltip when off) — closes the flag-interaction hazard that would strand a rejected projectless draft after cutover (audit #2) - Settings PersonaEditor gains the UX-1 Version-history affordance, sharing the Persona tab's attributed history components (audit #3) - persona_service_tests and persona_update_approval_tests mechanically split by concern under the 500-line rule; test counts preserved (195 == 195) (audit #5)
- global-persona Refine gated on standalone_conversations at both Settings and the Persona artifact tab (disabled + tooltip when off) — closes the flag-interaction hazard that would strand a rejected projectless draft after cutover (audit #2) - Settings PersonaEditor gains the UX-1 Version-history affordance, sharing the Persona tab's attributed history components (audit #3) - persona_service_tests and persona_update_approval_tests mechanically split by concern under the 500-line rule; test counts preserved (195 == 195) (audit #5)
- global-persona Refine gated on standalone_conversations at both Settings and the Persona artifact tab (disabled + tooltip when off) — closes the flag-interaction hazard that would strand a rejected projectless draft after cutover (audit #2) - Settings PersonaEditor gains the UX-1 Version-history affordance, sharing the Persona tab's attributed history components (audit #3) - persona_service_tests and persona_update_approval_tests mechanically split by concern under the 500-line rule; test counts preserved (195 == 195) (audit #5)
…ontext, standalone conversations (#779) * docs: add personas v2 builder scoping handoff spec * feat: enforced-mode filesystem containment in ralphx-mcp-server (personas v2 P0B) - argv-only --filesystem-enforced flag (no env fallback, no env write) - enforced mode: configured read roots only, no implicit CWD, empty roots deny all - realpath containment incl. symlink-inside-root escapes and ENOENT parent checks - read-tools-only isTrustedReadRootPath in permission bridge; Bash branch untouched - rebuilt tracked build/ output * feat: filesystem-enforcement flag plumbing + conversation-id threading (personas v2 P0A) - McpRuntimeContext.enforce_filesystem_roots, derived in one seam (build_mcp_runtime_context receives effective mode; true iff persona_builder) - --filesystem-enforced 1 rides MCP server CLI args only; never process env - real conversation_id threaded through fresh/resume/queued/recovery runtime contexts in Claude and Codex lanes; Codex no longer substitutes the project context_id for the conversation id - launch plan rejects missing/mismatched conversation ids (typed error) - spawn-shape parity + enforcement emission tests across both harnesses Note: suite_chat_service carries 5 pre-existing failures inherited from main (plan-verification revamp fallout, red on main CI since #752); this commit adds zero new failures. * feat: save_persona_draft 3-state fail-closed + extractor prompt touch-up (personas v2 P0C) - caller-session header required: missing header / unknown conversation / non-builder conversation each rejected with typed, actionable errors - client draft_id no longer an authority source (comparison-only against the conversation's bound draft); unrestricted fall-through removed - bound-draft create/update flow preserved; cross-conversation writes rejected - extractor prompts (both harnesses) lead with interview + draft tools and treat fs denials as absent context instead of an error state * feat: persona project scoping backend (personas v2 P1A) - M1: personas.project_id (NULL = global) + scoped active-slug unique index (slug, IFNULL(project_id,'')) WHERE active; map_live_slug_unique_error updated to the new index name - PersonaScopeFilter (All | GlobalOnly | GlobalAndProject) through repo, service, and list_personas command; drafts stamp project_id (empty/blank ids rejected at the boundary) - shared bindability predicate at bind/start/send; scope mismatch suppresses with project_scope_mismatch attribution BEFORE rendering; repo errors stay typed and abort the send - Explicit directive: suppressed by Automation/PersonaBuilder/verification, exempt from the start-path agent-name override (adversarial-review fix) - approval-module raw-SQL slug checks scope-aware in both directions - transactional delete_project persona sweep (drafts deleted, actives archived, persona_id/builder_draft_id bindings cleared) * feat: persona project scoping frontend (personas v2 P1B) - Persona schema gains projectId (snake_case raw -> camelCase transform) - Settings: scope filter (All | Global | per-project), scope badges with deleted-project fallback, create-form scope select, read-only editor scope - usePersonas(scope) with typed tagged-union DTO matching the backend enum - PersonaPickerControl fetches globalAndProject(current) and renders grouped Global / project sections - PersonaManagementRows extracted to keep the section under the size limit * feat: manual persona draft editing backend, CAS (personas v2 P2A) - update_draft gains optional expected_content_hash CAS, atomic at the SQL layer (UPDATE ... WHERE id = ? AND status = 'draft' AND content_hash = ?), not a check-then-write race; None preserves today's behavior unchanged - new update_persona_draft Tauri command (thin, delegates to the service) - typed PersonaDraftConflict with a stable PERSONA_DRAFT_CONFLICT: prefix the frontend can match on - conflict and active-persona rejections emit no persona:draft_updated event - seeded-draft SOURCE_CHANGED_SINCE_SEED freshness computation unaffected * feat: manual persona draft editing frontend (personas v2 P2B) - drafts fully editable in PersonaEditor (extracted to its own component); read-only draft banner and disabled inputs removed - saves send expectedContentHash (CAS); PERSONA_DRAFT_CONFLICT responses show a distinct conflict banner with Reload draft (refetch + repopulate, discarding stale local edits); other errors keep the generic banner - 'Open builder conversation' link via the persona's sourceSessionId (bound drafts stamp the builder conversation id there; builderDraftId is a conversation-side column, spec text adjusted to reality) - client-side canonical document composition for structured-field edits * feat: composer folder references backend (personas v2 P3A) - M2: conversation_folder_references (soft-delete removed_at, live-cap 5 config-driven, UNIQUE(conversation_id, folder_path) WHERE live) - registration validation: canonicalize, absolute non-root, symlink-root rejection, bidirectional app-data containment check (fail-closed), control-char rejection in display_name; canonical form stored - per-ref revalidation at every read: failures EXCLUDE the ref from prompt and roots with folder_refs_skipped diagnostics (never brick the send); repository errors still abort (adversarial-review fix F1) - composer_folder_references feature flag gates commands, overlay, roots (F2) - <referenced_folders> overlay ordered after persona block on fresh/resume/ recovery/queued paths, both harness lanes, all five XML entities escaped; recovery-retry resume carries refs too (F3) - read roots append live refs for Project conversations only; add command rejects non-Project and persona_builder conversations server-side (F5) - three thin Tauri commands, camelCase DTOs, atomic cap enforcement * feat: composer folder references frontend (personas v2 P3B) - 'Add folder' menu row (flag-gated, hidden for persona_builder and projectless composers), native directory picker - FolderReferenceChips shared by persisted and pre-send draft rows: tooltip = full path, aria-labeled soft-remove, TanStack invalidation - first-paint-deferred hydration of chips (rAF + macrotask boundary) - pre-send folders held draft-locally, registered against the seeded conversation before attachment upload; inline cap-error surfacing - composerFolderReferences flag threaded through feature-flag schema/hook - fixes ridden along per zero-failure rule: stale useFeatureFlags strict assertions, unscoped persona-picker query key in AgentsView test Implemented by codex (partial, usage-limit death) + sonnet continuation. * feat: ChatContextType::Standalone variant + exhaustive site coverage (personas v2 P4A.1) - Standalone variant ('standalone'), self-keyed semantics documented; creation sites fail typed until the creation slice lands - named sites: message-queue dispatch converted to exhaustive FromStr (branch_update silent omission fixed), archive stops via the real context, restart + durable recovery enumerate Standalone, queued-context resolver extended (stale-default bug fixed), provider-pause requeue and agent-waiting eligibility include Standalone - audit fixes beyond the compiler sweep: live-send workspace loading, silent-completion recovery, global-pause launch gating, history injection, execution-halt queue management - CWD/workspace resolution intentionally a typed gap until P4A.2 - carry-forward (reviewed, tracked): resume-path standalone drain MUST land with the creation slice; today's pause layers accept what resume cannot drain Implemented by codex (partial, usage-limit death) + sonnet continuation. * feat: standalone workspace service + CWD/read-root arms (personas v2 P4A.2) - standalone_workspace.rs: idempotent ensure_workspace under app_data_dir/standalone_workspaces/conversation-<sha256[:12]>/, symlink rejection, require_under_root containment, manifest.json - crash-orphan startup sweep: deletes only manifest-identified orphans under the canonical root; repo errors, symlinks, unreadable manifests are preserved (fail-closed), registered in startup jobs - resolve_working_directory: Standalone arm resolves the real workspace; typed error on failure, never default_working_directory - resolve_mcp_filesystem_read_roots: Standalone arm = [workspace root], PersonaBuilder mode arm keeps precedence (D9.3) - enforce_filesystem_roots derivation extended to Standalone context in the single Phase 0 seam Implemented by sonnet (codex accounts exhausted). * feat: standalone conversation creation, start arms, pause-drain parity (personas v2 P4A.3) - standalone_conversations flag (AtomicI8 override pattern); creation gated at create command, projectless start, and seeded start; existing rows operate regardless of flag (operation is not gated) - ChatConversation::new_standalone() self-keyed; invariant enforced at both repo layers (adversarial-review F4) - start arm: project_id Option, chat-mode-only allowlist (explicit, no default-mode leak), seeded-ownership all directions - Team invariant enforced at ALL seams: create arm, seeded coordination check, and the send-path coordination flip now Project-only (F1) - pause-drain parity: non-slot drain matches Standalone; end-to-end test asserts DELIVERY after resume (4a.1 review carry-forward) - session namer: standalone runs use the app-owned workspace, skip typed when unavailable (F2); progress events carry the conversation id (F3) - sidebar: list_by_context_type enumeration + data-driven No-project group - get_first_user_message_by_context wires 'standalone' in both repos Implemented by sonnet (codex outage) + codex fix round. * feat: standalone conversations frontend (personas v2 P4B) - chat-context-registry standalone entry (store-key family standalone:) - useStartAgentConversation standalone-aware: optimistic conversation, seeded creation (contextId omitted), store keys, chat-mode startInput - main region + selection model accept project-less conversations; IntegratedChatPanel.projectId nullable with hook short-circuits (no sentinel ids reach project-scoped queries) - start composer: 'No project' inside the project picker (reachable at zero projects, flag-gated), Base/Team/persona/assist hidden, private workspace caption, requiresProject mode metadata with snap-back to Chat - sidebar No-project pseudo-group ( __no_project__ ) with running/queue state and invalidation mirroring project groups - standaloneConversations feature flag threaded through schema/hook * feat: builder mode lifts + seeded-refine provenance (personas v2 P5A) - persona_builder creatable/startable through the standard pipeline (agent_personas-gated); mode-switch and automation rejections kept; standalone start allowlist += persona_builder (Global builds) - ingest-liveness gate retired (pulled forward from 5.5, spec-sanctioned: conditional retirement unimplementable) — new-pipeline builders can send; dangling bound drafts still fail closed at persona resolution - seeded-refine provenance: source_persona_id (builder-mode-only) with exact scope lock, seeding moved into PersonaService (transactional draft+binding), legacy command delegates - standalone builder drafts stamp GLOBAL scope (context-type match closes the P1-review landmine) - Persona-mode Team rejection at create/start/send in any context - Codex-lane rejection for standalone builders enforced by ONE shared predicate at start, send (provider switches), and queued-resume seams (adversarial-review B1) - seeded starts respect persisted mode locks (B2); queued resume fails closed on conversation-lookup errors (B3) * refactor: pre-P5B consolidation (personas v2 P5-CLEANUP) - neutral path-safety home for require_under_root/filesystem_error; persona_ingest and standalone_workspace both consume it - backend-owned abort_seeded_agent_conversation: never-started guard (messages/runs/session ids), draft-status check, ordered deletions, contained workspace removal; frontend error paths call it (leak fix) - workspace create/resolve split: creation only at conversation creation/start; spawn paths resolve fail-closed (no silent recreate) - ResolvedConversationSpawnContext: one construction for folder blocks, roots, workspace, enforcement across fresh/queued/recovery spawns - overlay delivery decoupled from persona-injection attribution - mechanical splits: start service (directory-backed modules), PersonaService transactional internals, spawn-context extraction; unused revalidate_stored_path unified away - integrated no-ingest refine deny-all fixture (Phase 0 §12 gap) - layering baseline: start-service crate::commands violation removed (274 -> 273 tracked entries) * feat: extractor prompt rewrite, analyze/interview/draft (personas v2 P5C) - both harness prompts restructured per D5: Analyze (inventory context, bounded repo sampling for project builds) -> Interview (ask_user_question, max 3 rounds before first draft) -> Draft & iterate (save early, revise the bound draft) - fs denials framed as absent context (no retries, no probing); ingest-era language removed; SKILL.md output contract and 7-tool surface unchanged * feat: builder workspaces, attachment materialization, D9 root restructure (personas v2 P5B) - private workspace created for every builder conversation (both contexts) - composer attachments materialized as text into the workspace (normalized leaf names, containment at the write sink, server-side UTF-8/no-NUL, idempotent re-sync); binary attachments rejected typed - builder prompts reference workspace paths (fs_read_file) — no inlining; non-builder formatting pinned byte-identical by exact-string snapshot - D9 restructure through ResolvedConversationSpawnContext: legacy ingest precedence, Project/Standalone fall-through, CWD-dedup bypassed for builders (enforcement-equivalent), workspace-less legacy refine stays deny-all incl. folder refs and prompt block (lockstep by construction) - folder refs become live enforced roots for builders in both contexts * feat: scope chooser, deep link, Persona mode UI (personas v2 P5D) - Settings 'Build with Agent' scope chooser (Global flag-gated | project), exact deep-link sequence into the Agents start composer - 'Refine with Agent' inherits the source persona's scope, skips chooser - projectLocked lifted into composer-local state at draft consumption; reset effect gated (survives projects-query identity churn) - Persona build banners (Global / project / refine variants) - Persona mode in start list (flag-gated) + conversation chip label; locked conversations disable every mode-menu option (leak fix) - Team toggle and persona picker hidden in Persona mode; attachments and folder chips available; startInput carries mode/sourcePersonaId/null projectId per the backend contract * feat: versioned persona artifacts backend (personas v2 P6A) - M3: builder_result_persona_id + backfill (non-draft legacy bindings moved and cleared); M4: personas.artifact_id + persona-library bucket + one backfill artifact per persona (atomic BEGIN/COMMIT, INSERT OR IGNORE — re-entrant, partial-failure converges; adversarial-review F2) - ArtifactType::Persona (Rust + TS closed enums + both label maps) - ArtifactMetadata extended so appends stamp {persona_version, created_by} - six-writer chokepoint: every persona content mutation appends to the chain IN the same transaction; append failure aborts the write; plain approve validates + recomputes content_hash from the in-tx read (F3); approve-as-new recompose appends as system; graft-on-apply appends to the source chain with forensic metadata, interim draft rows orphaned intentionally; draft hard-delete/bound-delete/project-sweep/seeded-abort all delete chain rows transactionally (F1) - uniform binding transitions across all three approvals + typed post-approval save rejection; archive keeps the result pointer - get_artifact_version_history += created_by/metadata; persona:draft_updated += artifact_id (additive) * feat: Persona artifact tab (personas v2 P6B) - Persona tab in the artifacts pane, mode-gated to persona_builder (AUTOMATION_TAB precedent; unavailable-reason exhaustiveness kept) - four states from conversation bindings: draft (Approve/Approve-as-new), approved (read-only result + Open in Settings/Refine deep link), archived result (badge, refine disabled), empty - version dropdown with created-by attribution incl. backfill/system; historical versions read-only; skeleton-first hydration - persona:draft_updated.artifact_id drives query invalidation - backend: builder_result_persona_id exposed on the shared AgentConversationResponse (every hydration path) so approved/archived state reconstructs after reload — both serialization directions tested * fix: atomic first-draft binding claim + single enforcement seam (personas v2) - create_bound_draft claims the conversation's builder_draft_id via a conditional UPDATE (NULL draft and result bindings required) — two concurrent first saves can no longer clobber each other; loser gets a typed Conflict mapped to HTTP 409; NotFound distinguished - ResolvedConversationSpawnContext::without_app_state no longer re-derives the filesystem-enforcement flag inline — routed through build_mcp_runtime_context, restoring D9's single derivation seam (pre-cutover audit finding #1) * fix: pre-cutover audit remediation (personas v2) - global-persona Refine gated on standalone_conversations at both Settings and the Persona artifact tab (disabled + tooltip when off) — closes the flag-interaction hazard that would strand a rejected projectless draft after cutover (audit #2) - Settings PersonaEditor gains the UX-1 Version-history affordance, sharing the Persona tab's attributed history components (audit #3) - persona_service_tests and persona_update_approval_tests mechanically split by concern under the 500-line rule; test counts preserved (195 == 195) (audit #5) * feat: legacy builder cutover (personas v2 P7) - Settings routes exclusively through the scope chooser + deep link; PersonaBuilderView, ingest_persona_context, get_persona_builder_ingest_status, create_persona_builder_conversation deleted (write path only — the legacy ingest READ path and D9 fall-through stay, old conversations resumable indefinitely; on-disk addressing now pinned by a literal-hash test) - carried review fixes: app_data_dir threaded (reverse-derivation gone, literal-dirname regression test), materialized-file existence check at prompt render (typed failure, no dangling paths), dead ConversationFolderReferenceUnsupportedMode variant and dormant PersonaRepository::update_content removed (chokepoint closed by construction), stale doc comment fixed - docs: agent-personas.md rewritten for v2, persona-ga-gates.md gates updated for the new flow, honest binary-rejection wording, agent-mcp-tools/agent-type-map deltas - fix round: orphaned ingest-schema imports removed from persona.test.ts (both independent reviews caught it; tsc excludes tests, vitest caught) -3,377/+335 net. All 8 spec phases now landed. * test: post-v1 hardening — D9 tripwire, rollback matrix, flag matrix (personas v2) - check-layering.py gains the filesystem single-derivation invariant: exact-count allowlist of legitimate enforce_filesystem_roots sites; any new derivation/construction site fails the ratchet - induced-failure rollback tests for the remaining artifact writers (update_persona, create_persona_draft, plain approve, approve-as-new incl. recompose) — content/tip/status/bindings unchanged on append failure, completing the six-writer atomicity matrix - flag-matrix coverage across agent_personas x standalone_conversations x composer_folder_references at backend seams + both frontend gating surfaces (tracker documents pre-existing vs new coverage) - ride-along: extractor agent.yaml description de-ingest-ified (stale wording; out of declared scope, kept deliberately) * fix: big-PR checklist merge-gate remediation (personas v2) - F1 HIGH: seed createConversation now sends mode — the US-1 flow (Global build + attached folders) works; masking test rewired through the production seed command; E2E test added - F2 HIGH-coverage: enforcement-flag parity asserted on recovered and queued builder/standalone spawns (fail-open-by-default regression tripwire at the test layer); MCP context wiring pinned - F3 security: Codex extractor spawns get the full apply_patch-disable override set via typed config (was plan-profile-only) — native writes can no longer bypass MCP enforcement on project Codex builds - F4 security: enforced mode ignores ambient RALPHX_FILESYSTEM_READ_ROOTS — zero CLI roots means deny-all, never env inheritance (MCP server rebuilt); the env-leak class D9 closed for the flag now closed for roots - F6: PersonaArtifactPanel keyed by conversation id — no cross- conversation approve/error bleed * fix: big-PR checklist fast-follows (personas v2) - Codex rejected for ALL standalone conversations (chat included) — the D3 permission-prompt boundary assumed Claude; danger-full-access has none (owner-reviewable: one-line revert if the lane should instead be documented as an exception) - builder attachment deletion prunes the materialized workspace copy - abort-refused starts reveal the surviving conversation (sidebar invalidation) instead of silently hiding it - frontend error states distinct from empty: folder-ref list warning (with backend-divergence note + retry), persona picker error row, version-history error rendering - resumed builders fail closed at send when agent_personas is off - LOW batch: sticky remove-disable fixed, fetchQuery rejection handled, message-bearing conversations can't be converted to builder via seeded start, folder-chip double-writer wipe removed, MCP fail-open default param removed (rebuilt), flag-off folder list now typed FeatureDisabled * test: split oversized persona integration suites * test: align plan verification expectations * fix: contain standalone workspace filesystem sinks Intent: Close CodeQL path-injection alerts #384 and #385 without weakening the standalone workspace lifecycle or trusting construction provenance at filesystem sinks. Behavior: - Validate the process-owned app-data path as absolute, non-root, and free of traversal components before creation or lookup. - Canonicalize the app-data and workspaces roots, reject a symlinked workspaces root, and re-check containment before create, metadata, read, and removal operations. - Keep conversation identifiers hash-derived, so absolute and traversal-shaped IDs remain inert path input. - Apply the same guard seam to create, resolve, remove, and startup sweep paths so adjacent operations cannot bypass the boundary. Compatibility and failure semantics: - Preserve creation when the process-owned app-data directory does not exist yet. - Preserve resolve as non-creating with StandaloneWorkspaceMissing for absent roots. - Preserve removal as an idempotent no-op when no root exists. - Keep orphan sweeping fail-closed around unreadable, invalid, or symlinked entries. Verification: Focused TDD captured the missing-root regression before the final mapping fix. The exact standalone workspace suite passes 19 tests covering normal/idempotent creation, absolute and traversal IDs, root and entry symlink escapes, missing-root resolve/remove behavior, and sweep containment. Both touched files pass surgical rustfmt checks and the staged diff passes git diff --check. * test: cover Persona artifact type registration Intent: Keep the ArtifactType registry contract aligned with the Persona artifact introduced by Personas v2, and turn the stale CI count failure into explicit behavioral coverage. Behavior: - Updates the registry cardinality from 23 to 24. - Asserts Persona is present in ArtifactType::all(), so a future omission cannot hide behind a count-only expectation. - Verifies Persona's stable snake_case wire representation in both serialization and deserialization directions. Risk and compatibility: This is a test-only correction; production behavior and persisted wire values are unchanged. Verification: - cargo test --manifest-path src-tauri/Cargo.toml -p ralphx-domain artifact_type_all_includes_persona - rustfmt --edition 2021 --check src-tauri/crates/ralphx-domain/src/entities/artifact/tests.rs - git diff --check * fix: align standalone conversation security across providers Intent Support Personas v2 standalone conversations on both Claude and Codex without weakening RalphX filesystem containment or allowing caller-supplied context to select a more permissive launch policy. Make the persisted conversation the authority for identity, workspace mode, provider launch security, and recorded runtime metadata. Behavior - Launch standalone Chat and PersonaBuilder flows through either Claude or Codex for fresh sends, true resumes, queued continuations, and session recovery. - Resolve the actual context-specific global role default before provider capability validation; explicit provider overrides remain authoritative. - Record the effective Codex approval and sandbox policy on fresh and queued AgentRun rows so audit/UI metadata matches the command that ran. - Persist replacement Codex thread identity after recovery and retain the provider-specific session on subsequent work. - Keep frontend start payloads, canonical MCP grants, generated MCP tests, docs, and multi-harness guidance aligned with the bilateral runtime. Security boundaries - Derive one exhaustive ConversationLaunchSecurityClass from the authoritative persisted context and workspace mode. - Standalone Claude Chat uses the CLI prompt boundary: permission mode default, no dangerous skip flags, native Read/Grep/Glob remain available but are not preapproved, and the permission bridge remains available. - Standalone Codex Chat uses on-request plus workspace-write. Because codex exec is noninteractive, operations requiring a new approval fail closed; Project and standalone PersonaBuilder retain the existing MCP compatibility policy. - Validate explicit conversation identity before provider resolution, pause enqueue, workspace rollover, event emission, stdin delivery, or live-process cleanup, and validate again at the later dispatch seam. - Disable Codex apply_patch whenever the canonical agent disables shell_tool, and assert exact Persona Extractor grants plus General Explorer denials. - Keep filesystem enforcement and private standalone read roots intact across fresh, queue, resume, and recovery paths. Structure and compatibility - Extract provider-specific policy objects from the Claude and Codex command modules while keeping those modules as the canonical CLI construction seams. - Name external CLI protocol values through Rust constants; lowercase values such as default, on-request, and workspace-write intentionally match provider CLI contracts. - Move the pre-existing Claude inline test block to a sibling test module and split provider security coverage into bounded leaves with shared validated fake CLI fixtures. - Preserve configured Project and PersonaBuilder launch behavior, legacy provider settings, explicit overrides, and the existing default-off feature flags. Verification - Focused provider, start, queue, recovery, Gate-1 identity, capability, and prompt-contract tests pass, including true Codex exec resume and real attempt_session_recovery paths. - Frontend AgentsView start coverage: 60/60 passed. - MCP tool authorization/schema coverage: 228/228 passed; package build regenerated checked-in output. - Both Rust Clippy matrices pass with warnings denied. - Layering ratchet, integration module checker, touched-leaf rustfmt checks, and diff checks pass. - PR Rust stack passed layering and IPC 120/120; library lane passed 4,950/4,966 in the sandbox. The 16 blocked tests were all host-resource restrictions and passed 16/16 when rerun with normal worktree, listener, and keychain access. * fix: harden standalone workspace path sinks Intent Close the two outdated CodeQL path-injection review threads with current-tree evidence and bring the adjacent orphan-sweep sinks up to the same sink-local containment standard. Preserve the fail-closed workspace lifecycle: no untrusted or filesystem-derived path may authorize inspection, manifest reads, canonicalization, or deletion outside the app-owned standalone root. Behavior - Keep conversation identifiers hash-derived before they become path components. - Reject symlinks at the derived workspace destination and at manifest.json. - Treat unexpected metadata failures as explicit filesystem errors instead of assuming the path is not a symlink. - Skip orphan-sweep candidates unless the entry and fixed manifest path are proven contained under the canonical standalone root. - Preserve valid live workspaces, unreadable or unprovable entries, and every outside symlink target. Security and CodeQL - Validate containment immediately before symlink_metadata, read_to_string, canonicalize, and remove_dir_all sinks. - Pass the canonical root into manifest parsing so that helper owns its validation instead of relying on caller provenance. - Keep each rust/path-injection suppression immediately adjacent to a sink whose path has already been validated. - Retain post-canonicalization containment as defense in depth before recursive removal. Tests and verification - Add behavioral coverage proving create rejects a symlink at the hash-derived workspace path and preserves its outside target. - Add sweep coverage proving a symlinked manifest cannot authorize deletion and its outside target survives. - Standalone workspace module: 21/21 passed. - Both warnings-denied Clippy matrices passed. - Layering ratchet, integration module checker, leaf rustfmt, and diff checks passed. * fix: make Personas CI validation deterministic Intent: Restore trustworthy PR validation after the final Personas v2 rebase exposed one frontend state race, two environment-dependent Rust assertions, scanned test diagnostics, and repeated hosted-runner termination during the cold integration build. Behavior: - Keep project-backed Edit mode stable while the first available project is selected when standalone conversations are disabled. - Align the visual contract with the shipped Ideation start mode without accepting unrelated snapshot churn. - Exercise durable Project and Standalone recovery through the established paused queue seam, asserting one attempt-marked recovery per context and no provider runtime dispatch. - Align the task-execution rule test with main's focused-validation ownership and timing contract. Security and provider alignment: - Preserve the exact Codex PersonaExtractor MCP-grant predicate while removing runtime-derived override values from assertion diagnostics, closing CodeQL cleartext-logging and log-injection flows. - Keep recovery coverage provider-neutral: neither Claude nor Codex CLI availability can change the result. CI reliability: Cap full-integration compilation at two Cargo build jobs, matching the existing coverage-lane resource pattern. Five unbounded control runs, including a dedicated rerun, received runner shutdown/SIGTERM before any test executed. Validation: Focused Rust regressions 3/3; frontend Vitest 4/4; the four named Playwright visual cases pass against existing baselines; frontend typecheck and lint pass; both Clippy feature matrices pass with warnings denied; leaf rustfmt, layering, integration-module, workflow YAML, shell syntax, and diff checks pass. * fix: align filesystem enforcement ratchet after extraction Intent: Keep the single-derivation security tripwire authoritative after the conversation spawn-context logic was mechanically extracted into its own sibling module. Behavior: The ratchet now expects the two live direct build_mcp_runtime_context derivations in resolved_conversation_spawn_context.rs and no longer expects the obsolete pre-extraction super:: call spelling. Production enforcement behavior is unchanged. Security: The allowlist remains exact-counted and fail-closed: any additional derivation site, missing canonical site, or alternate spelling still fails the layering gate. Validation: python3 scripts/check-layering.py git diff --check * fix: complete bilateral PersonaBuilder alignment Intent: Make the Personas v2 builder and standalone flows provider-neutral and fail closed at one canonical conversation-identity seam, while preserving all rebased main behavior and legacy Claude configuration compatibility. Behavior: Treat PersonaBuilder as valid only for Project or Standalone conversations across creation, attachments, folder roots, draft writers, runtime injection, and recovery. Reject corrupted Task/Ideation builder rows before filesystem, draft, or provider side effects. Persist provider/model/effort and capability intent consistently in frontend starts and continuations for both Claude and Codex. Recovery and failure atomicity: Reload the authoritative conversation during recovery, restore bound draft metadata and effective builder agent without leaking draft content, and apply the same recovery path to Claude and Codex. Delay existing-conversation mode changes until workspace preparation succeeds; restore prior runtime state and remove only newly created private workspaces when persistence or draft seeding fails. Provider alignment and compatibility: Expose shared Personas flags and limits through the provider-neutral agents facade while retaining legacy Claude reexports. Keep Claude native-agent injection suppression Claude-only so Codex continues receiving the backend persona contract. Preserve provider-specific launch security already established by this PR. Security: Extend the canonical builder predicate to every filesystem and six-writer sink. Keep filesystem-root derivation ratcheted, validate authoritative recovery identity before replay, and prove invalid contexts leave draft versions/content and workspace files untouched. Validation: Frontend focused matrix 160/160; IPC guards 2/2; HTTP writer guard 1/1; runtime/recovery guards 4/4; compensation 1/1; domain predicate 1/1; bilateral recovery 1/1 plus adjacent security 2/2. Layering ratchet, integration module check, leaf rustfmt, and diff checks pass. * fix: satisfy bilateral test helper Clippy gate Intent: Keep the shared Claude/Codex continuation fixture warning-free under the required all-targets Clippy configuration. Behavior: Pass the Copy conversation identifier by value instead of cloning it; runtime and test behavior are unchanged. Validation: Both the no-default-features Clippy gate and the all-targets/all-features Clippy gate pass locally with warnings denied; leaf rustfmt and diff checks pass. * test: make Personas queue contracts deterministic Intent: Keep the Personas v2 standalone and queued-builder contracts independent of provider binaries, global probe timing, and asynchronous process-marker races in parallel CI. Behavior: Seed the authoritative completed provider-session owner required by current main before testing a queued builder continuation; wait up to five seconds for the spawned fake CLI capture; provide the private workspace required by the missing-row queue entry; and seed harness availability before the ownership rejection test. Production behavior is unchanged. Why: Current-head CI reproduced three fixture-order failures: provider discovery preempted the intended ownership assertion, a queue helper resolved an intentionally absent workspace, and the queued process capture was read before dispatch completed. Validation: All three failures pass together under the CI nextest profile. All-targets/all-features Clippy passes with warnings denied; layering, suite module registration, leaf rustfmt, and diff checks pass. * fix: separate folder chip tooltip triggers Intent: Make the icon-only folder removal control reliably expose its accessible tooltip in Chromium, WKWebView, and parallel frontend tests. Behavior: Render the folder path trigger and remove-button trigger as sibling Radix tooltips instead of nesting the remove tooltip inside the path tooltip region. The button keeps its folder-specific accessible name and the visible tooltip remains 'Remove folder'. Why: Exact-head CI reproduced a race where hovering the nested remove trigger opened competing tooltip state and the required remove tooltip was absent. The isolated test passed intermittently, confirming structural timing rather than missing copy. Validation: AgentComposerSurface 91/91; frontend TypeScript check; full frontend ESLint; diff check. * fixes CI * test: restore Personas coverage and harden CI diagnostics Intent: Restore meaningful patch coverage after the Personas integration suites were mechanically split, without weakening the 90% Codecov gate or excluding production code. Behavior: Enroll the existing PersonaBuilder attachment and standalone start contracts in the IPC coverage selector using the established persona/ipc_contract naming convention. Add failure-atomicity regressions for binary attachment materialization and seeded refine draft insertion: prior Chat/Solo state and source attachments survive, partial private workspaces and draft bindings are removed, and no agent run is persisted. Provider alignment: Keep the contracts provider-neutral where behavior is shared, retain explicit standalone Codex launch coverage, and prove a failed Codex-selected refine start never invokes the provider. Security: Replace value-printing equality diagnostics for externally derived ClickUp keys with exact value-free predicates and static messages, closing the CodeQL cleartext-log path without weakening assertions. Why this differs from main: Main does not contain the Personas v2 split production seams. On this PR those mechanically extracted files count as new patch lines, while several already-valid integration behaviors were omitted by the coverage job's focused selector. Validation: 13 affected IPC behaviors and the ClickUp persistence test pass; both warnings-denied Clippy matrices, layering ratchet, integration module checker, surgical rustfmt checks, and diff checks are green. * test: close final Personas coverage and CodeQL gaps Intent: Clear the two external checks left after the otherwise-green b86836cd5 run: Codecov patch coverage at 89.64041% and the repeated ClickUp cleartext diagnostic. Coverage behavior: Exercise the PersonaBuilder attachment removal contract through three real production outcomes: a missing private workspace is an idempotent no-op, a missing materialized file is an idempotent no-op, and a symlinked materialized path fails closed while its outside target survives. The test name intentionally participates in the existing persona IPC coverage selector and covers more than the roughly nine executable lines still required. Security behavior: Keep exact ClickUp title, queued-reference, and persisted-link assertions while separating externally derived string comparisons from assertion macros. Assertion diagnostics now receive only booleans and static intent messages. Scope: Test-only change; no production behavior, provider policy, feature flags, or prior Personas work is modified. Both Claude-neutral shared behavior and Codex support remain unchanged. Validation: The focused PersonaBuilder removal integration test and ClickUp persistence lib test pass; both touched Rust leaves pass surgical rustfmt checks and the diff is clean. * test: cover remaining attachment symlink guards Intent: Close the final Codecov patch gap after 946aa3665 raised coverage to 89.85445%, four executable lines below the 90% requirement. Behavior: Extend the existing PersonaBuilder attachment containment contract through both write-side symlink branches. Materialization rejects an existing destination symlink before overwrite and rejects a stored-source symlink before read; both assertions prove the outside target remains unchanged. Coverage direction: The two production guards account for six currently uncovered lines in builder_attachment_materializer.rs, providing a two-line margin over the four-line threshold gap without exclusions, no-op execution, or production changes. Security: The test canonicalizes the app-owned attachment source and verifies containment under canonical storage before its fixture sinks. Sink-local CodeQL annotations document that validation. Validation: The focused suite_ipc_commands regression passes; the touched leaf is rustfmt-clean, remains below the 500-line limit, and the diff check is clean. * test: keep sensitive values out of diagnostics Intent: Clear the last GHAS CodeQL status after all functional, coverage, CodeQL analysis, and repository code-scanning gates passed on aafb23a72. Root cause: CodeQL models Rust assertion macros as diagnostic sinks even when externally derived ClickUp keys are first reduced to booleans. The two key flows therefore remained attached to ticketing_commands/tests.rs:3910. Behavior: Retain exact checks for the conversation title, queued provider-neutral reference, and persisted external link. Wrong values still fail the test, but ordinary if guards now call panic with static text only; no externally derived value reaches a diagnostic argument or format input. Scope: Test-only diagnostic hardening. No production ticketing, Personas, provider, filesystem, or persistence behavior changes. Validation: The focused ClickUp persistence lib test passes; the touched Rust leaf is rustfmt-clean and the diff check is clean. * test: align Personas queues with excerpt references Intent: Repair the single post-main-merge compile failure shared by Rust Integration Archive, Rust IPC Coverage, and all-target Clippy. Root cause: Main #797 added composer_excerpt_references before attachment_ids in MessageQueue::queue_with_runtime_overrides_and_project_references. Five Personas v2 queue fixtures still used the legacy 17-argument call shape. Behavior: Supply an empty excerpt-reference vector in the new slot for the two PersonaBuilder queue cases and three standalone Claude/Codex queue cases. Preserve the existing empty attachment-id vectors and all provider/security assertions; these scenarios intentionally carry no selected excerpts. Provider alignment: Update both Claude and Codex standalone continuation fixtures symmetrically, plus provider-neutral rejection and builder gates. Validation: The exact five affected suite_chat_service tests pass 5/5; focused suite-target Clippy passes with warnings denied; both touched Rust leaves are rustfmt-clean and the diff check is clean. * test: remove sensitive branch data from diagnostics Intent: Clear the final GHAS CodeQL blocker on PR #779 without weakening the ticket-start contract or changing runtime behavior. Root cause: The full Rust SARIF shows both reported sources flow through AgentConversationStartService::start into the returned workspace, then terminate when the ticket-branch assertion formats workspace.branch_name into panic output. The alert is displayed on the next tokio::test attribute because of macro source mapping, which made the earlier ClickUp-key assertions appear responsible. Behavior: Keep the exact ticket-derived branch-prefix assertion but use a static failure message, so a failure cannot disclose the runtime branch. Restore the unrelated ClickUp title, queued-reference, and persisted-link checks to concise matches assertions with static diagnostics. Scope and compatibility: Test-only diagnostic hardening. Claude and Codex runtime behavior, provider selection, ticket persistence, workspace creation, and production error semantics are unchanged. The inherited dynamic diagnostic exists on main, but this PR must remove it because GHAS evaluates the PR merge result. Validation: Both focused ticket-start tests pass, the touched Rust leaf passes rustfmt --check, and git diff --check is clean. * fix: make Personas folder context reachable and reference-aligned Intent: close the remaining gap between the converged Personas v2 plan and the native composer. The live-folder backend was already present, but the default-off capability had no Settings path, so normal installs hid Add folder even while Personas were enabled. The standalone flag was also missing from the Rust UI response, forcing the frontend fallback to false and making projectless conversations and Global Persona builds unreachable. Behavior: expose the effective standaloneConversations value, persist Folder context through the existing feature-flag command, keep all three rollout flags explicitly default-off, and retain Add folder directly beneath Add files. Render persisted and draft folders through the same reference-pill component as Plan, ticket, and artifact context, including a visible Folder badge, full-path hover and keyboard tooltip, and accessible removal. Runtime direction: folder references remain canonical live read-only roots and continue through the shared conversation spawn context used by both Claude and Codex. This commit changes reachability and presentation, not the containment, persistence, prompt-overlay, or provider authorization model. Verification: 202 focused frontend tests; TypeScript; ESLint; two focused Rust feature-flag tests; standalone builder folder-root integration; 11 folder-reference service tests; recovery/retry folder-root test; rustfmt; diff check; layering ratchet; independent static alignment review. * fix: retire legacy Claude RalphX MCP registration safely Intent: restore Claude launches for users upgrading across main's provider-native MCP policy change. Older RalphX releases registered a user-scoped server named ralphx; the new reserved-ID preflight correctly rejects third-party collisions but previously mistook RalphX's own persisted registration for one, blocking every Claude spawn before Persona or folder context could run. Behavior: at the shared MCP launch-policy seam, detect only the complete historical RalphX stdio signature rooted under the current app-data directory, serialize concurrent cleanup attempts, remove that one user-scoped entry through the resolved Claude CLI, rediscover provider-native state, and then run the unchanged fail-closed reserved-ID check. The migration is Claude-only and idempotent. Security: arbitrary or partially matching Claude registrations remain untouched and rejected; ralphx_internal remains reserved; Codex receives no migration bypass and now has a real-file reserved-ID regression test. Unrelated provider-native MCP definitions and metadata remain provider-owned. Why this is not on main: commit 8f1587d9f removed startup registration and introduced collision preflight without retiring entries that earlier RalphX versions had written. The live config reproduced that exact upgrade residue, and the running app successfully removed it through this path. Verification: 10 focused launch-policy tests passed, including exact cleanup, idempotence, mismatch preservation, and bilateral Claude/Codex rejection; rustfmt, diff check, and layering ratchet passed. * fix: scope the legacy MCP cleanup seam to unit tests Rust Clippy builds the library with all features but without cfg(test). The fake-CLI injection method was therefore present and unused under test-utils, and -D warnings rejected the build. Limit the helper to unit-test compilation; production cleanup behavior and integration-facing APIs are unchanged. Verified with the failing CI-equivalent lib Clippy command using all features and -D warnings. * fix: retain folder context in conversation history Folder references were persisted only as live conversation roots, so the agent could read an attached folder while the corresponding user message lost that context in the transcript. Snapshot the currently validated live folder references into user-message metadata at the shared ChatService send seam before immediate persistence or queueing. Preserve an existing snapshot during replay, omit removed or unavailable roots, and keep malformed pre-existing metadata intact. Thread the same display-only snapshot through active, review/merge, question-mode, and seeded-start optimistic messages, then parse and render it with the established message reference pills after reload. The frontend snapshot is never sent as filesystem authority; Claude and Codex still derive read roots only from the repository-backed containment service. Document the immutable history behavior and cover parser/rendering, hydrated sends, seeded sends, review sends, invalid-folder omission, removal semantics, and queue replay stability. Validation: 244 focused frontend tests; 130-test post-cleanup frontend rerun; 3 focused Rust tests; TypeScript; ESLint; rustfmt checks on touched Rust leaf files; layering ratchet; diff checks. * fix: persist persona builder results in conversation Persona Builder could still look like a Markdown generator: its prompts did not make persistence a completion requirement, the result pane stayed closed, and the first draft event was not scoped strongly enough to bind a stale conversation view safely. Require both Claude and Codex builders to save one named persona lineage through save_persona_draft before claiming completion. Plural requests now choose one persona and use separate builder conversations; copy/paste and Settings handoffs are explicitly rejected. Scope agent-save events with the owning builder conversation, consume only matching events in the Persona panel, and auto-open that panel on the Persona tab. The saved draft appears immediately with the existing in-conversation Approve persona action while foreign events remain isolated. Keep the transactional PersonaService writer and explicit approval boundary unchanged. Rebuild the MCP server output, document the direct-save UX, and add focused bilateral, backend, and frontend regressions. * fix: launch persona builder from project conversations Intent: Let users turn a project Agent conversation into a real Persona-building workflow without copying generated Markdown into Settings. The existing persona chip was a binding switcher only, which left ordinary Agent conversations with no route to the transactional builder. Behavior: Add a Create persona for this project action to the active conversation persona menu. The action opens a new Persona Builder starter locked to the current project, while leaving the source conversation and its immutable mode unchanged. Standalone conversations cannot invoke the project action. Architecture: Route the transition through useAgentConversationActions and its existing showStarterComposer owner so focused-project, optimistic selection, persisted selection, and task-history state stay synchronized. Keep save_persona_draft restricted to the dedicated Persona Builder tool surface and preserve the one-conversation/one-persona lineage. Validation: - 107 focused Vitest tests across menu, action owner, panel routing, and main-region wiring - frontend TypeScript check - frontend ESLint - git diff --check * fix: align persona artifacts with plan rendering Intent: - Remove the bespoke in-chat Persona artifact presentation and make Persona documents use the same established versioned Markdown surface as Plan and Review. - Preserve Persona domain identity and lifecycle behavior; this does not replace Plan data, tabs, references, or actions. Behavior: - Loads the canonical Persona artifact by artifact_id and renders its heading, Markdown, overflow actions, version dropdown, historical banner, and Back to latest flow through the shared artifact display. - Renders canonical Persona YAML frontmatter as structured Description, Name, and Kind metadata instead of collapsing it into Markdown prose. - Applies frontmatter parsing to each selected artifact version so historical metadata and body stay consistent. - Uses the canonical compact approval control and suppresses all lifecycle actions while a historical version is open. - Keeps approve-as-new, Settings, and agent refinement as Persona-owned actions. - Uses shared artifact loading and empty states; legacy null-artifact Personas retain a safe version-one display fallback. Architecture and safety: - Exposes VersionedArtifactDisplay as a domain-neutral adapter over the existing PlanDisplay implementation already reused by Review. - Adds narrow content preparation and action composition seams rather than copying Plan markup or version state. - Disables excerpt selection for Persona until persona is supported end to end as a composer reference kind, preventing false Plan references. - Fails closed when a bound artifact id cannot be resolved. Validation: - 76 focused Vitest cases pass across Persona parsing, PersonaArtifactPanel, and PlanDisplay. - Frontend TypeScript typecheck passes. - ESLint passes for every touched frontend source and test file. - git diff --check passes. * fix: preserve tolerant persona editor parsing Intent: - Repair the frontend CI regression introduced when structured Persona artifact parsing was added. Behavior: - Keeps strict name/kind/description parsing for the read-only artifact metadata presentation. - Restores splitPersonaBody's established editor/import contract: any correctly delimited frontmatter block is removed even when it is partial. - Adds a focused regression test for partial frontmatter used by Settings editor flows. Validation: - 103 focused tests pass, including the exact PersonasSection CI failure plus Persona artifact and Plan display suites. - Frontend typecheck passes. - ESLint and diff checks pass. * fix: unify Persona Builder artifact workflows Intent: - make Persona Builder conversations behave as the direct creation and refinement surface instead of sending users through duplicate or generic UI paths - give Persona artifacts the same document and version UX as Plan artifacts everywhere they appear Behavior: - show only the Persona artifact in Persona Builder conversations, ignore stale hidden-tab preferences, and remove tab customization and redundant Refine with Agent actions in that mode - deep-link Open in Settings to the exact draft/active Persona and preserve the originating conversation so Open in Agent returns to that conversation - route linked project and standalone conversations through their real context without borrowing the active project, and close Settings before navigation - enlarge the Markdown instructions editor and widen the version-history dialog Architecture: - reuse VersionedArtifactDisplay and one shared Persona YAML-frontmatter preparation path for both the Agent pane and Settings history - keep artifact-tab policy in AgentsArtifactPane, entity deep-link resolution in PersonasManagementSection, and context routing in the canonical navigation helper - replace the obsolete Settings-only Persona history hooks with canonical current-artifact query keys; manual/agent updates now invalidate the cache used by both hosts Correctness: - deep links wait for an authoritative Persona list, apply once, reject missing/archived targets, and let Back remain on the list - standalone navigation selects a null project and the canonical standalone chat key - historical Persona versions remain read-only and structured YAML metadata is never rendered as collapsed Markdown Validation: - 77 focused Vitest assertions pass across Persona artifacts, Settings, mutation/event invalidation, and project/standalone navigation - focused Persona-only AgentsArtifactPane policy test passes - frontend TypeScript, touched-file ESLint, and git diff checks pass * fix: refresh Persona artifacts after manual saves Intent: Keep an open Persona artifact pane synchronized with the transactional Persona version created by a manual Settings save. Root cause: The update mutations invalidated Persona lists and the returned artifact query, but left the cached Persona detail row untouched. The pane derives its artifact ID from that detail row, so it remained pinned to the prior artifact version even though the backend had created and returned the next version. Behavior: Publish every successful Persona write into the canonical Persona detail cache before refreshing its exact artifact tip. Apply the same centralized handoff to active edits, draft edits, approval, and approve-as-new so all mutation paths preserve one cache contract. Coverage: Extend mutation tests to assert the returned Persona becomes the detail-cache source of truth, and add an open-pane regression proving a manual save moves the rendered artifact from v3 to v4 with updated frontmatter and Markdown. Validation: - focused usePersonas mutation tests - focused PersonaArtifactPanel v3-to-v4 integration test - frontend TypeScript check - touched-file ESLint - git diff --check * fix: isolate Persona artifact state by conversation Intent: Preserve the established conversation boundary after Persona artifacts moved from a bespoke nested scroller onto the shared Plan artifact surface. Root cause: The prior Persona panel owned a keyed overflow container, so switching builder conversations replaced the scroller and discarded local approval state. Plan-aligned rendering removed that nested scroller; the persistent outer artifact pane then leaked the previous Persona conversation's scroll position even though the keyed Persona component still protected its local mutation state. Behavior: Key the shared artifact-content scroller by conversation only in Persona Builder mode. A Persona conversation switch now resets scroll, selection-provider ownership, and the rendered approval subtree. Other artifact modes retain their stable content boundary so in-flight publish dialogs and progress state are not remounted during ordinary updates. Coverage: The existing bidirectional alpha/beta delayed-approval tests again prove a fresh scroll container at position zero and prove a late approval cannot overwrite the newly selected Persona. The publish-progress ownership test confirms the narrowed key does not regress non-Persona workflows. Validation: - all 199 AgentsArtifactPane tests - focused Persona-switch and publish-progress cases - frontend TypeScript check - touched-file ESLint - git diff --check * fix: close persona ownership and metadata gaps Intent: - Address both blocking findings from Bogdan’s PR #779 review. - Restore the one-conversation/one-draft authorization invariant for Persona Builder reads. - Keep structured Persona state and immutable artifact history consistent with canonical Markdown after manual edits. Caller-bound draft reads: - Make get_persona_draft consume X-RalphX-Caller-Session-Id just like save_persona_draft. - Centralize caller header parsing, conversation lookup, and PersonaBuilder Project/Standalone validation in one handler helper shared by GET and SAVE. - Fail closed unless the caller conversation’s builder_draft_id exactly matches the requested draft before any draft lookup occurs. - Cover missing identity, non-builder callers, cross-draft access, and the valid bound-draft path; retain adjacent SAVE authorization coverage. Transactional metadata persistence: - Pass the already-validated ParsedPersona into the existing update_content_with_artifact chokepoint. - Atomically write parsed name and description together with content, content hash, version, and timestamp before appending the new immutable artifact. - Preserve CAS rejection and status guards; no new writer or parallel update path is introduced. - Prove draft and active manual updates return and reload canonical structured fields and append artifact tips with the updated title, content, version, attribution, and persona metadata. Validation: - suite_http_handlers get_persona_draft_ filter: 4 passed - adjacent SAVE caller-validation filter: 3 passed - suite_ipc_commands draft/active persona update filters: 2 passed - rustfmt --edition 2021 --check on all four touched Rust leaf files - git diff --cached --check
…ontext, standalone conversations (#779) * docs: add personas v2 builder scoping handoff spec * feat: enforced-mode filesystem containment in ralphx-mcp-server (personas v2 P0B) - argv-only --filesystem-enforced flag (no env fallback, no env write) - enforced mode: configured read roots only, no implicit CWD, empty roots deny all - realpath containment incl. symlink-inside-root escapes and ENOENT parent checks - read-tools-only isTrustedReadRootPath in permission bridge; Bash branch untouched - rebuilt tracked build/ output * feat: filesystem-enforcement flag plumbing + conversation-id threading (personas v2 P0A) - McpRuntimeContext.enforce_filesystem_roots, derived in one seam (build_mcp_runtime_context receives effective mode; true iff persona_builder) - --filesystem-enforced 1 rides MCP server CLI args only; never process env - real conversation_id threaded through fresh/resume/queued/recovery runtime contexts in Claude and Codex lanes; Codex no longer substitutes the project context_id for the conversation id - launch plan rejects missing/mismatched conversation ids (typed error) - spawn-shape parity + enforcement emission tests across both harnesses Note: suite_chat_service carries 5 pre-existing failures inherited from main (plan-verification revamp fallout, red on main CI since #752); this commit adds zero new failures. * feat: save_persona_draft 3-state fail-closed + extractor prompt touch-up (personas v2 P0C) - caller-session header required: missing header / unknown conversation / non-builder conversation each rejected with typed, actionable errors - client draft_id no longer an authority source (comparison-only against the conversation's bound draft); unrestricted fall-through removed - bound-draft create/update flow preserved; cross-conversation writes rejected - extractor prompts (both harnesses) lead with interview + draft tools and treat fs denials as absent context instead of an error state * feat: persona project scoping backend (personas v2 P1A) - M1: personas.project_id (NULL = global) + scoped active-slug unique index (slug, IFNULL(project_id,'')) WHERE active; map_live_slug_unique_error updated to the new index name - PersonaScopeFilter (All | GlobalOnly | GlobalAndProject) through repo, service, and list_personas command; drafts stamp project_id (empty/blank ids rejected at the boundary) - shared bindability predicate at bind/start/send; scope mismatch suppresses with project_scope_mismatch attribution BEFORE rendering; repo errors stay typed and abort the send - Explicit directive: suppressed by Automation/PersonaBuilder/verification, exempt from the start-path agent-name override (adversarial-review fix) - approval-module raw-SQL slug checks scope-aware in both directions - transactional delete_project persona sweep (drafts deleted, actives archived, persona_id/builder_draft_id bindings cleared) * feat: persona project scoping frontend (personas v2 P1B) - Persona schema gains projectId (snake_case raw -> camelCase transform) - Settings: scope filter (All | Global | per-project), scope badges with deleted-project fallback, create-form scope select, read-only editor scope - usePersonas(scope) with typed tagged-union DTO matching the backend enum - PersonaPickerControl fetches globalAndProject(current) and renders grouped Global / project sections - PersonaManagementRows extracted to keep the section under the size limit * feat: manual persona draft editing backend, CAS (personas v2 P2A) - update_draft gains optional expected_content_hash CAS, atomic at the SQL layer (UPDATE ... WHERE id = ? AND status = 'draft' AND content_hash = ?), not a check-then-write race; None preserves today's behavior unchanged - new update_persona_draft Tauri command (thin, delegates to the service) - typed PersonaDraftConflict with a stable PERSONA_DRAFT_CONFLICT: prefix the frontend can match on - conflict and active-persona rejections emit no persona:draft_updated event - seeded-draft SOURCE_CHANGED_SINCE_SEED freshness computation unaffected * feat: manual persona draft editing frontend (personas v2 P2B) - drafts fully editable in PersonaEditor (extracted to its own component); read-only draft banner and disabled inputs removed - saves send expectedContentHash (CAS); PERSONA_DRAFT_CONFLICT responses show a distinct conflict banner with Reload draft (refetch + repopulate, discarding stale local edits); other errors keep the generic banner - 'Open builder conversation' link via the persona's sourceSessionId (bound drafts stamp the builder conversation id there; builderDraftId is a conversation-side column, spec text adjusted to reality) - client-side canonical document composition for structured-field edits * feat: composer folder references backend (personas v2 P3A) - M2: conversation_folder_references (soft-delete removed_at, live-cap 5 config-driven, UNIQUE(conversation_id, folder_path) WHERE live) - registration validation: canonicalize, absolute non-root, symlink-root rejection, bidirectional app-data containment check (fail-closed), control-char rejection in display_name; canonical form stored - per-ref revalidation at every read: failures EXCLUDE the ref from prompt and roots with folder_refs_skipped diagnostics (never brick the send); repository errors still abort (adversarial-review fix F1) - composer_folder_references feature flag gates commands, overlay, roots (F2) - <referenced_folders> overlay ordered after persona block on fresh/resume/ recovery/queued paths, both harness lanes, all five XML entities escaped; recovery-retry resume carries refs too (F3) - read roots append live refs for Project conversations only; add command rejects non-Project and persona_builder conversations server-side (F5) - three thin Tauri commands, camelCase DTOs, atomic cap enforcement * feat: composer folder references frontend (personas v2 P3B) - 'Add folder' menu row (flag-gated, hidden for persona_builder and projectless composers), native directory picker - FolderReferenceChips shared by persisted and pre-send draft rows: tooltip = full path, aria-labeled soft-remove, TanStack invalidation - first-paint-deferred hydration of chips (rAF + macrotask boundary) - pre-send folders held draft-locally, registered against the seeded conversation before attachment upload; inline cap-error surfacing - composerFolderReferences flag threaded through feature-flag schema/hook - fixes ridden along per zero-failure rule: stale useFeatureFlags strict assertions, unscoped persona-picker query key in AgentsView test Implemented by codex (partial, usage-limit death) + sonnet continuation. * feat: ChatContextType::Standalone variant + exhaustive site coverage (personas v2 P4A.1) - Standalone variant ('standalone'), self-keyed semantics documented; creation sites fail typed until the creation slice lands - named sites: message-queue dispatch converted to exhaustive FromStr (branch_update silent omission fixed), archive stops via the real context, restart + durable recovery enumerate Standalone, queued-context resolver extended (stale-default bug fixed), provider-pause requeue and agent-waiting eligibility include Standalone - audit fixes beyond the compiler sweep: live-send workspace loading, silent-completion recovery, global-pause launch gating, history injection, execution-halt queue management - CWD/workspace resolution intentionally a typed gap until P4A.2 - carry-forward (reviewed, tracked): resume-path standalone drain MUST land with the creation slice; today's pause layers accept what resume cannot drain Implemented by codex (partial, usage-limit death) + sonnet continuation. * feat: standalone workspace service + CWD/read-root arms (personas v2 P4A.2) - standalone_workspace.rs: idempotent ensure_workspace under app_data_dir/standalone_workspaces/conversation-<sha256[:12]>/, symlink rejection, require_under_root containment, manifest.json - crash-orphan startup sweep: deletes only manifest-identified orphans under the canonical root; repo errors, symlinks, unreadable manifests are preserved (fail-closed), registered in startup jobs - resolve_working_directory: Standalone arm resolves the real workspace; typed error on failure, never default_working_directory - resolve_mcp_filesystem_read_roots: Standalone arm = [workspace root], PersonaBuilder mode arm keeps precedence (D9.3) - enforce_filesystem_roots derivation extended to Standalone context in the single Phase 0 seam Implemented by sonnet (codex accounts exhausted). * feat: standalone conversation creation, start arms, pause-drain parity (personas v2 P4A.3) - standalone_conversations flag (AtomicI8 override pattern); creation gated at create command, projectless start, and seeded start; existing rows operate regardless of flag (operation is not gated) - ChatConversation::new_standalone() self-keyed; invariant enforced at both repo layers (adversarial-review F4) - start arm: project_id Option, chat-mode-only allowlist (explicit, no default-mode leak), seeded-ownership all directions - Team invariant enforced at ALL seams: create arm, seeded coordination check, and the send-path coordination flip now Project-only (F1) - pause-drain parity: non-slot drain matches Standalone; end-to-end test asserts DELIVERY after resume (4a.1 review carry-forward) - session namer: standalone runs use the app-owned workspace, skip typed when unavailable (F2); progress events carry the conversation id (F3) - sidebar: list_by_context_type enumeration + data-driven No-project group - get_first_user_message_by_context wires 'standalone' in both repos Implemented by sonnet (codex outage) + codex fix round. * feat: standalone conversations frontend (personas v2 P4B) - chat-context-registry standalone entry (store-key family standalone:) - useStartAgentConversation standalone-aware: optimistic conversation, seeded creation (contextId omitted), store keys, chat-mode startInput - main region + selection model accept project-less conversations; IntegratedChatPanel.projectId nullable with hook short-circuits (no sentinel ids reach project-scoped queries) - start composer: 'No project' inside the project picker (reachable at zero projects, flag-gated), Base/Team/persona/assist hidden, private workspace caption, requiresProject mode metadata with snap-back to Chat - sidebar No-project pseudo-group ( __no_project__ ) with running/queue state and invalidation mirroring project groups - standaloneConversations feature flag threaded through schema/hook * feat: builder mode lifts + seeded-refine provenance (personas v2 P5A) - persona_builder creatable/startable through the standard pipeline (agent_personas-gated); mode-switch and automation rejections kept; standalone start allowlist += persona_builder (Global builds) - ingest-liveness gate retired (pulled forward from 5.5, spec-sanctioned: conditional retirement unimplementable) — new-pipeline builders can send; dangling bound drafts still fail closed at persona resolution - seeded-refine provenance: source_persona_id (builder-mode-only) with exact scope lock, seeding moved into PersonaService (transactional draft+binding), legacy command delegates - standalone builder drafts stamp GLOBAL scope (context-type match closes the P1-review landmine) - Persona-mode Team rejection at create/start/send in any context - Codex-lane rejection for standalone builders enforced by ONE shared predicate at start, send (provider switches), and queued-resume seams (adversarial-review B1) - seeded starts respect persisted mode locks (B2); queued resume fails closed on conversation-lookup errors (B3) * refactor: pre-P5B consolidation (personas v2 P5-CLEANUP) - neutral path-safety home for require_under_root/filesystem_error; persona_ingest and standalone_workspace both consume it - backend-owned abort_seeded_agent_conversation: never-started guard (messages/runs/session ids), draft-status check, ordered deletions, contained workspace removal; frontend error paths call it (leak fix) - workspace create/resolve split: creation only at conversation creation/start; spawn paths resolve fail-closed (no silent recreate) - ResolvedConversationSpawnContext: one construction for folder blocks, roots, workspace, enforcement across fresh/queued/recovery spawns - overlay delivery decoupled from persona-injection attribution - mechanical splits: start service (directory-backed modules), PersonaService transactional internals, spawn-context extraction; unused revalidate_stored_path unified away - integrated no-ingest refine deny-all fixture (Phase 0 §12 gap) - layering baseline: start-service crate::commands violation removed (274 -> 273 tracked entries) * feat: extractor prompt rewrite, analyze/interview/draft (personas v2 P5C) - both harness prompts restructured per D5: Analyze (inventory context, bounded repo sampling for project builds) -> Interview (ask_user_question, max 3 rounds before first draft) -> Draft & iterate (save early, revise the bound draft) - fs denials framed as absent context (no retries, no probing); ingest-era language removed; SKILL.md output contract and 7-tool surface unchanged * feat: builder workspaces, attachment materialization, D9 root restructure (personas v2 P5B) - private workspace created for every builder conversation (both contexts) - composer attachments materialized as text into the workspace (normalized leaf names, containment at the write sink, server-side UTF-8/no-NUL, idempotent re-sync); binary attachments rejected typed - builder prompts reference workspace paths (fs_read_file) — no inlining; non-builder formatting pinned byte-identical by exact-string snapshot - D9 restructure through ResolvedConversationSpawnContext: legacy ingest precedence, Project/Standalone fall-through, CWD-dedup bypassed for builders (enforcement-equivalent), workspace-less legacy refine stays deny-all incl. folder refs and prompt block (lockstep by construction) - folder refs become live enforced roots for builders in both contexts * feat: scope chooser, deep link, Persona mode UI (personas v2 P5D) - Settings 'Build with Agent' scope chooser (Global flag-gated | project), exact deep-link sequence into the Agents start composer - 'Refine with Agent' inherits the source persona's scope, skips chooser - projectLocked lifted into composer-local state at draft consumption; reset effect gated (survives projects-query identity churn) - Persona build banners (Global / project / refine variants) - Persona mode in start list (flag-gated) + conversation chip label; locked conversations disable every mode-menu option (leak fix) - Team toggle and persona picker hidden in Persona mode; attachments and folder chips available; startInput carries mode/sourcePersonaId/null projectId per the backend contract * feat: versioned persona artifacts backend (personas v2 P6A) - M3: builder_result_persona_id + backfill (non-draft legacy bindings moved and cleared); M4: personas.artifact_id + persona-library bucket + one backfill artifact per persona (atomic BEGIN/COMMIT, INSERT OR IGNORE — re-entrant, partial-failure converges; adversarial-review F2) - ArtifactType::Persona (Rust + TS closed enums + both label maps) - ArtifactMetadata extended so appends stamp {persona_version, created_by} - six-writer chokepoint: every persona content mutation appends to the chain IN the same transaction; append failure aborts the write; plain approve validates + recomputes content_hash from the in-tx read (F3); approve-as-new recompose appends as system; graft-on-apply appends to the source chain with forensic metadata, interim draft rows orphaned intentionally; draft hard-delete/bound-delete/project-sweep/seeded-abort all delete chain rows transactionally (F1) - uniform binding transitions across all three approvals + typed post-approval save rejection; archive keeps the result pointer - get_artifact_version_history += created_by/metadata; persona:draft_updated += artifact_id (additive) * feat: Persona artifact tab (personas v2 P6B) - Persona tab in the artifacts pane, mode-gated to persona_builder (AUTOMATION_TAB precedent; unavailable-reason exhaustiveness kept) - four states from conversation bindings: draft (Approve/Approve-as-new), approved (read-only result + Open in Settings/Refine deep link), archived result (badge, refine disabled), empty - version dropdown with created-by attribution incl. backfill/system; historical versions read-only; skeleton-first hydration - persona:draft_updated.artifact_id drives query invalidation - backend: builder_result_persona_id exposed on the shared AgentConversationResponse (every hydration path) so approved/archived state reconstructs after reload — both serialization directions tested * fix: atomic first-draft binding claim + single enforcement seam (personas v2) - create_bound_draft claims the conversation's builder_draft_id via a conditional UPDATE (NULL draft and result bindings required) — two concurrent first saves can no longer clobber each other; loser gets a typed Conflict mapped to HTTP 409; NotFound distinguished - ResolvedConversationSpawnContext::without_app_state no longer re-derives the filesystem-enforcement flag inline — routed through build_mcp_runtime_context, restoring D9's single derivation seam (pre-cutover audit finding #1) * fix: pre-cutover audit remediation (personas v2) - global-persona Refine gated on standalone_conversations at both Settings and the Persona artifact tab (disabled + tooltip when off) — closes the flag-interaction hazard that would strand a rejected projectless draft after cutover (audit #2) - Settings PersonaEditor gains the UX-1 Version-history affordance, sharing the Persona tab's attributed history components (audit #3) - persona_service_tests and persona_update_approval_tests mechanically split by concern under the 500-line rule; test counts preserved (195 == 195) (audit #5) * feat: legacy builder cutover (personas v2 P7) - Settings routes exclusively through the scope chooser + deep link; PersonaBuilderView, ingest_persona_context, get_persona_builder_ingest_status, create_persona_builder_conversation deleted (write path only — the legacy ingest READ path and D9 fall-through stay, old conversations resumable indefinitely; on-disk addressing now pinned by a literal-hash test) - carried review fixes: app_data_dir threaded (reverse-derivation gone, literal-dirname regression test), materialized-file existence check at prompt render (typed failure, no dangling paths), dead ConversationFolderReferenceUnsupportedMode variant and dormant PersonaRepository::update_content removed (chokepoint closed by construction), stale doc comment fixed - docs: agent-personas.md rewritten for v2, persona-ga-gates.md gates updated for the new flow, honest binary-rejection wording, agent-mcp-tools/agent-type-map deltas - fix round: orphaned ingest-schema imports removed from persona.test.ts (both independent reviews caught it; tsc excludes tests, vitest caught) -3,377/+335 net. All 8 spec phases now landed. * test: post-v1 hardening — D9 tripwire, rollback matrix, flag matrix (personas v2) - check-layering.py gains the filesystem single-derivation invariant: exact-count allowlist of legitimate enforce_filesystem_roots sites; any new derivation/construction site fails the ratchet - induced-failure rollback tests for the remaining artifact writers (update_persona, create_persona_draft, plain approve, approve-as-new incl. recompose) — content/tip/status/bindings unchanged on append failure, completing the six-writer atomicity matrix - flag-matrix coverage across agent_personas x standalone_conversations x composer_folder_references at backend seams + both frontend gating surfaces (tracker documents pre-existing vs new coverage) - ride-along: extractor agent.yaml description de-ingest-ified (stale wording; out of declared scope, kept deliberately) * fix: big-PR checklist merge-gate remediation (personas v2) - F1 HIGH: seed createConversation now sends mode — the US-1 flow (Global build + attached folders) works; masking test rewired through the production seed command; E2E test added - F2 HIGH-coverage: enforcement-flag parity asserted on recovered and queued builder/standalone spawns (fail-open-by-default regression tripwire at the test layer); MCP context wiring pinned - F3 security: Codex extractor spawns get the full apply_patch-disable override set via typed config (was plan-profile-only) — native writes can no longer bypass MCP enforcement on project Codex builds - F4 security: enforced mode ignores ambient RALPHX_FILESYSTEM_READ_ROOTS — zero CLI roots means deny-all, never env inheritance (MCP server rebuilt); the env-leak class D9 closed for the flag now closed for roots - F6: PersonaArtifactPanel keyed by conversation id — no cross- conversation approve/error bleed * fix: big-PR checklist fast-follows (personas v2) - Codex rejected for ALL standalone conversations (chat included) — the D3 permission-prompt boundary assumed Claude; danger-full-access has none (owner-reviewable: one-line revert if the lane should instead be documented as an exception) - builder attachment deletion prunes the materialized workspace copy - abort-refused starts reveal the surviving conversation (sidebar invalidation) instead of silently hiding it - frontend error states distinct from empty: folder-ref list warning (with backend-divergence note + retry), persona picker error row, version-history error rendering - resumed builders fail closed at send when agent_personas is off - LOW batch: sticky remove-disable fixed, fetchQuery rejection handled, message-bearing conversations can't be converted to builder via seeded start, folder-chip double-writer wipe removed, MCP fail-open default param removed (rebuilt), flag-off folder list now typed FeatureDisabled * test: split oversized persona integration suites * test: align plan verification expectations * fix: contain standalone workspace filesystem sinks Intent: Close CodeQL path-injection alerts #384 and #385 without weakening the standalone workspace lifecycle or trusting construction provenance at filesystem sinks. Behavior: - Validate the process-owned app-data path as absolute, non-root, and free of traversal components before creation or lookup. - Canonicalize the app-data and workspaces roots, reject a symlinked workspaces root, and re-check containment before create, metadata, read, and removal operations. - Keep conversation identifiers hash-derived, so absolute and traversal-shaped IDs remain inert path input. - Apply the same guard seam to create, resolve, remove, and startup sweep paths so adjacent operations cannot bypass the boundary. Compatibility and failure semantics: - Preserve creation when the process-owned app-data directory does not exist yet. - Preserve resolve as non-creating with StandaloneWorkspaceMissing for absent roots. - Preserve removal as an idempotent no-op when no root exists. - Keep orphan sweeping fail-closed around unreadable, invalid, or symlinked entries. Verification: Focused TDD captured the missing-root regression before the final mapping fix. The exact standalone workspace suite passes 19 tests covering normal/idempotent creation, absolute and traversal IDs, root and entry symlink escapes, missing-root resolve/remove behavior, and sweep containment. Both touched files pass surgical rustfmt checks and the staged diff passes git diff --check. * test: cover Persona artifact type registration Intent: Keep the ArtifactType registry contract aligned with the Persona artifact introduced by Personas v2, and turn the stale CI count failure into explicit behavioral coverage. Behavior: - Updates the registry cardinality from 23 to 24. - Asserts Persona is present in ArtifactType::all(), so a future omission cannot hide behind a count-only expectation. - Verifies Persona's stable snake_case wire representation in both serialization and deserialization directions. Risk and compatibility: This is a test-only correction; production behavior and persisted wire values are unchanged. Verification: - cargo test --manifest-path src-tauri/Cargo.toml -p ralphx-domain artifact_type_all_includes_persona - rustfmt --edition 2021 --check src-tauri/crates/ralphx-domain/src/entities/artifact/tests.rs - git diff --check * fix: align standalone conversation security across providers Intent Support Personas v2 standalone conversations on both Claude and Codex without weakening RalphX filesystem containment or allowing caller-supplied context to select a more permissive launch policy. Make the persisted conversation the authority for identity, workspace mode, provider launch security, and recorded runtime metadata. Behavior - Launch standalone Chat and PersonaBuilder flows through either Claude or Codex for fresh sends, true resumes, queued continuations, and session recovery. - Resolve the actual context-specific global role default before provider capability validation; explicit provider overrides remain authoritative. - Record the effective Codex approval and sandbox policy on fresh and queued AgentRun rows so audit/UI metadata matches the command that ran. - Persist replacement Codex thread identity after recovery and retain the provider-specific session on subsequent work. - Keep frontend start payloads, canonical MCP grants, generated MCP tests, docs, and multi-harness guidance aligned with the bilateral runtime. Security boundaries - Derive one exhaustive ConversationLaunchSecurityClass from the authoritative persisted context and workspace mode. - Standalone Claude Chat uses the CLI prompt boundary: permission mode default, no dangerous skip flags, native Read/Grep/Glob remain available but are not preapproved, and the permission bridge remains available. - Standalone Codex Chat uses on-request plus workspace-write. Because codex exec is noninteractive, operations requiring a new approval fail closed; Project and standalone PersonaBuilder retain the existing MCP compatibility policy. - Validate explicit conversation identity before provider resolution, pause enqueue, workspace rollover, event emission, stdin delivery, or live-process cleanup, and validate again at the later dispatch seam. - Disable Codex apply_patch whenever the canonical agent disables shell_tool, and assert exact Persona Extractor grants plus General Explorer denials. - Keep filesystem enforcement and private standalone read roots intact across fresh, queue, resume, and recovery paths. Structure and compatibility - Extract provider-specific policy objects from the Claude and Codex command modules while keeping those modules as the canonical CLI construction seams. - Name external CLI protocol values through Rust constants; lowercase values such as default, on-request, and workspace-write intentionally match provider CLI contracts. - Move the pre-existing Claude inline test block to a sibling test module and split provider security coverage into bounded leaves with shared validated fake CLI fixtures. - Preserve configured Project and PersonaBuilder launch behavior, legacy provider settings, explicit overrides, and the existing default-off feature flags. Verification - Focused provider, start, queue, recovery, Gate-1 identity, capability, and prompt-contract tests pass, including true Codex exec resume and real attempt_session_recovery paths. - Frontend AgentsView start coverage: 60/60 passed. - MCP tool authorization/schema coverage: 228/228 passed; package build regenerated checked-in output. - Both Rust Clippy matrices pass with warnings denied. - Layering ratchet, integration module checker, touched-leaf rustfmt checks, and diff checks pass. - PR Rust stack passed layering and IPC 120/120; library lane passed 4,950/4,966 in the sandbox. The 16 blocked tests were all host-resource restrictions and passed 16/16 when rerun with normal worktree, listener, and keychain access. * fix: harden standalone workspace path sinks Intent Close the two outdated CodeQL path-injection review threads with current-tree evidence and bring the adjacent orphan-sweep sinks up to the same sink-local containment standard. Preserve the fail-closed workspace lifecycle: no untrusted or filesystem-derived path may authorize inspection, manifest reads, canonicalization, or deletion outside the app-owned standalone root. Behavior - Keep conversation identifiers hash-derived before they become path components. - Reject symlinks at the derived workspace destination and at manifest.json. - Treat unexpected metadata failures as explicit filesystem errors instead of assuming the path is not a symlink. - Skip orphan-sweep candidates unless the entry and fixed manifest path are proven contained under the canonical standalone root. - Preserve valid live workspaces, unreadable or unprovable entries, and every outside symlink target. Security and CodeQL - Validate containment immediately before symlink_metadata, read_to_string, canonicalize, and remove_dir_all sinks. - Pass the canonical root into manifest parsing so that helper owns its validation instead of relying on caller provenance. - Keep each rust/path-injection suppression immediately adjacent to a sink whose path has already been validated. - Retain post-canonicalization containment as defense in depth before recursive removal. Tests and verification - Add behavioral coverage proving create rejects a symlink at the hash-derived workspace path and preserves its outside target. - Add sweep coverage proving a symlinked manifest cannot authorize deletion and its outside target survives. - Standalone workspace module: 21/21 passed. - Both warnings-denied Clippy matrices passed. - Layering ratchet, integration module checker, leaf rustfmt, and diff checks passed. * fix: make Personas CI validation deterministic Intent: Restore trustworthy PR validation after the final Personas v2 rebase exposed one frontend state race, two environment-dependent Rust assertions, scanned test diagnostics, and repeated hosted-runner termination during the cold integration build. Behavior: - Keep project-backed Edit mode stable while the first available project is selected when standalone conversations are disabled. - Align the visual contract with the shipped Ideation start mode without accepting unrelated snapshot churn. - Exercise durable Project and Standalone recovery through the established paused queue seam, asserting one attempt-marked recovery per context and no provider runtime dispatch. - Align the task-execution rule test with main's focused-validation ownership and timing contract. Security and provider alignment: - Preserve the exact Codex PersonaExtractor MCP-grant predicate while removing runtime-derived override values from assertion diagnostics, closing CodeQL cleartext-logging and log-injection flows. - Keep recovery coverage provider-neutral: neither Claude nor Codex CLI availability can change the result. CI reliability: Cap full-integration compilation at two Cargo build jobs, matching the existing coverage-lane resource pattern. Five unbounded control runs, including a dedicated rerun, received runner shutdown/SIGTERM before any test executed. Validation: Focused Rust regressions 3/3; frontend Vitest 4/4; the four named Playwright visual cases pass against existing baselines; frontend typecheck and lint pass; both Clippy feature matrices pass with warnings denied; leaf rustfmt, layering, integration-module, workflow YAML, shell syntax, and diff checks pass. * fix: align filesystem enforcement ratchet after extraction Intent: Keep the single-derivation security tripwire authoritative after the conversation spawn-context logic was mechanically extracted into its own sibling module. Behavior: The ratchet now expects the two live direct build_mcp_runtime_context derivations in resolved_conversation_spawn_context.rs and no longer expects the obsolete pre-extraction super:: call spelling. Production enforcement behavior is unchanged. Security: The allowlist remains exact-counted and fail-closed: any additional derivation site, missing canonical site, or alternate spelling still fails the layering gate. Validation: python3 scripts/check-layering.py git diff --check * fix: complete bilateral PersonaBuilder alignment Intent: Make the Personas v2 builder and standalone flows provider-neutral and fail closed at one canonical conversation-identity seam, while preserving all rebased main behavior and legacy Claude configuration compatibility. Behavior: Treat PersonaBuilder as valid only for Project or Standalone conversations across creation, attachments, folder roots, draft writers, runtime injection, and recovery. Reject corrupted Task/Ideation builder rows before filesystem, draft, or provider side effects. Persist provider/model/effort and capability intent consistently in frontend starts and continuations for both Claude and Codex. Recovery and failure atomicity: Reload the authoritative conversation during recovery, restore bound draft metadata and effective builder agent without leaking draft content, and apply the same recovery path to Claude and Codex. Delay existing-conversation mode changes until workspace preparation succeeds; restore prior runtime state and remove only newly created private workspaces when persistence or draft seeding fails. Provider alignment and compatibility: Expose shared Personas flags and limits through the provider-neutral agents facade while retaining legacy Claude reexports. Keep Claude native-agent injection suppression Claude-only so Codex continues receiving the backend persona contract. Preserve provider-specific launch security already established by this PR. Security: Extend the canonical builder predicate to every filesystem and six-writer sink. Keep filesystem-root derivation ratcheted, validate authoritative recovery identity before replay, and prove invalid contexts leave draft versions/content and workspace files untouched. Validation: Frontend focused matrix 160/160; IPC guards 2/2; HTTP writer guard 1/1; runtime/recovery guards 4/4; compensation 1/1; domain predicate 1/1; bilateral recovery 1/1 plus adjacent security 2/2. Layering ratchet, integration module check, leaf rustfmt, and diff checks pass. * fix: satisfy bilateral test helper Clippy gate Intent: Keep the shared Claude/Codex continuation fixture warning-free under the required all-targets Clippy configuration. Behavior: Pass the Copy conversation identifier by value instead of cloning it; runtime and test behavior are unchanged. Validation: Both the no-default-features Clippy gate and the all-targets/all-features Clippy gate pass locally with warnings denied; leaf rustfmt and diff checks pass. * test: make Personas queue contracts deterministic Intent: Keep the Personas v2 standalone and queued-builder contracts independent of provider binaries, global probe timing, and asynchronous process-marker races in parallel CI. Behavior: Seed the authoritative completed provider-session owner required by current main before testing a queued builder continuation; wait up to five seconds for the spawned fake CLI capture; provide the private workspace required by the missing-row queue entry; and seed harness availability before the ownership rejection test. Production behavior is unchanged. Why: Current-head CI reproduced three fixture-order failures: provider discovery preempted the intended ownership assertion, a queue helper resolved an intentionally absent workspace, and the queued process capture was read before dispatch completed. Validation: All three failures pass together under the CI nextest profile. All-targets/all-features Clippy passes with warnings denied; layering, suite module registration, leaf rustfmt, and diff checks pass. * fix: separate folder chip tooltip triggers Intent: Make the icon-only folder removal control reliably expose its accessible tooltip in Chromium, WKWebView, and parallel frontend tests. Behavior: Render the folder path trigger and remove-button trigger as sibling Radix tooltips instead of nesting the remove tooltip inside the path tooltip region. The button keeps its folder-specific accessible name and the visible tooltip remains 'Remove folder'. Why: Exact-head CI reproduced a race where hovering the nested remove trigger opened competing tooltip state and the required remove tooltip was absent. The isolated test passed intermittently, confirming structural timing rather than missing copy. Validation: AgentComposerSurface 91/91; frontend TypeScript check; full frontend ESLint; diff check. * fixes CI * test: restore Personas coverage and harden CI diagnostics Intent: Restore meaningful patch coverage after the Personas integration suites were mechanically split, without weakening the 90% Codecov gate or excluding production code. Behavior: Enroll the existing PersonaBuilder attachment and standalone start contracts in the IPC coverage selector using the established persona/ipc_contract naming convention. Add failure-atomicity regressions for binary attachment materialization and seeded refine draft insertion: prior Chat/Solo state and source attachments survive, partial private workspaces and draft bindings are removed, and no agent run is persisted. Provider alignment: Keep the contracts provider-neutral where behavior is shared, retain explicit standalone Codex launch coverage, and prove a failed Codex-selected refine start never invokes the provider. Security: Replace value-printing equality diagnostics for externally derived ClickUp keys with exact value-free predicates and static messages, closing the CodeQL cleartext-log path without weakening assertions. Why this differs from main: Main does not contain the Personas v2 split production seams. On this PR those mechanically extracted files count as new patch lines, while several already-valid integration behaviors were omitted by the coverage job's focused selector. Validation: 13 affected IPC behaviors and the ClickUp persistence test pass; both warnings-denied Clippy matrices, layering ratchet, integration module checker, surgical rustfmt checks, and diff checks are green. * test: close final Personas coverage and CodeQL gaps Intent: Clear the two external checks left after the otherwise-green b86836cd5 run: Codecov patch coverage at 89.64041% and the repeated ClickUp cleartext diagnostic. Coverage behavior: Exercise the PersonaBuilder attachment removal contract through three real production outcomes: a missing private workspace is an idempotent no-op, a missing materialized file is an idempotent no-op, and a symlinked materialized path fails closed while its outside target survives. The test name intentionally participates in the existing persona IPC coverage selector and covers more than the roughly nine executable lines still required. Security behavior: Keep exact ClickUp title, queued-reference, and persisted-link assertions while separating externally derived string comparisons from assertion macros. Assertion diagnostics now receive only booleans and static intent messages. Scope: Test-only change; no production behavior, provider policy, feature flags, or prior Personas work is modified. Both Claude-neutral shared behavior and Codex support remain unchanged. Validation: The focused PersonaBuilder removal integration test and ClickUp persistence lib test pass; both touched Rust leaves pass surgical rustfmt checks and the diff is clean. * test: cover remaining attachment symlink guards Intent: Close the final Codecov patch gap after 946aa3665 raised coverage to 89.85445%, four executable lines below the 90% requirement. Behavior: Extend the existing PersonaBuilder attachment containment contract through both write-side symlink branches. Materialization rejects an existing destination symlink before overwrite and rejects a stored-source symlink before read; both assertions prove the outside target remains unchanged. Coverage direction: The two production guards account for six currently uncovered lines in builder_attachment_materializer.rs, providing a two-line margin over the four-line threshold gap without exclusions, no-op execution, or production changes. Security: The test canonicalizes the app-owned attachment source and verifies containment under canonical storage before its fixture sinks. Sink-local CodeQL annotations document that validation. Validation: The focused suite_ipc_commands regression passes; the touched leaf is rustfmt-clean, remains below the 500-line limit, and the diff check is clean. * test: keep sensitive values out of diagnostics Intent: Clear the last GHAS CodeQL status after all functional, coverage, CodeQL analysis, and repository code-scanning gates passed on aafb23a72. Root cause: CodeQL models Rust assertion macros as diagnostic sinks even when externally derived ClickUp keys are first reduced to booleans. The two key flows therefore remained attached to ticketing_commands/tests.rs:3910. Behavior: Retain exact checks for the conversation title, queued provider-neutral reference, and persisted external link. Wrong values still fail the test, but ordinary if guards now call panic with static text only; no externally derived value reaches a diagnostic argument or format input. Scope: Test-only diagnostic hardening. No production ticketing, Personas, provider, filesystem, or persistence behavior changes. Validation: The focused ClickUp persistence lib test passes; the touched Rust leaf is rustfmt-clean and the diff check is clean. * test: align Personas queues with excerpt references Intent: Repair the single post-main-merge compile failure shared by Rust Integration Archive, Rust IPC Coverage, and all-target Clippy. Root cause: Main #797 added composer_excerpt_references before attachment_ids in MessageQueue::queue_with_runtime_overrides_and_project_references. Five Personas v2 queue fixtures still used the legacy 17-argument call shape. Behavior: Supply an empty excerpt-reference vector in the new slot for the two PersonaBuilder queue cases and three standalone Claude/Codex queue cases. Preserve the existing empty attachment-id vectors and all provider/security assertions; these scenarios intentionally carry no selected excerpts. Provider alignment: Update both Claude and Codex standalone continuation fixtures symmetrically, plus provider-neutral rejection and builder gates. Validation: The exact five affected suite_chat_service tests pass 5/5; focused suite-target Clippy passes with warnings denied; both touched Rust leaves are rustfmt-clean and the diff check is clean. * test: remove sensitive branch data from diagnostics Intent: Clear the final GHAS CodeQL blocker on PR #779 without weakening the ticket-start contract or changing runtime behavior. Root cause: The full Rust SARIF shows both reported sources flow through AgentConversationStartService::start into the returned workspace, then terminate when the ticket-branch assertion formats workspace.branch_name into panic output. The alert is displayed on the next tokio::test attribute because of macro source mapping, which made the earlier ClickUp-key assertions appear responsible. Behavior: Keep the exact ticket-derived branch-prefix assertion but use a static failure message, so a failure cannot disclose the runtime branch. Restore the unrelated ClickUp title, queued-reference, and persisted-link checks to concise matches assertions with static diagnostics. Scope and compatibility: Test-only diagnostic hardening. Claude and Codex runtime behavior, provider selection, ticket persistence, workspace creation, and production error semantics are unchanged. The inherited dynamic diagnostic exists on main, but this PR must remove it because GHAS evaluates the PR merge result. Validation: Both focused ticket-start tests pass, the touched Rust leaf passes rustfmt --check, and git diff --check is clean. * fix: make Personas folder context reachable and reference-aligned Intent: close the remaining gap between the converged Personas v2 plan and the native composer. The live-folder backend was already present, but the default-off capability had no Settings path, so normal installs hid Add folder even while Personas were enabled. The standalone flag was also missing from the Rust UI response, forcing the frontend fallback to false and making projectless conversations and Global Persona builds unreachable. Behavior: expose the effective standaloneConversations value, persist Folder context through the existing feature-flag command, keep all three rollout flags explicitly default-off, and retain Add folder directly beneath Add files. Render persisted and draft folders through the same reference-pill component as Plan, ticket, and artifact context, including a visible Folder badge, full-path hover and keyboard tooltip, and accessible removal. Runtime direction: folder references remain canonical live read-only roots and continue through the shared conversation spawn context used by both Claude and Codex. This commit changes reachability and presentation, not the containment, persistence, prompt-overlay, or provider authorization model. Verification: 202 focused frontend tests; TypeScript; ESLint; two focused Rust feature-flag tests; standalone builder folder-root integration; 11 folder-reference service tests; recovery/retry folder-root test; rustfmt; diff check; layering ratchet; independent static alignment review. * fix: retire legacy Claude RalphX MCP registration safely Intent: restore Claude launches for users upgrading across main's provider-native MCP policy change. Older RalphX releases registered a user-scoped server named ralphx; the new reserved-ID preflight correctly rejects third-party collisions but previously mistook RalphX's own persisted registration for one, blocking every Claude spawn before Persona or folder context could run. Behavior: at the shared MCP launch-policy seam, detect only the complete historical RalphX stdio signature rooted under the current app-data directory, serialize concurrent cleanup attempts, remove that one user-scoped entry through the resolved Claude CLI, rediscover provider-native state, and then run the unchanged fail-closed reserved-ID check. The migration is Claude-only and idempotent. Security: arbitrary or partially matching Claude registrations remain untouched and rejected; ralphx_internal remains reserved; Codex receives no migration bypass and now has a real-file reserved-ID regression test. Unrelated provider-native MCP definitions and metadata remain provider-owned. Why this is not on main: commit 8f1587d9f removed startup registration and introduced collision preflight without retiring entries that earlier RalphX versions had written. The live config reproduced that exact upgrade residue, and the running app successfully removed it through this path. Verification: 10 focused launch-policy tests passed, including exact cleanup, idempotence, mismatch preservation, and bilateral Claude/Codex rejection; rustfmt, diff check, and layering ratchet passed. * fix: scope the legacy MCP cleanup seam to unit tests Rust Clippy builds the library with all features but without cfg(test). The fake-CLI injection method was therefore present and unused under test-utils, and -D warnings rejected the build. Limit the helper to unit-test compilation; production cleanup behavior and integration-facing APIs are unchanged. Verified with the failing CI-equivalent lib Clippy command using all features and -D warnings. * fix: retain folder context in conversation history Folder references were persisted only as live conversation roots, so the agent could read an attached folder while the corresponding user message lost that context in the transcript. Snapshot the currently validated live folder references into user-message metadata at the shared ChatService send seam before immediate persistence or queueing. Preserve an existing snapshot during replay, omit removed or unavailable roots, and keep malformed pre-existing metadata intact. Thread the same display-only snapshot through active, review/merge, question-mode, and seeded-start optimistic messages, then parse and render it with the established message reference pills after reload. The frontend snapshot is never sent as filesystem authority; Claude and Codex still derive read roots only from the repository-backed containment service. Document the immutable history behavior and cover parser/rendering, hydrated sends, seeded sends, review sends, invalid-folder omission, removal semantics, and queue replay stability. Validation: 244 focused frontend tests; 130-test post-cleanup frontend rerun; 3 focused Rust tests; TypeScript; ESLint; rustfmt checks on touched Rust leaf files; layering ratchet; diff checks. * fix: persist persona builder results in conversation Persona Builder could still look like a Markdown generator: its prompts did not make persistence a completion requirement, the result pane stayed closed, and the first draft event was not scoped strongly enough to bind a stale conversation view safely. Require both Claude and Codex builders to save one named persona lineage through save_persona_draft before claiming completion. Plural requests now choose one persona and use separate builder conversations; copy/paste and Settings handoffs are explicitly rejected. Scope agent-save events with the owning builder conversation, consume only matching events in the Persona panel, and auto-open that panel on the Persona tab. The saved draft appears immediately with the existing in-conversation Approve persona action while foreign events remain isolated. Keep the transactional PersonaService writer and explicit approval boundary unchanged. Rebuild the MCP server output, document the direct-save UX, and add focused bilateral, backend, and frontend regressions. * fix: launch persona builder from project conversations Intent: Let users turn a project Agent conversation into a real Persona-building workflow without copying generated Markdown into Settings. The existing persona chip was a binding switcher only, which left ordinary Agent conversations with no route to the transactional builder. Behavior: Add a Create persona for this project action to the active conversation persona menu. The action opens a new Persona Builder starter locked to the current project, while leaving the source conversation and its immutable mode unchanged. Standalone conversations cannot invoke the project action. Architecture: Route the transition through useAgentConversationActions and its existing showStarterComposer owner so focused-project, optimistic selection, persisted selection, and task-history state stay synchronized. Keep save_persona_draft restricted to the dedicated Persona Builder tool surface and preserve the one-conversation/one-persona lineage. Validation: - 107 focused Vitest tests across menu, action owner, panel routing, and main-region wiring - frontend TypeScript check - frontend ESLint - git diff --check * fix: align persona artifacts with plan rendering Intent: - Remove the bespoke in-chat Persona artifact presentation and make Persona documents use the same established versioned Markdown surface as Plan and Review. - Preserve Persona domain identity and lifecycle behavior; this does not replace Plan data, tabs, references, or actions. Behavior: - Loads the canonical Persona artifact by artifact_id and renders its heading, Markdown, overflow actions, version dropdown, historical banner, and Back to latest flow through the shared artifact display. - Renders canonical Persona YAML frontmatter as structured Description, Name, and Kind metadata instead of collapsing it into Markdown prose. - Applies frontmatter parsing to each selected artifact version so historical metadata and body stay consistent. - Uses the canonical compact approval control and suppresses all lifecycle actions while a historical version is open. - Keeps approve-as-new, Settings, and agent refinement as Persona-owned actions. - Uses shared artifact loading and empty states; legacy null-artifact Personas retain a safe version-one display fallback. Architecture and safety: - Exposes VersionedArtifactDisplay as a domain-neutral adapter over the existing PlanDisplay implementation already reused by Review. - Adds narrow content preparation and action composition seams rather than copying Plan markup or version state. - Disables excerpt selection for Persona until persona is supported end to end as a composer reference kind, preventing false Plan references. - Fails closed when a bound artifact id cannot be resolved. Validation: - 76 focused Vitest cases pass across Persona parsing, PersonaArtifactPanel, and PlanDisplay. - Frontend TypeScript typecheck passes. - ESLint passes for every touched frontend source and test file. - git diff --check passes. * fix: preserve tolerant persona editor parsing Intent: - Repair the frontend CI regression introduced when structured Persona artifact parsing was added. Behavior: - Keeps strict name/kind/description parsing for the read-only artifact metadata presentation. - Restores splitPersonaBody's established editor/import contract: any correctly delimited frontmatter block is removed even when it is partial. - Adds a focused regression test for partial frontmatter used by Settings editor flows. Validation: - 103 focused tests pass, including the exact PersonasSection CI failure plus Persona artifact and Plan display suites. - Frontend typecheck passes. - ESLint and diff checks pass. * fix: unify Persona Builder artifact workflows Intent: - make Persona Builder conversations behave as the direct creation and refinement surface instead of sending users through duplicate or generic UI paths - give Persona artifacts the same document and version UX as Plan artifacts everywhere they appear Behavior: - show only the Persona artifact in Persona Builder conversations, ignore stale hidden-tab preferences, and remove tab customization and redundant Refine with Agent actions in that mode - deep-link Open in Settings to the exact draft/active Persona and preserve the originating conversation so Open in Agent returns to that conversation - route linked project and standalone conversations through their real context without borrowing the active project, and close Settings before navigation - enlarge the Markdown instructions editor and widen the version-history dialog Architecture: - reuse VersionedArtifactDisplay and one shared Persona YAML-frontmatter preparation path for both the Agent pane and Settings history - keep artifact-tab policy in AgentsArtifactPane, entity deep-link resolution in PersonasManagementSection, and context routing in the canonical navigation helper - replace the obsolete Settings-only Persona history hooks with canonical current-artifact query keys; manual/agent updates now invalidate the cache used by both hosts Correctness: - deep links wait for an authoritative Persona list, apply once, reject missing/archived targets, and let Back remain on the list - standalone navigation selects a null project and the canonical standalone chat key - historical Persona versions remain read-only and structured YAML metadata is never rendered as collapsed Markdown Validation: - 77 focused Vitest assertions pass across Persona artifacts, Settings, mutation/event invalidation, and project/standalone navigation - focused Persona-only AgentsArtifactPane policy test passes - frontend TypeScript, touched-file ESLint, and git diff checks pass * fix: refresh Persona artifacts after manual saves Intent: Keep an open Persona artifact pane synchronized with the transactional Persona version created by a manual Settings save. Root cause: The update mutations invalidated Persona lists and the returned artifact query, but left the cached Persona detail row untouched. The pane derives its artifact ID from that detail row, so it remained pinned to the prior artifact version even though the backend had created and returned the next version. Behavior: Publish every successful Persona write into the canonical Persona detail cache before refreshing its exact artifact tip. Apply the same centralized handoff to active edits, draft edits, approval, and approve-as-new so all mutation paths preserve one cache contract. Coverage: Extend mutation tests to assert the returned Persona becomes the detail-cache source of truth, and add an open-pane regression proving a manual save moves the rendered artifact from v3 to v4 with updated frontmatter and Markdown. Validation: - focused usePersonas mutation tests - focused PersonaArtifactPanel v3-to-v4 integration test - frontend TypeScript check - touched-file ESLint - git diff --check * fix: isolate Persona artifact state by conversation Intent: Preserve the established conversation boundary after Persona artifacts moved from a bespoke nested scroller onto the shared Plan artifact surface. Root cause: The prior Persona panel owned a keyed overflow container, so switching builder conversations replaced the scroller and discarded local approval state. Plan-aligned rendering removed that nested scroller; the persistent outer artifact pane then leaked the previous Persona conversation's scroll position even though the keyed Persona component still protected its local mutation state. Behavior: Key the shared artifact-content scroller by conversation only in Persona Builder mode. A Persona conversation switch now resets scroll, selection-provider ownership, and the rendered approval subtree. Other artifact modes retain their stable content boundary so in-flight publish dialogs and progress state are not remounted during ordinary updates. Coverage: The existing bidirectional alpha/beta delayed-approval tests again prove a fresh scroll container at position zero and prove a late approval cannot overwrite the newly selected Persona. The publish-progress ownership test confirms the narrowed key does not regress non-Persona workflows. Validation: - all 199 AgentsArtifactPane tests - focused Persona-switch and publish-progress cases - frontend TypeScript check - touched-file ESLint - git diff --check * fix: close persona ownership and metadata gaps Intent: - Address both blocking findings from Bogdan’s PR #779 review. - Restore the one-conversation/one-draft authorization invariant for Persona Builder reads. - Keep structured Persona state and immutable artifact history consistent with canonical Markdown after manual edits. Caller-bound draft reads: - Make get_persona_draft consume X-RalphX-Caller-Session-Id just like save_persona_draft. - Centralize caller header parsing, conversation lookup, and PersonaBuilder Project/Standalone validation in one handler helper shared by GET and SAVE. - Fail closed unless the caller conversation’s builder_draft_id exactly matches the requested draft before any draft lookup occurs. - Cover missing identity, non-builder callers, cross-draft access, and the valid bound-draft path; retain adjacent SAVE authorization coverage. Transactional metadata persistence: - Pass the already-validated ParsedPersona into the existing update_content_with_artifact chokepoint. - Atomically write parsed name and description together with content, content hash, version, and timestamp before appending the new immutable artifact. - Preserve CAS rejection and status guards; no new writer or parallel update path is introduced. - Prove draft and active manual updates return and reload canonical structured fields and append artifact tips with the updated title, content, version, attribution, and persona metadata. Validation: - suite_http_handlers get_persona_draft_ filter: 4 passed - adjacent SAVE caller-validation filter: 3 passed - suite_ipc_commands draft/active persona update filters: 2 passed - rustfmt --edition 2021 --check on all four touched Rust leaf files - git diff --cached --check
…mination Adds two focused lib tests that prove observed_repair_push_receipt_for_head walks push_branch ordinal keys (#2..#N) when the base key is terminated (Failed + completed_at). Previously every fixture seeded the receipt under the base key, so a regression to a base-key-only lookup would silently disable the entire escape hatch with the suite green.
…1021) * chore: commit in-progress workspace changes to unblock base merge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: pin ordinal push-receipt resolution in terminated PR update termination Adds two focused lib tests that prove observed_repair_push_receipt_for_head walks push_branch ordinal keys (#2..#N) when the base key is terminated (Failed + completed_at). Previously every fixture seeded the receipt under the base key, so a regression to a base-key-only lookup would silently disable the entire escape hatch with the suite green. * test: pin reconciler's ordinal update_pr resolution after base key termination Adds a missing test for Blueprint Step 2c: reconcile_blocked_agent_workspace_repair_pr_handoff now resolves update_pr via resolve_repair_effect_identity rather than a base-key-only lookup. Without this test a revert to base-key resolution would silently retire the no-op recovery path with no failing test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: lazabogdan <6580668+lazabogdan@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The
docs/user-guides/directory had a merge pipeline guide but no guide for the Ideation Studio — the entry point of the entire feature development journey.New file:
docs/user-guides/ideation-studio.mdCovers the full idea → merged code pipeline, modelled on the style of
merge.md:Cmd+Shift+Pquick switcher, lifecycle state tablemerge.mdfor full merge details)main → plan/feature → task branches)team_constraintsYAML structure, keyboard shortcutsOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.