Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

Description

@pseudoseed

Goal

Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
render-gate profile produces a builder you can spawn but can never message.

Fork-only. We are not sending this upstream.

Two defects, both must be fixed

1. Launch is broken — the prompt is passed as a project path

A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
"Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
positional argument — the claude/codex convention:

opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
prompt falls through to codev's generic positional form.

But opencode's positional slot is a project path, not a prompt:

opencode [project] start opencode tui [default]
opencode run [message..] run opencode with a message
Positionals:
project path to start opencode in [string]
Options:
--prompt

Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
against the installed opencode version rather than assuming the flag name — check opencode --help
first and record the version you tested.

2. No render-gate profile

packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
afx send to an opencode builder is held forever with reason no-profile.

A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

  • markerPattern — the composer's prompt glyph at the input row start
  • regionEndPatterns — the line(s) that bound the composer region below

These must be MEASURED from a real captured screen, never guessed. The file's own warning:
REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
patterns from those captures. Include the captures in the PR.

Acceptance

  1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
  2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
  3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
  4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
    instead of producing a silently unmessageable builder.
  5. Captured frames used to derive the profile are committed.

Notes

  • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
    the way agy got its own for a different marker glyph and color rule.
  • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
    strict app-identity table so an unknown app never inherits claude's (wrong) profile.

Activity

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

    area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

      Description

      @pseudoseed

      Goal

      Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
      prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
      render-gate profile produces a builder you can spawn but can never message.

      Fork-only. We are not sending this upstream.

      Two defects, both must be fixed

      1. Launch is broken — the prompt is passed as a project path

      A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
      "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

      Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
      positional argument — the claude/codex convention:

      opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

      OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
      file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
      prompt falls through to codev's generic positional form.

      But opencode's positional slot is a project path, not a prompt:

      opencode [project] start opencode tui [default]
      opencode run [message..] run opencode with a message
      Positionals:
      project path to start opencode in [string]
      Options:
      --prompt
      

      Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
      against the installed opencode version rather than assuming the flag name — check opencode --help
      first and record the version you tested.

      2. No render-gate profile

      packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
      afx send to an opencode builder is held forever with reason no-profile.

      A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

      • markerPattern — the composer's prompt glyph at the input row start
      • regionEndPatterns — the line(s) that bound the composer region below

      These must be MEASURED from a real captured screen, never guessed. The file's own warning:
      REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
      injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
      live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
      patterns from those captures. Include the captures in the PR.

      Acceptance

      1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
      2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
      3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
      4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
        instead of producing a silently unmessageable builder.
      5. Captured frames used to derive the profile are committed.

      Notes

      • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
        the way agy got its own for a different marker glyph and color rule.
      • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
        strict app-identity table so an unknown app never inherits claude's (wrong) profile.

      Activity

      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

        area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

          Description

          @pseudoseed

          Goal

          Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
          prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
          render-gate profile produces a builder you can spawn but can never message.

          Fork-only. We are not sending this upstream.

          Two defects, both must be fixed

          1. Launch is broken — the prompt is passed as a project path

          A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
          "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

          Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
          positional argument — the claude/codex convention:

          opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

          OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
          file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
          prompt falls through to codev's generic positional form.

          But opencode's positional slot is a project path, not a prompt:

          opencode [project] start opencode tui [default]
          opencode run [message..] run opencode with a message
          Positionals:
          project path to start opencode in [string]
          Options:
          --prompt
          

          Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
          against the installed opencode version rather than assuming the flag name — check opencode --help
          first and record the version you tested.

          2. No render-gate profile

          packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
          afx send to an opencode builder is held forever with reason no-profile.

          A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

          • markerPattern — the composer's prompt glyph at the input row start
          • regionEndPatterns — the line(s) that bound the composer region below

          These must be MEASURED from a real captured screen, never guessed. The file's own warning:
          REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
          injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
          live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
          patterns from those captures. Include the captures in the PR.

          Acceptance

          1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
          2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
          3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
          4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
            instead of producing a silently unmessageable builder.
          5. Captured frames used to derive the profile are committed.

          Notes

          • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
            the way agy got its own for a different marker glyph and color rule.
          • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
            strict app-identity table so an unknown app never inherits claude's (wrong) profile.

          Activity

          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

            area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

              Description

              @pseudoseed

              Goal

              Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
              prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
              render-gate profile produces a builder you can spawn but can never message.

              Fork-only. We are not sending this upstream.

              Two defects, both must be fixed

              1. Launch is broken — the prompt is passed as a project path

              A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
              "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

              Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
              positional argument — the claude/codex convention:

              opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

              OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
              file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
              prompt falls through to codev's generic positional form.

              But opencode's positional slot is a project path, not a prompt:

              opencode [project] start opencode tui [default]
              opencode run [message..] run opencode with a message
              Positionals:
              project path to start opencode in [string]
              Options:
              --prompt
              

              Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
              against the installed opencode version rather than assuming the flag name — check opencode --help
              first and record the version you tested.

              2. No render-gate profile

              packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
              afx send to an opencode builder is held forever with reason no-profile.

              A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

              • markerPattern — the composer's prompt glyph at the input row start
              • regionEndPatterns — the line(s) that bound the composer region below

              These must be MEASURED from a real captured screen, never guessed. The file's own warning:
              REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
              injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
              live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
              patterns from those captures. Include the captures in the PR.

              Acceptance

              1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
              2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
              3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
              4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
                instead of producing a silently unmessageable builder.
              5. Captured frames used to derive the profile are committed.

              Notes

              • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
                the way agy got its own for a different marker glyph and color rule.
              • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
                strict app-identity table so an unknown app never inherits claude's (wrong) profile.

              Activity

              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

                area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

                  Description

                  @pseudoseed

                  Goal

                  Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
                  prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
                  render-gate profile produces a builder you can spawn but can never message.

                  Fork-only. We are not sending this upstream.

                  Two defects, both must be fixed

                  1. Launch is broken — the prompt is passed as a project path

                  A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
                  "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

                  Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
                  positional argument — the claude/codex convention:

                  opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

                  OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
                  file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
                  prompt falls through to codev's generic positional form.

                  But opencode's positional slot is a project path, not a prompt:

                  opencode [project] start opencode tui [default]
                  opencode run [message..] run opencode with a message
                  Positionals:
                  project path to start opencode in [string]
                  Options:
                  --prompt
                  

                  Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
                  against the installed opencode version rather than assuming the flag name — check opencode --help
                  first and record the version you tested.

                  2. No render-gate profile

                  packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
                  afx send to an opencode builder is held forever with reason no-profile.

                  A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

                  • markerPattern — the composer's prompt glyph at the input row start
                  • regionEndPatterns — the line(s) that bound the composer region below

                  These must be MEASURED from a real captured screen, never guessed. The file's own warning:
                  REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
                  injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
                  live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
                  patterns from those captures. Include the captures in the PR.

                  Acceptance

                  1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
                  2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
                  3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
                  4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
                    instead of producing a silently unmessageable builder.
                  5. Captured frames used to derive the profile are committed.

                  Notes

                  • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
                    the way agy got its own for a different marker glyph and color rule.
                  • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
                    strict app-identity table so an unknown app never inherits claude's (wrong) profile.

                  Activity

                  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

                    area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

                      Description

                      @pseudoseed

                      Goal

                      Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
                      prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
                      render-gate profile produces a builder you can spawn but can never message.

                      Fork-only. We are not sending this upstream.

                      Two defects, both must be fixed

                      1. Launch is broken — the prompt is passed as a project path

                      A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
                      "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

                      Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
                      positional argument — the claude/codex convention:

                      opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

                      OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
                      file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
                      prompt falls through to codev's generic positional form.

                      But opencode's positional slot is a project path, not a prompt:

                      opencode [project] start opencode tui [default]
                      opencode run [message..] run opencode with a message
                      Positionals:
                      project path to start opencode in [string]
                      Options:
                      --prompt
                      

                      Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
                      against the installed opencode version rather than assuming the flag name — check opencode --help
                      first and record the version you tested.

                      2. No render-gate profile

                      packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
                      afx send to an opencode builder is held forever with reason no-profile.

                      A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

                      • markerPattern — the composer's prompt glyph at the input row start
                      • regionEndPatterns — the line(s) that bound the composer region below

                      These must be MEASURED from a real captured screen, never guessed. The file's own warning:
                      REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
                      injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
                      live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
                      patterns from those captures. Include the captures in the PR.

                      Acceptance

                      1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
                      2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
                      3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
                      4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
                        instead of producing a silently unmessageable builder.
                      5. Captured frames used to derive the profile are committed.

                      Notes

                      • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
                        the way agy got its own for a different marker glyph and color rule.
                      • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
                        strict app-identity table so an unknown app never inherits claude's (wrong) profile.

                      Activity

                      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

                        area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

                          Description

                          @pseudoseed

                          Goal

                          Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
                          prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
                          render-gate profile produces a builder you can spawn but can never message.

                          Fork-only. We are not sending this upstream.

                          Two defects, both must be fixed

                          1. Launch is broken — the prompt is passed as a project path

                          A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
                          "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

                          Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
                          positional argument — the claude/codex convention:

                          opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

                          OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
                          file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
                          prompt falls through to codev's generic positional form.

                          But opencode's positional slot is a project path, not a prompt:

                          opencode [project] start opencode tui [default]
                          opencode run [message..] run opencode with a message
                          Positionals:
                          project path to start opencode in [string]
                          Options:
                          --prompt
                          

                          Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
                          against the installed opencode version rather than assuming the flag name — check opencode --help
                          first and record the version you tested.

                          2. No render-gate profile

                          packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
                          afx send to an opencode builder is held forever with reason no-profile.

                          A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

                          • markerPattern — the composer's prompt glyph at the input row start
                          • regionEndPatterns — the line(s) that bound the composer region below

                          These must be MEASURED from a real captured screen, never guessed. The file's own warning:
                          REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
                          injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
                          live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
                          patterns from those captures. Include the captures in the PR.

                          Acceptance

                          1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
                          2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
                          3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
                          4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
                            instead of producing a silently unmessageable builder.
                          5. Captured frames used to derive the profile are committed.

                          Notes

                          • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
                            the way agy got its own for a different marker glyph and color rule.
                          • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
                            strict app-identity table so an unknown app never inherits claude's (wrong) profile.

                          Activity

                          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

                            area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Make opencode a working builder harness: fix launch invocation + add a measured render-gate profile #4

                              Description

                              @pseudoseed

                              Goal

                              Make opencode work end-to-end as a builder harness in this fork, so it can host Grok. This is the
                              prerequisite for #2 (per-spawn --model): a harness without a working launch and a measured
                              render-gate profile produces a builder you can spawn but can never message.

                              Fork-only. We are not sending this upstream.

                              Two defects, both must be fixed

                              1. Launch is broken — the prompt is passed as a project path

                              A builder with shell.builder: "opencode" launches, immediately exits 0, and shows
                              "Agent exited at your request. Press Enter to relaunch, or close this terminal." It never runs.

                              Cause: packages/codev/src/agent-farm/commands/spawn-worktree.ts:875 passes the initial prompt as a
                              positional argument — the claude/codex convention:

                              opencode "$(cat '/…/.builders/task-XXXX/.builder-prompt.txt')"

                              OPENCODE_HARNESS.buildScriptRoleInjection returns an empty fragment (opencode injects its role
                              file-based, via the opencode.json it writes: { "instructions": [".builder-role.md"] }), so the
                              prompt falls through to codev's generic positional form.

                              But opencode's positional slot is a project path, not a prompt:

                              opencode [project] start opencode tui [default]
                              opencode run [message..] run opencode with a message
                              Positionals:
                              project path to start opencode in [string]
                              Options:
                              --prompt
                              

                              Fix: emit --prompt (or opencode run) for this harness instead of the positional form. Verify
                              against the installed opencode version rather than assuming the flag name — check opencode --help
                              first and record the version you tested.

                              2. No render-gate profile

                              packages/codev/src/agent-farm/servers/gate-profiles.ts resolves opencode to null, so every
                              afx send to an opencode builder is held forever with reason no-profile.

                              A profile needs, per the existing CLAUDE_PROFILE / CODEX_PROFILE / agy shapes:

                              • markerPattern — the composer's prompt glyph at the input row start
                              • regionEndPatterns — the line(s) that bound the composer region below

                              These must be MEASURED from a real captured screen, never guessed. The file's own warning:
                              REGION_END_PATTERNS is "FAIL-SAFE but DRIFT-FRAGILE"; a too-loose pattern is a false-clean that
                              injects a message while the agent is mid-typing and corrupts its input. Capture real frames from a
                              live opencode TUI (idle-empty, mid-typing, mid-stream, and with a dialog open) and derive the
                              patterns from those captures. Include the captures in the PR.

                              Acceptance

                              1. afx spawn --task "sit idle" with shell.builder: "opencode" produces a builder that actually runs.
                              2. afx send <that-builder> "hello" returns delivered, not held/no-profile.
                              3. afx send while the opencode builder is mid-turn HOLDS rather than injecting (no false-clean).
                              4. Spawning a harness that has no gate profile fails loudly at spawn time with a clear message,
                                instead of producing a silently unmessageable builder.
                              5. Captured frames used to derive the profile are committed.

                              Notes

                              • Do not "fix" this by loosening the shared REGION_END_PATTERNS. Give opencode its own patterns,
                                the way agy got its own for a different marker glyph and color rule.
                              • resolveHarness's claude fallback must not be reused here — gate-profiles deliberately keeps a
                                strict app-identity table so an unknown app never inherits claude's (wrong) profile.

                              Activity

                              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

                                area/towerTower, afx, terminals, messagingbugSomething isn't workingfork-onlyDeliberately not upstreamed to cluesmith/codev

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions