Skip to content

docs: add Ideation Studio user guide - #2

Merged
lazabogdan merged 4 commits into
mainfrom
copilot/continue-user-guide-ideation-studio
Feb 21, 2026
Merged

docs: add Ideation Studio user guide#2
lazabogdan merged 4 commits into
mainfrom
copilot/continue-user-guide-ideation-studio

Conversation

Copilot AI commented Feb 21, 2026

Copy link
Copy Markdown

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.md

Covers the full idea → merged code pipeline, modelled on the style of merge.md:

  • Session modes — Solo / Research Team / Debate Team with token cost estimates and when to use each
  • Orchestrator workflow — All phases: RECOVER (Phase 0) + 6 active phases (UNDERSTAND → EXPLORE → PLAN → CONFIRM → PROPOSE → FINALIZE), with Solo vs Team differences called out per phase
  • Team Activity panel — Live teammate status, user-to-teammate messaging flow, per-teammate token cost breakdown
  • Plan artifact — Contents, team research summary table, Debate mode side-by-side advocate comparison (with narrow-viewport stacked card fallback)
  • CONFIRM gate — Hard guarantee the orchestrator never creates proposals before explicit approval; how to edit/delete/add proposals
  • Accept → tasks — What happens on accept: task creation, dependency edges preserved, Active Plan auto-set, plan-merge task creation and when it fires
  • Active Plan — Inline selector, Cmd+Shift+P quick switcher, lifecycle state table
  • Downstream journey — Execution pipeline, review cycle, and merge pipeline (links to merge.md for full merge details)
  • End-to-end ASCII flow diagram + 3-level plan branch hierarchy (main → plan/feature → task branches)
  • Troubleshooting — Stuck EXPLORE, unanswered agent questions, missing proposals, reopening accepted sessions, team resume after crash, Active Plan persistence
  • Configuration reference — Session-level settings, team_constraints YAML structure, keyboard shortcuts
Original prompt

Continue create the next user guide in docs/user-guides as we have the merge pipeline for the ideation studio this time, being the start of the journey for an end to end feature development.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

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>
Copilot AI changed the title [WIP] Add user guide for ideation studio merge pipeline docs: add Ideation Studio user guide Feb 21, 2026
Copilot AI requested a review from lazabogdan February 21, 2026 16:17
@lazabogdan
lazabogdan requested review from Copilot and removed request for lazabogdan February 21, 2026 16:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@lazabogdan
lazabogdan marked this pull request as ready for review February 21, 2026 17:31
@lazabogdan
lazabogdan requested a review from Copilot February 21, 2026 18:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +87 to +92
| 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) |

Copilot AI Feb 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
| 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.

Copilot uses AI. Check for mistakes.
Comment thread docs/user-guides/ideation-studio.md Outdated
Comment thread docs/user-guides/ideation-studio.md Outdated
lazabogdan and others added 2 commits February 21, 2026 20:20
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@lazabogdan
lazabogdan merged commit 0d75bb1 into main Feb 21, 2026
lazabogdan added a commit that referenced this pull request Mar 31, 2026
reefagent pushed a commit that referenced this pull request Jun 26, 2026
…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.
adriandemian added a commit that referenced this pull request Jun 26, 2026
…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
reefagent pushed a commit that referenced this pull request Jul 17, 2026
- 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)
reefagent pushed a commit that referenced this pull request Jul 18, 2026
- 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)
reefagent pushed a commit that referenced this pull request Jul 19, 2026
- 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)
adriandemian added a commit that referenced this pull request Jul 20, 2026
…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
lazabogdan pushed a commit that referenced this pull request Jul 20, 2026
…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
lazabogdan added a commit that referenced this pull request Aug 11, 2026
…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.
lazabogdan added a commit that referenced this pull request Aug 12, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants