Skip to content

[Windows 11] Follow-up for v0.7.2: OpenCode GOA composer regex mismatch, watchdog stall flood on reported tasks, and worker tool scoping #51

Open

Description

@sicknewbie

[Windows 11] Follow-up for v0.7.2: OpenCode GOA composer regex mismatch, watchdog stall flood on reported tasks, and worker tool scoping

First off, thank you for the super fast turnaround on #49 and the credit in v0.7.2! The daemon ping-pong fix, old session check, and blank chat fallback are working great.

Following up from our live test session on Windows 11 with OpenCode as GOA (and worker sessions running under OpenCode with Gemini Flash / DeepSeek), here are three factual issues with code traces, live screen evidence, and telemetry from our running daemon:


1. OpenCode GOA auto-wake failure on Windows (core/agent/prompts.go)

Observed Behavior

When worker sessions finish their turn and invoke goa report --outcome done "...", the report is accepted by the daemon and appended to ~/.enowx-app/goa-tell.json. However, OpenCode GOA never wakes up to process it, leaving reports trapped in the file indefinitely.

Root Cause Trace

In daemon/goa_inject.go:130-133, goaCanType guards line injection into idle sessions:

func goaCanType(sess *session.Session, now time.Time) bool {
    info := sess.Info()
    return !info.Exited && info.State != proto.StateWorking && info.State != proto.StateWaiting &&
        sess.TypedFor(now) >= goaTypedQuiet && agent.PromptEmpty(sess.Terminal().Text())
}

OpenCode is an agent with no native background hooks, so it relies entirely on path 4 ("typed in, but only into a composer sure to be empty").

On Windows, reading OpenCode's idle terminal buffer (enowx read <goa-session-id>) reveals the exact screen lines:

Baris 15:      ▣  Goa · wz/deepseek-v4.1-flash · 2m 10s
Baris 16: 
Baris 17:   ┃
Baris 18:   ┃
Baris 19:   ┃ ... ctrl+p commands • OpenCode 1.18.35

Notice lines 17–18: OpenCode renders an input box border with two leading spaces: " ┃".

When evaluated against core/agent/prompts.go:

  1. emptyPlaceholder fails because it expects Claude/Codex placeholders (Ask Codex..., Type your message..., Try "...") and requires prompt glyphs [❯›>]. OpenCode uses Ask anything… without glyphs.
  2. Fallback promptAlone fails:
    var promptAlone = regexp.MustCompile(`^[│|┃]?\s*[❯›>$%#]\s*[│|┃]?$`)
    promptAlone strictly requires at least one of [❯›>$%#]. In OpenCode on Windows, the idle box contains only " ┃" with no prompt glyph and 2 leading spaces.

Because PromptEmpty returns false, goaCanType evaluates to false. In daemon/goa_inject.go:111, lines are continually pushed back into s.goa.tell, keeping GOA permanently silent.

Suggested Fix

Update regexes in core/agent/prompts.go to match OpenCode's empty input border and placeholder:

var promptAlone = regexp.MustCompile(`^\s*[│|┃]?\s*[❯›>$%#]?\s*[│|┃]?$`)
var emptyPlaceholder = regexp.MustCompile(`^\s*[│|┃]?\s*([❯›>]\s*)?(Ask anything.*|Ask Codex to do anything|Type your message or @path/to/file|Try "[^"]*")\s*[│|┃]?$`)

2. Cascading Watchdog stall flood on tasks that already reported done (daemon/goa_stall.go)

Observed Behavior

~/.enowx-app/goa-tell.json maxed out at its 200-line capacity (goaTellMax), with 156+ duplicate stall warnings generated for the exact same task (t64ea68d8). The daemon log flooded with:

level=WARN msg="goa: too much waits for GOA, the oldest goes" lines=1

This warning loop evicted genuine worker completion reports from the buffer.

Root Cause

In daemon/goa_stall.go:

case l.Info.State == "" || l.Info.State == proto.StateDone:
    if l.Still >= idle {
        return "idle: its agent isn't working on it", idle
    }

The watchdog assumes "a turn ending moves the board on". However, because GOA never received the completion report due to Issue 1, the task remained on goa-board.json with "status": "working", even though the worker had already finished and set "outcome": "done".

stallCase does not inspect task.Outcome. It repeatedly flags the completed worker session as stalled every few minutes.

Suggested Fix

In daemon/goa_stall.go, skip or deduplicate stall notifications if task.Outcome != "" or if an unsettled turn report for that task is already pending.


3. Field Data & Feedback: OpenCode optimization & Worker MCP tool scoping

From extended multi-agent testing on Windows 11:

  1. OpenCode SQLite database bloat:
    After intensive multi-turn runs, ~/.local/share/opencode/opencode.db grew to 4.85 GB on disk. Performing reads or transcript operations on a database this size creates noticeable disk I/O lag on Windows. Consider adding a vacuum/compaction check or recommending DB maintenance in docs.

  2. Restricting MCP tool schema for Worker sessions (Mitigating Role Confusion):
    In worker sessions, enowX currently exposes the full suite of orchestrator MCP tools (enowx_session_send, enowx_session_read, enowx_session_spawn, etc.).
    When running smaller, cost-effective models (e.g. Gemini 2.5/3.8 Flash, DeepSeek V3) in worker sessions, exposing supervisor tools causes acute Role Confusion. Workers mistakenly assume they are the supervisor:

    Sesi 9e2fe58d5ce1 sedang membaca cameras.json... Saya pantau manual sampai selesai dan saya laporkan hasilnya...
    

    The worker then ends its turn prematurely without executing any file edits. Restricting worker sessions to file/execution tools (read, edit, write, bash, browser) prevents this hallucination and makes lightweight models significantly more reliable in worker sessions.


4. Sleep/Resume 7-hour RPC context deadline hang

In app.log and enowxd.log after the machine resumed from overnight sleep:

2026-11-07T08:18:39+07:00 rpc.slow method=cloud.request took=6h55m40.338s sentBytes=72 gotBytes=0 err="cloud.request: cloud: Post \"https://cloud.enowx.ai/v1/teams/.../presence\": context deadline exceeded"

The presence RPC had no active TCP keepalive or sleep-aware cancellation, hanging for ~6h 55m and triggering a 1.18s GDI thread lag upon wake (app-lag-20261107-081841.txt). A transport-level timeout on HTTP clients would resolve this cleanly.

Activity

  1. sicknewbie commented on Nov 7, 2026

    @sicknewbie
    Author

    Update & Empirical Field Trace: OpenCode GOA Session Collision & Revert to build Agent (Agent not found: "goa")

    Following up with another reproducible finding observed during sustained testing when running OpenCode for both GOD of Agents and worker sessions on Windows 11 (OpenCode 1.18.35):


    1. Symptom

    While in the GOD of Agents tab:

    1. The terminal prompt mode unexpectedly switches from Goa to Build · <model> (e.g. Build · ag/gemini-3.8-flash 9router), despite the working directory remaining ~/.enowx-app/goa and the enowX tab header displaying GOD of Agents.
    2. When the user or daemon submits a prompt to the session, OpenCode renders a red error box:
      Failed to send prompt
      Agent not found: "goa". Available agents: build, explore, general, plan
      

    2. Root Cause Evidence (From opencode.log and opencode.db)

    A. Session ID Collision in Runtime Log

    Inspecting ~/.local/share/opencode/log/opencode.log reveals two concurrent runner processes colliding on the exact same session ID (ses_e517d0fb0ffe4fHiDZvKRZ0v8x):

    timestamp=2026-11-07T03:10:05.919Z level=INFO run=1ca5157d message=stream providerID=9router modelID=ag/gemini-3.8-flash session.id=ses_e517d0fb0ffe4fHiDZvKRZ0v8x agent=build mode=primary
    timestamp=2026-11-07T03:10:16.361Z level=INFO run=7eedb956 message=stream providerID=weizerouter modelID=wz/deepseek-v4.1-flash session.id=ses_e517d0fb0ffe4fHiDZvKRZ0v8x agent=goa mode=primary
    timestamp=2026-11-07T03:10:30.056Z level=ERROR run=1ca5157d message=failed ref=err_3f6fda60 error="UnknownError: UnknownError" cause="...SessionPrompt.run ... SessionHttpApi.prompt"
    timestamp=2026-11-07T03:12:45.643Z level=ERROR run=7eedb956 message=process session.id=ses_e517d0fb0ffe4fHiDZvKRZ0v8x error=Aborted
    

    run=1ca5157d (worker process running standard agent=build) and run=7eedb956 (GOA process running agent=goa) both latched onto ses_e517d0fb0ffe4fHiDZvKRZ0v8x. Because SQLite operations are shared across OpenCode processes, the GOA process aborted and the session record was overwritten.

    B. Persistent State Overwrite in SQLite

    Querying ~/.local/share/opencode/opencode.db for the affected session:

    SELECT id, directory, agent, model FROM session WHERE id = 'ses_e517d0fb0ffe4fHiDZvKRZ0v8x';

    Output:

    • directory: C:/Users/.../.enowx-app/goa
    • agent: 'build' (overwritten from 'goa')
    • model: {"id":"ag/gemini-3.8-flash","providerID":"9router"}

    When OpenCode resumes via opencode --continue, it restores the agent from the SQLite session table (build), ignoring "default_agent": "goa" from OPENCODE_CONFIG_CONTENT. Under build mode, only built-in agents (build, explore, general, plan) exist in OpenCode's registry, so any prompt routed to goa triggers Agent not found: "goa".


    3. Code Trace in enowX

    1. Premise regarding OpenCode 1.x server/isolation:
      In core/agent/opencode_service.go:3-12:

      // OpenCode 2 runs one background server per user... Its --standalone
      // gives the session a private server... 1.x has no server, and
      // doesn't start with that flag.
      func OpenCodeArgs(command string) []string {
          if OpenCodeMajor(command) < 2 {
              return nil
          }
          return []string{"--standalone"}
      }

      OpenCode 1.18.x does have an internal background server (SessionHttpApi) and shares a single opencode.db file across all running instances on the user account. Without explicit session or port isolation, concurrent OpenCode sessions can step on each other's session IDs.

    2. Missing --agent flag on resume:
      In core/agent/presets.go:43, OpenCode continue is defined purely as:

      Continue: []string{"--continue"},

      Because it relies entirely on "default_agent": "goa" in OPENCODE_CONFIG_CONTENT without an explicit --agent goa CLI argument, OpenCode defers to the agent persisted in SQLite whenever --continue is used.


    4. Suggested Fix

    1. Pass --agent goa explicitly on CLI for GOA sessions:
      When spawning or resuming OpenCode GOA (daemon/create.go & daemon/persist.go), append --agent goa to the command line:

      opencode --continue --agent goa
      

      OpenCode 1.18.35 supports --agent <name> on the CLI. Passing it explicitly forces the active session to bind to goa regardless of prior SQLite state.

    2. Pass explicit --session <id> on session launch:
      Providing --session <unique-session-id> when starting each OpenCode session ensures GOA and worker sessions never bind to or overwrite each other's records in opencode.db.

  2. sicknewbie commented on Nov 7, 2026

    @sicknewbie
    Author

    Field Report & Deep-Dive (Part 3): Session Crossover via opencode --continue Template, Ghost Process PTY Resize Glitch, and Memory Exhaustion

    Following up on our earlier trace on session collisions and SQLite contention, we conducted an empirical audit on enowX v0.7.2 after experiencing a complete MemoryExhaustion crash, session cross-contamination, and rapid viewport flickering on Windows 11.

    Here are the concrete findings, binary traces, and log data:


    1. Session Crossover on Startup: opencode --continue Template Overrides --session <id>

    Observed Behavior

    After restarting or closing and reopening enowX with multiple sessions active, tab contents, chat transcripts, and canvases get cross-linked and scrambled across tabs (e.g. the GOA tab renders the Canvas Style 2 terminal, or worker tabs hijack each other's prompts).

    Root Cause & Evidence
    1. Binary Hardcoding:
      Inspecting compiled strings inside enowX.exe and enowx-daemon.exe (daemon/36a940ad363f/enowx-daemon.exe) reveals the --continue flag hardcoded into the default resume template for OpenCode:
      b'...Extra high/effort %s--continueOpenClaudeOpenCode...'
      
    2. Configuration Overwrite in ~/.enowx-app/sessions.json:
      When an OpenCode session is first created, command properly binds to the session ID:
      "command": [
        "powershell.exe", ...,
        "$PSNativeCommandArgumentPassing = 'Legacy'; & 'opencode' '--session' 'ses_e53adf1daffeEKjuDahs3mOHsF'"
      ]
      However, enowX populates the restart array with the generic template:
      "restart": [
        "powershell.exe", ...,
        "$PSNativeCommandArgumentPassing = 'Legacy'; & 'opencode' '--continue'"
      ]
    3. The Race Condition:
      On application startup, enowX iterates over all active sessions in sessions.json and executes restart. Every active OpenCode tab executes opencode --continue at the exact same moment. In OpenCode CLI, --continue resolves to the most recent session in that directory. As a result, multiple tabs race to attach to the same latest session row in SQLite, corrupting the UI state and scrambling tab transcripts.
    Suggested Fix

    In daemon/persist.go (and wherever the restart command is generated), preserve the explicit session ID from command:

    & 'opencode' '--session' '<ses_id>'

    Never default OpenCode sessions to a bare --continue when an explicit session ID is already assigned.


    2. Viewport & Terminal "Resize Ping-Pong" Glitch: Missing Single-Instance Mutex on Windows

    Observed Behavior

    The application window, sidebar, and terminal viewport rapidly expand and shrink in a continuous jitter/flicker loop.

    Root Cause & Evidence
    1. Dual GUI Processes Running:
      Checking running processes revealed two independent enowX.exe instances running concurrently under the same user session:
      • PID 52468 (launched earlier in the background)
      • PID 56304 (launched when the user reopened the app from the taskbar)
    2. Shared Socket Contention:
      Both GUI instances held open WebSocket/RPC connections to the same local daemon socket (~/.enowx-app/enowxd.sock):
      2026-11-07T22:34:53+07:00 conn.open conn=d45e84e8fb5e kind=app client=enowx/0.7.2
      2026-11-07T22:46:32+07:00 conn.open conn=3f25947514bd kind=app client=enowx/0.7.2
      
    3. PTY Resize Loop:
      Both windows render the same Ghostty/xterm terminal instance. Window A calculates its viewport (e.g. cols=120, rows=40) and sends a PTY resize RPC to the daemon. Window B receives the broadcasted resize, recalculates its bounding box (e.g. cols=118, rows=40 due to subtle DPI/padding differences), and sends a counter-resize RPC back.
      The daemon continuously bounces resize events between both windows, creating an infinite resize loop. Terminating PID 52468 immediately stopped the glitch.
    Suggested Fix

    Add a single-instance mutex check (CreateMutexW / named pipe check) on Windows startup. If an instance of enowX.exe is already running, activate and bring the existing window to the foreground instead of spawning a new GUI process.


    3. Unbounded Concurrency in GOA Triggers Windows MemoryExhaustion

    Observed Behavior

    Windows ran out of virtual memory / swap (MemoryExhaustion), forcefully terminating sockets and background daemons (wsarecv: An existing connection was forcibly closed by the remote host).

    Root Cause & Evidence
    1. Unthrottled Worker Fan-Out:
      GOA spawned 5 worker sessions concurrently (Matrix, Routes, Login/Register, Corridor, Popup rail).
      Upon relaunch, enowX started 11 parallel opencode.exe processes (~700 MB to 1.3 GB RAM each), 10 enowx-daemon.exe instances, and 9 separate git worktrees under ~/.enowx-app/worktrees/CCTVKU-0fc9d9c5/.
      Total RAM usage exceeded 14.5 GB of 16 GB physical RAM, completely exhausting the pagefile and crashing enowxd.sock.
    2. Monolithic SQLite Lock & Payload Bloat:
      All 11 instances concurrently read and wrote to a single 4.98 GB opencode.db (568 sessions, 23,793 messages, 97,045 parts).
      The daemon log shows RPC latencies spiking due to lock contention:
      rpc.slow method=canvas.get took=341ms sentBytes=21 gotBytes=31880816 err=""
      
      Payloads reaching ~31 MB per snapshot exacerbated memory pressure.
    Suggested Fix
    • Worker Pool / Throttling: Enforce a strict concurrency limit in GOA (e.g. max 1–2 concurrent active workers). Subsequent worker delegations should remain queued until active workers complete and exit.
    • Relaunch Guard: On application startup, do not blindly restart all non-exited worker sessions simultaneously. Prompt the user or restore them in a hibernated state.
  3. sicknewbie commented on Nov 8, 2026

    @sicknewbie
    Author

    Field Report & Deep-Dive (Part 4): GOA Lifecycle on Windows — Agent-Switching UI Mismatch & Worker Notification Stalls

    Following up on sustained testing with two interrelated findings observed during GOD of Agents (GOA) runtime operations on Windows 11 (enowX v0.7.2):


    Issue A: Agent-Switching UI/RPC Mismatch & False-Positive Rollback ("Claude Code didn't take")

    1. Symptom

    In the GUI modal "Settings of GOD of Agents":

    1. The user selects Claude Code (or another installed agent differing from the currently active one).
    2. The UI displays the informational note:

      "It restarts on a new conversation: the one it is on doesn't carry over to another agent."

    3. The user clicks "Restart GOA".
    4. The switch fails almost immediately with a red alert banner, reverting GOA back to the original agent:
      Claude Code didn't take (its agent ended right after starting: — enowX restarted this session —): GOD of Agents is back as it was
      

    This creates a false impression that the target agent binary (claude.exe) crashed or failed to start, when in fact the CLI is fully authenticated and healthy.

    2. Empirical Evidence & Diagnostic Trace

    • Daemon Log Trace (~/.enowx-app/enowxd.log):
      time=2026-11-08T09:24:25.077+07:00 level=INFO msg="session ended" id=b60df4832327 agent=claude code=0 why=""
      time=2026-11-08T09:24:25.543+07:00 level=WARN msg="goa: a change didn't take, going back" session=b60df4832327 to="Claude Code" err="its agent ended right after starting: "
      time=2026-11-08T09:24:25.551+07:00 level=INFO msg="session ended" id=b60df4832327 agent=opencode code=0 why=""
      
      agent=claude exited cleanly with code=0 (NOT a crash). The exit occurred within the 5-second goaSwitchSettle window, triggering automatic rollback.
    • App Log Trace (~/.enowx-app/app.log):
      "To start it on a new conversation instead: goa restart --new-conversation"
      
    • CLI Parity Verification:
      Running the exact transition via CLI succeeds instantly in 1 turn:
      enowX.exe goa switch --agent claude --new-conversation --force
      Output:
      GOD of Agents restarted: claude on the official config (limited), in its agent's default permission mode; a new conversation: the one it is on doesn't carry over
      
      Verified via enowX.exe goa switch --check: session b60df4832327 immediately attached to claude-opus-5-5 · medium in state idle.

    3. Root Cause Analysis

    1. UI/RPC Payload Mismatch: While the GUI modal states "It restarts on a new conversation", the UI action handler (goaSwitchRun / goaSettingsDialog) fails to pass newConversation: true to the daemon RPC. The daemon attempts to resume the previous conversation ID across incompatible agent runtimes (opencode -> claude), which cleanly aborts with code=0.
    2. Missing --force Handling When GOA Has Active Tasks: Per docs/goa, when GOA has worker sessions running or queued, the daemon requires --force to interrupt. The GUI dialog lacks a confirmation prompt (e.g. "GOA has active tasks. Force restart?") and does not pass the force flag, triggering an automatic rollback.
    3. Misleading Error Messaging: The error banner attributes the rollback to the agent ending unexpectedly, obscuring the actual cause (incomplete parameter set in the UI call).

    Issue B: Worker Report Delivery Stall on Windows (goa-tell.json Buffer Accumulation)

    1. Symptom

    After worker sessions finish their delegation and execute enowx goa report --outcome done "...", GOD of Agents does not proactively notify the user or settle tasks. GOA remains completely silent until the user manually submits an arbitrary message into GOA's chat terminal, at which point GOA immediately wakes up, acknowledges the backlog, and reports the results.

    2. Empirical Evidence

    • ~/.enowx-app/goa-tell.json Backlog:
      Inspecting goa-tell.json revealed dozens of unconsumed event lines accumulating over hours, reaching 92+ KB in file size:
      [
        {"text":"[enowX GOA] Task t6d06a8c8 \"...\" reports done: ...", "task":"t6d06a8c8"}
      ]
    • Agent Behavior Across Runtimes:
      This stall occurs on both Claude Code and OpenCode. When queried about the delay upon manual wake-up, the agent confirms:

      "Worker finished at 10:04, but the notification line ([enowX GOA]) never appeared in this conversation. I do not run in the background; I only process when a message is sent or when enowX types the notification line into this session."

    3. Root Cause Analysis

    • Neither Claude Code nor OpenCode runs as an autonomous background daemon; both operate as interactive CLI processes inside a PTY, waiting on stdin.
    • The daemon relies on simulated keystroke injection (goa_told_watch.go / goaTellLine) to type lines from goa-tell.json into the GOA terminal once the session is deemed idle.
    • On Windows, ConPTY input injection frequently fails to deliver keystrokes when the terminal window/tab lacks active foreground focus or when the idle-detection heuristic fails.
    • As soon as the user types an interactive prompt, the PTY buffer flushes, waking the agent and finally delivering the pending notification backlog.

    Suggestions for the Development Team

    1. Automate --new-conversation in the GUI:
      In the "Settings of GOD of Agents" modal handler, whenever targetAgent !== currentAgent, automatically ensure newConversation: true is included in the RPC payload sent to goa.switch.
    2. Add an Active-Worker Confirmation Modal:
      If GOA has active tasks/workers, display a confirmation prompt ("GOA has active tasks. Force restart?") and pass force: true upon user confirmation.
    3. Harden Windows ConPTY Event Injection for goa-tell.json:
      Rather than relying purely on simulated keystroke injection into a quiet PTY, consider triggering an explicit PTY wake-up / carriage return, or providing a dedicated local event-stream/hook pipe to deliver worker lifecycle updates to GOA reliably on Windows.
    4. End-to-End (E2E) Test Suite for Integration Buttons:
      We strongly suggest adding automated end-to-end integration tests for modal action buttons (specifically agent and config switching: opencode <-> claude <-> codex <-> claude-platform) to verify that GUI actions achieve parity with CLI executions without requiring manual terminal intervention.
  4. enowdev commented on Nov 9, 2026

    @enowdev
    Owner

    Thanks for the detailed traces. Where each point stands on main:

    1. OpenCode composer: confirmed. OpenCode GOA has no wake hook, and PromptEmpty doesn't know OpenCode's empty composer, so lines wait. Wait for GOA's agent to start before a switch fails #53 fixes the Claude Code side (the rule joined to the composer), not OpenCode. We'll add OpenCode's composer from real screens, and look at an OpenCode plugin that wakes GOA when it goes idle.
    2. Stall flood: confirmed. Stall lines repeat with no limit per task and ignore a reported outcome. We'll keep one per task and stop flagging tasks that already reported.
    3. Worker tools: agreed. Workers will start without the orchestration tools (spawn, send, read) while keeping goa report. We won't touch OpenCode's database ourselves; see [Feature Request] UX & Storage enhancements: Canvas Pick Element inspector and Clean Cache storage breakdown with DB compaction #52.
    4. Cloud presence: the call has a 20 s timeout and it did fire. The long time is the machine sleeping with the request in flight, not a hang.

    From your comments: OpenCode resuming with --continue instead of its session ID is real, and we'll resume by ID and pass --agent goa for GOA. The Settings switch does send newConversation after asking. The "didn't take" error was PowerShell starting the agent late, fixed in #53. If it still happens once that is released, the daemon log lines around the switch would help.

    The fixes above are being worked on now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions