From e7c84cda93b4c0838bd7f76d2e95bb3fea72b7a4 Mon Sep 17 00:00:00 2001 From: Jorge Alexander Taberoa Jimenez Date: Sun, 19 Apr 2026 18:43:42 -0600 Subject: [PATCH 1/7] feat(loopforge-app): create crate scaffold with dependencies --- Cargo.toml | 1 + crates/loopforge-app/Cargo.toml | 14 ++++++++++++++ crates/loopforge-app/src/errors.rs | 7 +++++++ crates/loopforge-app/src/lib.rs | 4 ++++ crates/loopforge-app/src/models.rs | 0 crates/loopforge-app/src/ports.rs | 0 crates/loopforge-app/src/services.rs | 0 7 files changed, 26 insertions(+) create mode 100644 crates/loopforge-app/Cargo.toml create mode 100644 crates/loopforge-app/src/errors.rs create mode 100644 crates/loopforge-app/src/lib.rs create mode 100644 crates/loopforge-app/src/models.rs create mode 100644 crates/loopforge-app/src/ports.rs create mode 100644 crates/loopforge-app/src/services.rs diff --git a/Cargo.toml b/Cargo.toml index ab32a14..42cc2d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "crates/app-services", + "crates/loopforge-app", "crates/loopforge-app-core", "crates/ralph-core", "src-tauri", diff --git a/crates/loopforge-app/Cargo.toml b/crates/loopforge-app/Cargo.toml new file mode 100644 index 0000000..a186c97 --- /dev/null +++ b/crates/loopforge-app/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "loopforge-app" +version = "0.1.0" +edition = "2021" +description = "Application layer for LoopForge hexagonal architecture" + +[dependencies] +ralph-core = { path = "../ralph-core" } +async-trait = "0.1" +serde = { version = "1", features = ["derive"] } +thiserror = "2" +tokio = { version = "1", features = ["rt-multi-thread", "sync", "macros"] } +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1", features = ["v4", "serde"] } diff --git a/crates/loopforge-app/src/errors.rs b/crates/loopforge-app/src/errors.rs new file mode 100644 index 0000000..4813eed --- /dev/null +++ b/crates/loopforge-app/src/errors.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum AppError { + #[error("Not implemented")] + NotImplemented, +} diff --git a/crates/loopforge-app/src/lib.rs b/crates/loopforge-app/src/lib.rs new file mode 100644 index 0000000..d9f07e1 --- /dev/null +++ b/crates/loopforge-app/src/lib.rs @@ -0,0 +1,4 @@ +pub mod errors; +pub mod models; +pub mod ports; +pub mod services; diff --git a/crates/loopforge-app/src/models.rs b/crates/loopforge-app/src/models.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-app/src/ports.rs b/crates/loopforge-app/src/ports.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-app/src/services.rs b/crates/loopforge-app/src/services.rs new file mode 100644 index 0000000..e69de29 From 261a61870594ba9263d1e69465d9478a75a803ec Mon Sep 17 00:00:00 2001 From: Jorge Alexander Taberoa Jimenez Date: Sun, 19 Apr 2026 18:45:11 -0600 Subject: [PATCH 2/7] feat(adapters): create loopforge-adapters crate scaffold --- Cargo.toml | 1 + crates/loopforge-adapters/Cargo.toml | 10 ++++++++++ crates/loopforge-adapters/src/channel_events.rs | 0 crates/loopforge-adapters/src/filesystem/mod.rs | 0 crates/loopforge-adapters/src/inmemory/mod.rs | 0 crates/loopforge-adapters/src/lib.rs | 5 +++++ crates/loopforge-adapters/src/paths.rs | 0 crates/loopforge-adapters/src/sqlite/mod.rs | 0 8 files changed, 16 insertions(+) create mode 100644 crates/loopforge-adapters/Cargo.toml create mode 100644 crates/loopforge-adapters/src/channel_events.rs create mode 100644 crates/loopforge-adapters/src/filesystem/mod.rs create mode 100644 crates/loopforge-adapters/src/inmemory/mod.rs create mode 100644 crates/loopforge-adapters/src/lib.rs create mode 100644 crates/loopforge-adapters/src/paths.rs create mode 100644 crates/loopforge-adapters/src/sqlite/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 42cc2d7..1fa650d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "crates/app-services", + "crates/loopforge-adapters", "crates/loopforge-app", "crates/loopforge-app-core", "crates/ralph-core", diff --git a/crates/loopforge-adapters/Cargo.toml b/crates/loopforge-adapters/Cargo.toml new file mode 100644 index 0000000..89cf989 --- /dev/null +++ b/crates/loopforge-adapters/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "loopforge-adapters" +version = "0.1.0" +edition = "2021" + +[dependencies] +rusqlite = { version = "0.32", features = ["bundled"] } +tokio = { version = "1", features = ["full"] } +thiserror = "1" +serde = { version = "1", features = ["derive"] } diff --git a/crates/loopforge-adapters/src/channel_events.rs b/crates/loopforge-adapters/src/channel_events.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-adapters/src/filesystem/mod.rs b/crates/loopforge-adapters/src/filesystem/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-adapters/src/inmemory/mod.rs b/crates/loopforge-adapters/src/inmemory/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-adapters/src/lib.rs b/crates/loopforge-adapters/src/lib.rs new file mode 100644 index 0000000..72eb8b6 --- /dev/null +++ b/crates/loopforge-adapters/src/lib.rs @@ -0,0 +1,5 @@ +pub mod channel_events; +pub mod filesystem; +pub mod inmemory; +pub mod paths; +pub mod sqlite; diff --git a/crates/loopforge-adapters/src/paths.rs b/crates/loopforge-adapters/src/paths.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-adapters/src/sqlite/mod.rs b/crates/loopforge-adapters/src/sqlite/mod.rs new file mode 100644 index 0000000..e69de29 From fb46fa6bf6fc1612495b737d087da08905b70247 Mon Sep 17 00:00:00 2001 From: Jorge Alexander Taberoa Jimenez Date: Sun, 19 Apr 2026 18:46:20 -0600 Subject: [PATCH 3/7] feat(api): create loopforge-api crate scaffold --- Cargo.toml | 1 + crates/loopforge-api/Cargo.toml | 9 +++++++++ crates/loopforge-api/src/events.rs | 0 crates/loopforge-api/src/lib.rs | 2 ++ crates/loopforge-api/src/methods.rs | 0 5 files changed, 12 insertions(+) create mode 100644 crates/loopforge-api/Cargo.toml create mode 100644 crates/loopforge-api/src/events.rs create mode 100644 crates/loopforge-api/src/lib.rs create mode 100644 crates/loopforge-api/src/methods.rs diff --git a/Cargo.toml b/Cargo.toml index 1fa650d..2ac99c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "crates/app-services", "crates/loopforge-adapters", + "crates/loopforge-api", "crates/loopforge-app", "crates/loopforge-app-core", "crates/ralph-core", diff --git a/crates/loopforge-api/Cargo.toml b/crates/loopforge-api/Cargo.toml new file mode 100644 index 0000000..1028214 --- /dev/null +++ b/crates/loopforge-api/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "loopforge-api" +version = "0.1.0" +edition = "2021" +description = "API contract definitions for LoopForge methods and events" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/crates/loopforge-api/src/events.rs b/crates/loopforge-api/src/events.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/loopforge-api/src/lib.rs b/crates/loopforge-api/src/lib.rs new file mode 100644 index 0000000..4174cb7 --- /dev/null +++ b/crates/loopforge-api/src/lib.rs @@ -0,0 +1,2 @@ +pub mod events; +pub mod methods; diff --git a/crates/loopforge-api/src/methods.rs b/crates/loopforge-api/src/methods.rs new file mode 100644 index 0000000..e69de29 From 78077f2137af16d1161b091c89adc60bfe00bb03 Mon Sep 17 00:00:00 2001 From: Jorge Alexander Taberoa Jimenez Date: Sun, 19 Apr 2026 18:47:37 -0600 Subject: [PATCH 4/7] feat(loopforge-app): define application error types --- crates/loopforge-app/src/errors.rs | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/crates/loopforge-app/src/errors.rs b/crates/loopforge-app/src/errors.rs index 4813eed..e8244c7 100644 --- a/crates/loopforge-app/src/errors.rs +++ b/crates/loopforge-app/src/errors.rs @@ -1,7 +1,50 @@ +use std::path::PathBuf; use thiserror::Error; +use uuid::Uuid; + +pub type AppResult = Result; #[derive(Debug, Error)] pub enum AppError { - #[error("Not implemented")] - NotImplemented, + #[error("Project not found: {0}")] + ProjectNotFound(Uuid), + + #[error("Session not found: {0}")] + SessionNotFound(Uuid), + + #[error("Artifact error for '{path}': {message}")] + ArtifactError { path: PathBuf, message: String }, + + #[error("Storage error: {0}")] + StorageError(String), + + #[error("Validation error: {0}")] + ValidationError(String), + + #[error("Service error in '{service}': {message}")] + ServiceError { service: String, message: String }, +} + +impl AppError { + pub fn artifact(path: impl Into, message: impl Into) -> Self { + Self::ArtifactError { + path: path.into(), + message: message.into(), + } + } + + pub fn storage(message: impl Into) -> Self { + Self::StorageError(message.into()) + } + + pub fn validation(message: impl Into) -> Self { + Self::ValidationError(message.into()) + } + + pub fn service(service: impl Into, message: impl Into) -> Self { + Self::ServiceError { + service: service.into(), + message: message.into(), + } + } } From 894aed63c37cbda21b3a4202e037f351069ca865 Mon Sep 17 00:00:00 2001 From: Jorge Alexander Taberoa Jimenez Date: Sun, 19 Apr 2026 19:06:13 -0600 Subject: [PATCH 5/7] feat(decoupling): scaffold backend separation and fix cursor plan output Add initial decoupling scaffolding across app services, adapters, and docs while restoring runtime compatibility updates. Remove cursor plan mode for plan execution to avoid empty output responses and keep planning flow reliable. --- .claude/commands/opsx/apply.md | 152 +++ .claude/commands/opsx/archive.md | 157 +++ .claude/commands/opsx/explore.md | 173 +++ .claude/commands/opsx/propose.md | 106 ++ .claude/skills/openspec-apply-change/SKILL.md | 156 +++ .../skills/openspec-archive-change/SKILL.md | 114 ++ .claude/skills/openspec-explore/SKILL.md | 288 +++++ .claude/skills/openspec-propose/SKILL.md | 110 ++ .codex/skills/openspec-apply-change/SKILL.md | 156 +++ .../skills/openspec-archive-change/SKILL.md | 114 ++ .codex/skills/openspec-explore/SKILL.md | 288 +++++ .codex/skills/openspec-propose/SKILL.md | 110 ++ .cursor/agents/clean-rebuild.md | 68 ++ .cursor/commands/opsx-apply.md | 152 +++ .cursor/commands/opsx-archive.md | 157 +++ .cursor/commands/opsx-explore.md | 173 +++ .cursor/commands/opsx-propose.md | 106 ++ .cursor/mcp.json | 25 + .cursor/skills/openspec-apply-change/SKILL.md | 156 +++ .../skills/openspec-archive-change/SKILL.md | 114 ++ .cursor/skills/openspec-explore/SKILL.md | 288 +++++ .cursor/skills/openspec-propose/SKILL.md | 110 ++ .gemini/commands/opsx/apply.toml | 149 +++ .gemini/commands/opsx/archive.toml | 154 +++ .gemini/commands/opsx/explore.toml | 170 +++ .gemini/commands/opsx/propose.toml | 103 ++ .gemini/skills/openspec-apply-change/SKILL.md | 156 +++ .../skills/openspec-archive-change/SKILL.md | 114 ++ .gemini/skills/openspec-explore/SKILL.md | 288 +++++ .gemini/skills/openspec-propose/SKILL.md | 110 ++ .opencode/command/opsx-apply.md | 149 +++ .opencode/command/opsx-archive.md | 154 +++ .opencode/command/opsx-explore.md | 170 +++ .opencode/command/opsx-propose.md | 103 ++ .../skills/openspec-apply-change/SKILL.md | 156 +++ .../skills/openspec-archive-change/SKILL.md | 114 ++ .opencode/skills/openspec-explore/SKILL.md | 288 +++++ .opencode/skills/openspec-propose/SKILL.md | 110 ++ Cargo.lock | 45 + crates/app-services/src/events/ask.rs | 0 crates/app-services/src/events/loop_events.rs | 0 crates/app-services/src/events/mod.rs | 12 + crates/app-services/src/events/plan.rs | 0 crates/app-services/src/lib.rs | 1 + docs/BACKEND_DECOUPLING_IMPLEMENTATION.md | 1034 +++++++++++++++++ docs/BACKEND_DECOUPLING_MASTER_PLAN.md | 769 ++++++++++++ docs/BACKEND_DECOUPLING_PLAN.md | 885 ++++++++++++++ src-tauri/src/adapters/mod.rs | 3 + src-tauri/src/adapters/paths_tauri.rs | 28 + src-tauri/src/agent_runtime_env.rs | 7 + src-tauri/src/lib.rs | 1 + src-tauri/src/plan_engine/args.rs | 2 - src-tauri/src/projects/artifacts.rs | 7 +- src-tauri/src/storage/artifacts.rs | 7 +- 54 files changed, 8556 insertions(+), 6 deletions(-) create mode 100644 .claude/commands/opsx/apply.md create mode 100644 .claude/commands/opsx/archive.md create mode 100644 .claude/commands/opsx/explore.md create mode 100644 .claude/commands/opsx/propose.md create mode 100644 .claude/skills/openspec-apply-change/SKILL.md create mode 100644 .claude/skills/openspec-archive-change/SKILL.md create mode 100644 .claude/skills/openspec-explore/SKILL.md create mode 100644 .claude/skills/openspec-propose/SKILL.md create mode 100644 .codex/skills/openspec-apply-change/SKILL.md create mode 100644 .codex/skills/openspec-archive-change/SKILL.md create mode 100644 .codex/skills/openspec-explore/SKILL.md create mode 100644 .codex/skills/openspec-propose/SKILL.md create mode 100644 .cursor/agents/clean-rebuild.md create mode 100644 .cursor/commands/opsx-apply.md create mode 100644 .cursor/commands/opsx-archive.md create mode 100644 .cursor/commands/opsx-explore.md create mode 100644 .cursor/commands/opsx-propose.md create mode 100644 .cursor/mcp.json create mode 100644 .cursor/skills/openspec-apply-change/SKILL.md create mode 100644 .cursor/skills/openspec-archive-change/SKILL.md create mode 100644 .cursor/skills/openspec-explore/SKILL.md create mode 100644 .cursor/skills/openspec-propose/SKILL.md create mode 100644 .gemini/commands/opsx/apply.toml create mode 100644 .gemini/commands/opsx/archive.toml create mode 100644 .gemini/commands/opsx/explore.toml create mode 100644 .gemini/commands/opsx/propose.toml create mode 100644 .gemini/skills/openspec-apply-change/SKILL.md create mode 100644 .gemini/skills/openspec-archive-change/SKILL.md create mode 100644 .gemini/skills/openspec-explore/SKILL.md create mode 100644 .gemini/skills/openspec-propose/SKILL.md create mode 100644 .opencode/command/opsx-apply.md create mode 100644 .opencode/command/opsx-archive.md create mode 100644 .opencode/command/opsx-explore.md create mode 100644 .opencode/command/opsx-propose.md create mode 100644 .opencode/skills/openspec-apply-change/SKILL.md create mode 100644 .opencode/skills/openspec-archive-change/SKILL.md create mode 100644 .opencode/skills/openspec-explore/SKILL.md create mode 100644 .opencode/skills/openspec-propose/SKILL.md create mode 100644 crates/app-services/src/events/ask.rs create mode 100644 crates/app-services/src/events/loop_events.rs create mode 100644 crates/app-services/src/events/mod.rs create mode 100644 crates/app-services/src/events/plan.rs create mode 100644 docs/BACKEND_DECOUPLING_IMPLEMENTATION.md create mode 100644 docs/BACKEND_DECOUPLING_MASTER_PLAN.md create mode 100644 docs/BACKEND_DECOUPLING_PLAN.md create mode 100644 src-tauri/src/adapters/mod.rs create mode 100644 src-tauri/src/adapters/paths_tauri.rs diff --git a/.claude/commands/opsx/apply.md b/.claude/commands/opsx/apply.md new file mode 100644 index 0000000..bf23721 --- /dev/null +++ b/.claude/commands/opsx/apply.md @@ -0,0 +1,152 @@ +--- +name: "OPSX: Apply" +description: Implement tasks from an OpenSpec change (Experimental) +category: Workflow +tags: [workflow, artifacts, experimental] +--- + +Implement tasks from an OpenSpec change. + +**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. + +**Steps** + +1. **Select the change** + + If a name is provided, use it. Otherwise: + - Infer from conversation context if the user mentioned a change + - Auto-select if only one active change exists + - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select + + Always announce: "Using change: " and how to override (e.g., `/opsx:apply `). + +2. **Check status to understand the schema** + ```bash + openspec status --change "" --json + ``` + Parse the JSON to understand: + - `schemaName`: The workflow being used (e.g., "spec-driven") + - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others) + +3. **Get apply instructions** + + ```bash + openspec instructions apply --change "" --json + ``` + + This returns: + - Context file paths (varies by schema) + - Progress (total, complete, remaining) + - Task list with status + - Dynamic instruction based on current state + + **Handle states:** + - If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` + - If `state: "all_done"`: congratulate, suggest archive + - Otherwise: proceed to implementation + +4. **Read context files** + + Read the files listed in `contextFiles` from the apply instructions output. + The files depend on the schema being used: + - **spec-driven**: proposal, specs, design, tasks + - Other schemas: follow the contextFiles from CLI output + +5. **Show current progress** + + Display: + - Schema being used + - Progress: "N/M tasks complete" + - Remaining tasks overview + - Dynamic instruction from CLI + +6. **Implement tasks (loop until done or blocked)** + + For each pending task: + - Show which task is being worked on + - Make the code changes required + - Keep changes minimal and focused + - Mark task complete in the tasks file: `- [ ]` → `- [x]` + - Continue to next task + + **Pause if:** + - Task is unclear → ask for clarification + - Implementation reveals a design issue → suggest updating artifacts + - Error or blocker encountered → report and wait for guidance + - User interrupts + +7. **On completion or pause, show status** + + Display: + - Tasks completed this session + - Overall progress: "N/M tasks complete" + - If all done: suggest archive + - If paused: explain why and wait for guidance + +**Output During Implementation** + +``` +## Implementing: (schema: ) + +Working on task 3/7: +[...implementation happening...] +✓ Task complete + +Working on task 4/7: +[...implementation happening...] +✓ Task complete +``` + +**Output On Completion** + +``` +## Implementation Complete + +**Change:** +**Schema:** +**Progress:** 7/7 tasks complete ✓ + +### Completed This Session +- [x] Task 1 +- [x] Task 2 +... + +All tasks complete! You can archive this change with `/opsx:archive`. +``` + +**Output On Pause (Issue Encountered)** + +``` +## Implementation Paused + +**Change:** +**Schema:** +**Progress:** 4/7 tasks complete + +### Issue Encountered + + +**Options:** +1.