# How to Debug LoopX Applications: A Complete Guide to Troubleshooting the AI-Agent Orchestration Framework

> Debug LoopX applications effectively. Learn to inspect state, query runtime health, trace errors via the CLI, registry, and state refresh, and diagnose sync failures with projection_enabled.

- Repository: [huangruiteng/loopx](https://github.com/huangruiteng/loopx)
- Tags: how-to-guide
- Published: 2026-08-15

---

**To debug LoopX applications, use dry-run flags to inspect state changes without mutation, query the status server for runtime health, trace errors through the CLI → registry → state refresh pipeline, and examine the `projection_enabled` flag to diagnose sync failures.**

LoopX is a modular **AI-agent orchestration framework** built around a runtime directory, global registry, and CLI commands that manipulate goal state. Debugging effectively requires understanding how these components interact—from argument parsing in the CLI to markdown manipulation in state files. This guide shows you exactly how to trace problems through the LoopX codebase according to the underlying source implementation.

## Understanding the LoopX Architecture for Debugging

The framework consists of layered components. Knowing which file handles what responsibility lets you isolate bugs quickly.

### Core Components and Responsibilities

| Component | Key Source File | Primary Responsibility |
|-----------|---------------|------------------------|
| **Runtime manager** | [`loopx/runtime.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/runtime.py) | Resolves active runtime root, validates goal IDs, archives completed goals |
| **State refresh engine** | [`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) | Parses goal markdown, updates *Next Action* sections, builds refresh records |
| **State projection** | [`loopx/state_projection.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_projection.py) | Detects gaps between public-safe representation and active state |
| **Global registry** | [`loopx/registry.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/registry.py) | Stores goal metadata, resolves state-file locations, lists registered agents |
| **Status server** | [`loopx/status_server.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/status_server.py) | Exposes HTTP endpoint reporting runtime and registry health |
| **CLI front-ends** | [`loopx/cli.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/cli.py) | Thin wrappers invoking services with argument validation |

### The Debugging Pipeline: Five Layers to Trace

When something fails, walk through these layers in order:

1. **CLI invocation** — Check arguments like `--dry-run` and `--goal-id`. Early validation functions `validate_goal_id_path_segment` and `validate_public_safe_text` catch malformed input immediately.

2. **Runtime path resolution** — The runtime root derives from `resolve_runtime_root` in the registry. Missing goal directories usually indicate stale [`registry.json`](https://github.com/huangruiteng/loopx/blob/main/registry.json) entries.

3. **State file parsing** — Functions `parse_frontmatter`, `extract_section_lines`, and `replace_next_action_section` manipulate markdown. Errors here stem from malformed frontmatter or missing `## Next Action` headings.

4. **Projection and repair** — `state_projection_gap_warning` flags todo-expansion gaps. Seeing `requires_todo_expansion` in output means investigating the *Agent Todo* section.

5. **Status server verification** — The HTTP `/status` endpoint mirrors `loopx status` CLI output. Query it with `curl` for JSON snapshots without re-invoking the refresh pipeline.

## Essential Debugging Commands and Techniques

### Using Dry-Run Mode to Preview Changes

The `--dry-run` flag is your most powerful debugging tool. It executes logic without file mutations, letting you inspect every decision.

```bash

# Preview a state refresh without writing files

loopx refresh-state \
    --registry /path/to/registry.json \
    --goal-id my-goal \
    --dry-run \
    --classification "state_refreshed"

```

Inspect the generated markdown in `/tmp/loopx/run-<timestamp>/refresh-state.md` to verify classification logic and *Next Action* generation.

### Querying the Status Server for Runtime Health

The status server avoids the overhead of full refresh cycles when you need quick health checks.

```bash

# Start the server (default port 8000)

loopx status-server &

# Get JSON snapshot of runtime, registry, and projection route

curl http://localhost:8000/status | jq .

```

The response includes `runtime_projection_route.projection_enabled`—critical for diagnosing why global registry sync may be disabled.

### Archiving Goals Safely

Always dry-run before executing destructive operations:

```bash

# Verify before archiving

loopx archive-goal \
    --registry /path/to/registry.json \
    --goal-id my-goal \
    --dry-run

# Execute once verified

loopx archive-goal \
    --registry /path/to/registry.json \
    --goal-id my-goal \
    --execute

```

## Common Debugging Scenarios and Solutions

### Unexpected Classification Results

**Symptom:** Goal classification doesn't match expectations.

**Resolution:** Run `loopx refresh-state --dry-run` and examine the generated markdown for the `classification` field. The dry-run bypasses state mutation, exposing the logic that determines classifications in [`state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/state_refresh.py).

### Goal Directory Not Found

**Symptom:** `FileNotFoundError` on `runtime/goals/<goal-id>`.

**Resolution:** Verify registry entries with `registry_goals` and runtime root resolution via `resolve_runtime_root`. Use `loopx status --verbose` to print resolved paths. The runtime manager raises this error only after failing to locate the directory.

### Next Action Section Not Updating

**Symptom:** State refresh completes but *Next Action* remains unchanged.

**Resolution:** Check the return value of `replace_next_action_section`. This function returns a tuple `(new_text, updated_flag)`. If `updated_flag` is `False`, the section already matches the desired action. When `dry_run=True`, the function returns updated text but does not persist it.

### Persistent Projection Gaps

**Symptom:** Warning continues after attempted fixes.

**Resolution:** Examine the warning object from `state_projection_gap_warning`. It contains fields `requires_todo_expansion`, `user_open`, and `agent_open`. The projection logic only collapses gaps after parsing todos cleanly—fix malformed bullet lines or missing todos in the state file.

### Global Registry Sync Failures

**Symptom:** Changes don't propagate to shared runtime projection.

**Resolution:** Inspect `runtime_projection_route` via `compact_runtime_projection_route`. If `status` is `"missing"` or `"single_runtime"`, global sync is intentionally disabled by design, not by error.

### Performance Bottlenecks

**Symptom:** Slow refresh operations.

**Resolution:** Enable the status server and monitor `runtime_projection_route.projection_enabled`. Use `loopx status --metrics` for timing statistics. The server aggregates metrics without re-invoking the full refresh pipeline.

## Source File Reference for Deep Debugging

| File | Purpose | Key Functions |
|------|---------|---------------|
| [`loopx/runtime.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/runtime.py) | Goal validation and archiving | `archive_runtime_goal`, `resolve_runtime_root` |
| [`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) | Core refresh routine | `refresh_state_run`, `parse_frontmatter`, `replace_next_action_section` |
| [`loopx/state_projection.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_projection.py) | Gap detection | `state_projection_gap_warning` |
| [`loopx/registry.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/registry.py) | Metadata and path resolution | `registry_goals`, `load_registry` |
| [`loopx/status_server.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/status_server.py) | HTTP health endpoint | `main` |
| [`loopx/cli.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/cli.py) | Command entry points | Argument parsing and service invocation |
| [`loopx/paths.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/paths.py) | Directory location helpers | Runtime and archive path utilities |
| [`loopx/feedback.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/feedback.py) | Text validation | `validate_goal_id_path_segment`, `validate_public_safe_text` |

## Summary

- **Start with `--dry-run`** to inspect logic without side effects when debugging LoopX applications
- **Trace errors through five layers:** CLI → runtime resolution → state parsing → projection → status server
- **Query `localhost:8000/status`** for lightweight runtime health checks without pipeline re-execution
- **Check `projection_enabled`** and `runtime_projection_route.status` to diagnose sync failures
- **Monitor `replace_next_action_section` return values** to verify whether text actually changed

## Frequently Asked Questions

### What is the fastest way to check if LoopX is running correctly?

Start the status server with `loopx status-server &` and query `curl http://localhost:8000/status`. This returns a JSON snapshot including runtime health, registry state, and projection route status without triggering the expensive refresh pipeline.

### Why does my goal's Next Action never update?

The `replace_next_action_section` function returns `(new_text, updated_flag)`. If `updated_flag` is `False`, the desired action already matches the current section content. Use `--dry-run` to see the proposed text, or check that your `--next-action` argument differs from the existing heading.

### How do I debug classification decisions in LoopX?

Run `loopx refresh-state --dry-run --classification <value>` and inspect `/tmp/loopx/run-*/refresh-state.md`. The classification field in the generated markdown reveals exactly how the state refresh engine categorized your goal based on the current state file contents.

### What causes global registry sync to fail silently?

Check `runtime_projection_route.status` via the status server or `loopx status --verbose`. Values of `"missing"` or `"single_runtime"` indicate intentional disablement of global sync, not an error. The `compact_runtime_projection_route` logic in the source code determines this based on runtime configuration.