Skip to content

fix: re-enable test and typecheck CI on integration-v2 - #420

Merged
shuv1337 merged 7 commits into
integration-v2from
fm/sc-ci-reenable
Oct 4, 2026
Merged

shuv1337 merged 7 commits into
integration-v2from
fm/sc-ci-reenable

Conversation

@shuv1337

@shuv1337 shuv1337 commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Intent

Captain's ask: "Queue a fix to re-enable shuvcode test + typecheck CI".

Context: in the fork https://github.com/shuv1337/shuvcode the GitHub Actions workflows test (.github/workflows/test.yml) and typecheck (.github/workflows/typecheck.yml) are disabled manually. Their last runs failed (test: 2026-08-17 on a PR branch; the run logs have expired). As a result a PR showing "green" only means Socket Security passed, and merges rely on local tests alone. The goal is for both workflows to pass on the default branch integration-v2 and on PRs against it, so they can be turned back on and mean something.

What Changed

  • The test and typecheck workflows now run on pushes to integration-v2 instead of upstream's v2. The test workflow's concurrency group and E2E_ENABLED guard also check integration-v2 now. The upstream "Verify packed workerd SDK" step is removed, with a comment explaining why: the fork's @opencode/core depends on private, source-only @shuvcode/* plugins that can't be packed.
  • packages/cli/script/service-smoke.ts now reads the service registration from state/shuvcode and the failure log from data/shuvcode/log, which match the fork's Global.app directories. It used to read from opencode.
  • packages/util/src/cross-spawn-spawner.ts adds an error listener on the child's stdin. It ignores an EPIPE that arrives late, after a child exits without reading all of its input, so the run reports the child's exit code instead of crashing on an uncaught error. Any other error is still thrown if no other handler is attached. A new AppProcess test in packages/core/test/process/process.test.ts covers this: the child closes stdin and exits with code 3, and the test expects exit code 3.

Risk Assessment

✅ Low: The change is small. It retargets the CI branch from v2 to integration-v2 and fixes the smoke-test paths to use the shuvcode XDG app name. The stdin EPIPE fix now defers to the active NodeSink: it returns while another error listener is attached and rethrows only when it is the sole listener, which matches the prior unhandled-error behavior. The new regression test checks observable behavior, the exit code.

Testing

I ran the two compiled-service smoke tests the way the test workflow does, with the Bun and Node builds, and both passed. I also ran the old smoke script against the same binary to show it failed before the path change. I ran the stdin regression test with and without the spawner fix and ran the whole process.test.ts file. All of these passed. I checked the workflow trigger changes by parsing the YAML files only. GitHub Actions has not run them, so that scenario is marked untested. The first smoke run failed only because my shell's OPENCODE_CONFIG_DIR pointed at the user's personal shuvcode config, which needs a token file that doesn't exist in the smoke test's temp home. GitHub runners don't set that variable, so I unset it and re-ran. Two things couldn't be run here: the real GitHub Actions runs, which belong to the CI phase, and the case where a non-EPIPE stdin error arrives while the stream is still writing. I removed the build output I created (packages/cli/dist, dist-node and .cache). This change has no UI, so there are no screenshots.

  • Live validation: ✅ go - 4 of 7 scenarios driven live against the product
Scenario Result Live Evidence
The 'Verify compiled service lifecycle' step (Bun single build plus service-smoke.ts) passes: two services start, registration appears under state/shuvcode, auth and the plugin hot-reload checks succe… ✅ pass live service-smoke-bun.log (exit 0)
The 'Verify Node build' step (build-node plus service-smoke.ts --node) passes ✅ pass live service-smoke-node.log (exit 0)
Failure check: the old smoke script, which reads state/opencode, fails against the same shuvcode binary, so the path fix is what makes the step pass ✅ pass live service-smoke-base-script.log: 'Compiled service did not publish registration', exit 1
A child process that closes stdin and exits with code 3 before late input arrives reports exit code 3 instead of crashing with an uncaught EPIPE ✅ pass live epipe-test-without-fix.log (fails with an uncaught EPIPE) vs epipe-test-with-fix.log (passes); process-test.log 28/28
Both workflows trigger on pushes to integration-v2, on all pull requests and on manual runs; the default-branch concurrency group and the E2E gate use integration-v2, and the unpackable SDK step is go… ⏸️ untested no The earlier payload only parsed the workflow YAML (workflow-semantics.txt). GitHub Actions never ran these triggers, so there is no live result. Confirming it needs real workflow runs, which belong to…
Both workflows go green on GitHub Actions for integration-v2 and for PRs once re-enabled ⏸️ untested no This needs a push and real GitHub Actions runs, which the outer pipeline's push and CI phases own. This test phase isn't allowed to push or trigger workflows.
A non-EPIPE stdin error while the stream is still writing still becomes a PlatformError instead of an uncaught throw (the review-round fix) ⏸️ untested no No public interface here can reliably make a running child's stdin pipe produce an error other than EPIPE, such as ERR_STREAM_DESTROYED. Testing it would need fault injection inside the spawner.
Evidence: Bun compiled service lifecycle smoke (pass)
bun smoke exit=0
Evidence: Node build service lifecycle smoke (pass)
shuvcode script {
  "channel": "fm/sc-ci-reenable",
  "version": "0.0.0-fm/sc-ci-reenable-202610040751",
  "preview": true,
  "release": false,
  "team": [
    "adamdotdevin",
    "arvsrn",
    "Brendonovich",
    "fwang",
    "Hona",
    "iamdavidhill",
    "jayair",
    "jlongster",
    "kitlangton",
    "kommander",
    "ludvigrask",
    "MrMushrooooom",
    "neriousy",
    "nexxeln",
    "R44VC0RP",
    "rekram1-node",
    "thdxr",
    "simonklee",
    "Slickstef11",
    "usrnk1",
    "vimtor",
    "StarpTech",
    "actions-user",
    "opencode",
    "opencode-agent[bot]"
  ]
}
$ vite build
(!) Your Vite config uses features that are unsupported by `configLoader: 'native'`, which is planned to become the default in a future major version of Vite:
  - import "./vite.icons" without a file extension (vite.config.ts:5:23). Add the file extension
  - import "./vite.pwa" without a file extension (vite.config.ts:6:31). Add the file extension
Set `VITE_CONFIG_NATIVE_IGNORE_WARNING=true` to suppress this warning.
vite v8.2.2 building client environment for production...
transforming...
Found 2 warnings while optimizing generated CSS:

Issue #1:
^[[2m│     }^[[22m
^[[2m│   }^[[22m
^[[2m│^[[22m   ::highlight(timeline-search-hit) {
^[[2m┆^[[22m               ^[[33m^[[2m^--^[[22m 'highlight' is not recognized as a valid pseudo-element. Did you mean ':highlight' (pseudo-class) or is this a typo?^[[39m
^[[2m┆^[[22m
^[[2m│     background-color: var(--v2-icon-icon-accent);^[[22m
^[[2m│     @supports (color: color-mix(in lab, red, red)) {^[[22m

Issue #2:
^[[2m│     }^[[22m
^[[2m│   }^[[22m
^[[2m│^[[22m   ::highlight(timeline-search-hit-active) {
^[[2m┆^[[22m               ^[[33m^[[2m^--^[[22m 'highlight' is not recognized as a valid pseudo-element. Did you mean ':highlight' (pseudo-class) or is this a typo?^[[39m
^[[2m┆^[[22m
^[[2m│     background-color: var(--v2-icon-icon-accent);^[[22m
^[[2m│     color: var(--v2-background-bg-deep);^[[22m

✓ 5622 modules transformed.
rendering chunks...
computing gzip size...
dist/site.webmanifest                                              0.38 kB
dist/_assets/shellsession-DSq1-0R_.js                              0.76 kB
dist/_assets/html-derivative-D-cG0UfK.js                           0.90 kB
dist/_assets/git-rebase-DfWiU9Be.js                                1.03 kB
dist/_assets/git-commit-dUGrvm6Q.js                                1.28 kB
dist/_assets/xsl-DDu5xR8B.js                                       1.40 kB
dist/_assets/sparql-C0ia5K60.js                                    1.52 kB
dist/_assets/fortran-fixed-form-D6mN0kjy.js                        1.72 kB
dist/_assets/hxml-D7Y24wKF.js                                      1.93 kB
dist/_assets/erb-BaXtTJcU.js                                       2.18 kB
dist/_assets/edge-CiVRGYk0.js                                      2.35 kB
dist/_assets/json-ujcYFro9.js                                      2.93 kB
dist/_assets/cairo-DRAZz75c.js                                     2.98 kB
dist/_assets/hurl-CSVV25V7.js                                      3.56 kB
dist/_assets/glsl-BypMdYZk.js                                      3.75 kB
dist/_assets/custom-managed-provider-DsgXGulM.svg                  4.16 kB │ gzip:   1.15 kB
dist/_assets/nope-02-EygnDbCM.aac                                  4.18 kB
dist/_assets/nope-08-COPo0uNf.aac                                  4.24 kB
dist/_assets/staplebops-06-Cj_2vOI4.aac                            4.28 kB
dist/_assets/bip-bop-03-DXp7Zb0f.aac                               4.38 kB
dist/_assets/yup-01-BtRq6dLN.aac                                   4.41 kB
dist/_assets/KaTeX_Size3-Regular-CTq5MqoE.woff                     4.42 kB
dist/_assets/http-Dqdy1dFK.js                                      4.46 kB
dist/_assets/nextflow-CYn2Pyp4.js                                  4.55 kB
dist/_assets/staplebops-07-cqQEvbIf.aac                            4.62 kB
dist/_assets/staplebops-04-olyHi8qQ.aac                            4.66 kB
dist/_assets/alert-02-CS75AsoP.aac                                 4.68 kB
dist/_assets/nope-05-DZsXzrQW.aac                                  4.79 kB
dist/_assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2                    4.92 kB
dist/_assets/alert-07-0r0kiLGz.aac                                 5.18 kB
dist/_assets/alert-05-D2gbGoRH.aac                                 5.20 kB
dist/_assets/KaTeX_Size2-Regular-Dy4dx90m.woff2                    5.20 kB
dist/_assets/bip-bop-08-DBf7Bwjz.aac                               5.30 kB
dist/_assets/staplebops-03-Aug82oH0.aac                            5.31 kB
dist/_assets/alert-04-CaGsIGFP.aac                                 5.31 kB
dist/_assets/gdresource-CZkFGo3B.js                                5.33 kB
dist/_assets/qml-CYjjm0hH.js                                       5.38 kB
dist/_assets/yup-05-CuuaeyjC.aac                                   5.42 kB
dist/_assets/KaTeX_Size1-Regular-mCD8mA8B.woff2                    5.46 kB
dist/_assets/64x64-D8-3fHil.png                                    5.46 kB
dist/_assets/xml-DmWisOGJ.js                                       5.49 kB
dist/_assets/bip-bop-04-CfVtpI7z.aac                               5.57 kB
dist/_assets/bip-bop-06-BuvNosjK.aac                               5.61 kB
dist/_assets/jinja-BZ51w7Vq.js                                     5.68 kB
dist/_assets/nope-12-BJR1Ka3c.aac                                  5.72 kB
dist/_assets/nope-11-CVdXg8G-.aac                                  5.76 kB
dist/_assets/shaderlab-C4rnI7x0.js                                 5.97 kB
dist/_assets/KaTeX_Size4-Regular-BF-4gkZK.woff                     5.98 kB
dist/_assets/KaTeX_Size2-Regular-oD1tc_U0.woff                     6.18 kB
dist/_assets/alert-01-BuOD_o_q.aac                                 6.30 kB
dist/_assets/alert-08-CZowNquU.aac                                 6.32 kB
dist/_assets/vscode-C5BXgFjm.svg                                   6.46 kB │ gzip:   2.43 kB
dist/_assets/KaTeX_Size1-Regular-C195tn64.woff                     6.49 kB
dist/_assets/chapel-BX7IPmUl.js                                    6.67 kB
dist/_assets/alert-10-Ck5hR7zH.aac                                 6.68 kB
dist/_assets/r-BoYMjVB3.js                                         6.83 kB
dist/_assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2              6.90 kB
dist/_assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2                 6.91 kB
dist/_assets/soy-BBMyZ0-9.js                                       6.97 kB
dist/index.html                                                    7.16 kB │ gzip:   1.99 kB
dist/_assets/yup-03-BHLEoqSS.aac                                   7.18 kB
dist/_assets/alert-06-GK5Tqy6u.aac                                 7.40 kB
dist/_assets/KaTeX_Size3-Regular-DgpXs0kz.ttf                      7.58 kB
dist/_assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff               7.65 kB
dist/_assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff                  7.71 kB
dist/_assets/antigravity-m-mWKI7R.svg                              8.03 kB │ gzip:   2.06 kB
dist/_assets/regexp-BUR_tR-p.js                                    8.09 kB
dist/_assets/yup-04-C7yadpJT.aac                                   8.37 kB
dist/_assets/haml-DwVgTuPZ.js                                      8.38 kB
dist/_assets/vue-html-CzF4tPpl.js                                  8.52 kB
dist/_assets/sas-pz8Lf1bp.js                                       9.11 kB
dist/_assets/KaTeX_Script-Regular-D3wIWfF6.woff2                   9.64 kB
dist/_assets/jison-CVssV8Ka.js                                     9.74 kB
dist/_assets/tex-86ipjeV1.js                                       9.78 kB
dist/_assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2               10.34 kB
dist/_assets/rst-Baqg6nS5.js                                      10.35 kB
dist/_assets/KaTeX_Size4-Regular-DWFBv043.ttf                     10.36 kB
dist/_assets/KaTeX_Script-Regular-D5yQViql.woff                   10.58 kB
dist/_assets/yaml-BLcy9mxl.js                                     10.61 kB
dist/_assets/just-C1leJKpN.js                                     10.88 kB
dist/_assets/elm-BdX11Kib.js                                      10.99 kB
dist/_ass

... [169152 bytes truncated] ...

                        332.37 kB │ gzip: 100.35 kB │ map: 1,298.66 kB
dist/_assets/cytoscape.esm-C2x7inlt.js                           434.97 kB │ gzip: 137.86 kB │ map: 1,710.03 kB
dist/_assets/wasm-BnjxR4X6.js                                    622.36 kB │ gzip: 232.13 kB │ map:   622.85 kB
dist/_assets/index-CXeku-4r.js                                   626.03 kB │ gzip: 191.67 kB │ map: 2,214.33 kB
dist/_assets/chunk-FOHPRMQF-DHwB1DNv.js                          662.17 kB │ gzip: 143.20 kB │ map: 1,952.99 kB
dist/_assets/cpp-71xkOQct.js                                     785.58 kB │ gzip:  58.31 kB │ map: 1,019.12 kB
dist/_assets/emacs-lisp-c_oH4hRZ.js                              790.04 kB │ gzip: 200.34 kB │ map:   800.02 kB
dist/_assets/ghostty-web-Ch-lxWIv.js                           1,379.62 kB │ gzip: 429.60 kB │ map: 1,525.54 kB

^[[33m[INEFFECTIVE_DYNAMIC_IMPORT] ^[[0m../../node_modules/.bun/@sentry+solid@10.71.0+1b925581e2785e69/node_modules/@sentry/solid/build/esm/index.js is dynamically imported by src/app.tsx, src/shell/errors/error.tsx but also statically imported by src/entry.tsx, dynamic import will not move module into another chunk.

[plugin builtin:vite-reporter] 
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rolldownOptions.output.codeSplitting to improve chunking: https://rolldown.rs/reference/OutputOptions.codeSplitting
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
^[[33m[INEFFECTIVE_DYNAMIC_IMPORT] ^[[0msrc/providers/models/select-dialog.tsx is dynamically imported by src/composer/commands.tsx but also statically imported by src/composer/composer.tsx, dynamic import will not move module into another chunk.

✓ built in 5.14s

PWA v1.3.0
mode      generateSW
precache  1609 entries (34495.81 KiB)
files generated
  dist/sw.js
building shuvcode-node-linux-x64
vite v7.3.6 building ssr environment for production...
transforming...
"Agent" and "getGlobalDispatcher" are imported from external module "undici" but never used in "../../node_modules/.bun/@effect+platform-node@4.0.0-rc.112+675766e6832c1e59/node_modules/@effect/platform-node/dist/Undici.js".
✓ 6193 modules transformed.
../../node_modules/.bun/gray-matter@4.0.3/node_modules/gray-matter/lib/engines.js (43:13): Use of eval in "../../node_modules/.bun/gray-matter@4.0.3/node_modules/gray-matter/lib/engines.js" is strongly discouraged as it poses security risks and may cause issues with minification.
../../node_modules/.bun/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.js (2978:32): Use of eval in "../../node_modules/.bun/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.js" is strongly discouraged as it poses security risks and may cause issues with minification.
../../node_modules/.bun/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.js (3001:34): Use of eval in "../../node_modules/.bun/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.js" is strongly discouraged as it poses security risks and may cause issues with minification.
rendering chunks...
dist-node/opencode.mjs  29,841.94 kB
✓ built in 14.00s
shuvcode-node v0.0.0-fm/sc-ci-reenable-202610040751
DESCRIPTION
  Shuvcode command line interface

USAGE
  shuvcode-node <subcommand> [flags] [<directory>]

ARGUMENTS
  directory string    Directory to start Shuvcode in (optional)

FLAGS
  --standalone            Run with a private server instead of the background service
  --server string         Connect to a server URL instead of the background service
  --auto                  Auto-approve permissions that are not explicitly denied
  --continue, -c          Continue the last session
  --session, -s string    Session ID to continue, or to create if it does not exist
  --prompt string         Prompt to use

GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
  --log-level <all|trace|debug|info|warn|warning|error|fatal|none>    Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
  --print-logs                                                        Print logs to stderr (server logs require --standalone)

SUBCOMMANDS
  upgrade, update    Upgrade Shuvcode to the latest or a specific version
  uninstall          Uninstall Shuvcode and remove all related files
  acp                Start an Agent Client Protocol server
  api                Make a request to the running server
  debug              Debugging and troubleshooting tools
  auth               manage integrations and credentials
  mcp                Manage MCP (Model Context Protocol) servers
  plugin             Manage plugins
  models             List all available models
  stats              Show shareable usage statistics
  mini               Start the minimal interactive interface
  run                Run Shuvcode with a message
  session            Manage sessions
  service            Manage the background server
  reload             Reload configuration
  pair               Print one-time links to connect a browser or app
  serve              Start the v2 API and web server
Generated single executable ~/.no-mistakes/worktrees/284027b67ce8/01M42Y6XEVSKNWR5YGPA6ZCMAW/packages/cli/.cache/node/node-v26.4.0-linux-x64/bin/node + dist-node/sea.json -> dist/node/shuvcode-node-linux-x64/bin/shuvcode-node
shuvcode-node v0.0.0-fm/sc-ci-reenable-202610040751
DESCRIPTION
  Shuvcode command line interface

USAGE
  shuvcode-node <subcommand> [flags] [<directory>]

ARGUMENTS
  directory string    Directory to start Shuvcode in (optional)

FLAGS
  --standalone            Run with a private server instead of the background service
  --server string         Connect to a server URL instead of the background service
  --auto                  Auto-approve permissions that are not explicitly denied
  --continue, -c          Continue the last session
  --session, -s string    Session ID to continue, or to create if it does not exist
  --prompt string         Prompt to use

GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
  --log-level <all|trace|debug|info|warn|warning|error|fatal|none>    Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
  --print-logs                                                        Print logs to stderr (server logs require --standalone)

SUBCOMMANDS
  upgrade, update    Upgrade Shuvcode to the latest or a specific version
  uninstall          Uninstall Shuvcode and remove all related files
  acp                Start an Agent Client Protocol server
  api                Make a request to the running server
  debug              Debugging and troubleshooting tools
  auth               manage integrations and credentials
  mcp                Manage MCP (Model Context Protocol) servers
  plugin             Manage plugins
  models             List all available models
  stats              Show shareable usage statistics
  mini               Start the minimal interactive interface
  run                Run Shuvcode with a message
  session            Manage sessions
  service            Manage the background server
  reload             Reload configuration
  pair               Print one-time links to connect a browser or app
  serve              Start the v2 API and web server
node smoke exit=0
Evidence: Base-commit smoke script against the same binary (fails: no registration under state/opencode)
 99 | await fs.rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }).catch((cause: unknown) => {
100 |   console.error("Failed to remove service smoke-test directory", cause)
101 |   failure ??= cause
102 | })
103 | if (failure)
104 |   throw new Error(output.filter(Boolean).join("\n") || "Compiled service lifecycle smoke test failed", {
                  ^
error: Compiled service lifecycle smoke test failed
      at ~/.no-mistakes/worktrees/284027b67ce8/01M42Y6XEVSKNWR5YGPA6ZCMAW/packages/cli/script/.base-smoke.ts:104:13

120 |       (file) => file === "service.json" || (file.startsWith("service-") && file.endsWith(".json")),
121 |     )
122 |     if (file) return path.join(directory, file)
123 |     await Bun.sleep(25)
124 |   }
125 |   throw new Error("Compiled service did not publish registration")
                  ^
error: Compiled service did not publish registration
      at waitForRegistration (~/.no-mistakes/worktrees/284027b67ce8/01M42Y6XEVSKNWR5YGPA6ZCMAW/packages/cli/script/.base-smoke.ts:125:13)

Bun v1.4.2 (Linux x64)
base script exit=1
Evidence: Stdin regression test without the spawner fix (uncaught EPIPE)
bun test v1.4.2 (744846f84)

test/process/process.test.ts:
EPIPE: broken pipe, write
 syscall: "write",
   errno: -32,
    code: "EPIPE"

      at end (unknown:1:1)
      at internal:fs/streams:402:23
      at _destroy (internal:streams/destroy:63:18)
      at destroy (internal:streams/destroy:41:13)
      at internal:streams/writable:601:23
      at finish (internal:streams/writable:482:21)
      at internal:streams/writable:469:19
      at processTicksAndRejections (native:7:39)
(fail) AppProcess > run with stdin option > reports the exit code when the child exits before reading stdin [118.28ms]

 0 pass
 27 filtered out
 1 fail
Ran 1 test across 1 file. [183.00ms]
exit=1
Evidence: Stdin regression test with the fix (pass)
bun test v1.4.2 (744846f84)

 1 pass
 27 filtered out
 0 fail
 1 expect() calls
Ran 1 test across 1 file. [378.00ms]
exit=0
Evidence: Full process.test.ts run
bun test v1.4.2 (744846f84)

 28 pass
 0 fail
 63 expect() calls
Ran 28 tests across 1 file. [846.00ms]
Evidence: Parsed workflow triggers and steps
test {
 "push": {
  "branches": [
   "integration-v2"
  ]
 },
 "pull_request": null,
 "dispatch": true,
 "concurrency": "${{ case(github.ref == 'refs/heads/integration-v2', format('{0}-{1}', github.workflow, github.run_id), format('{0}-{1}', github.workflow, github.event.pull_request.number || github.ref)) }}",
 "e2e": "${{ needs.affected.outputs.app == 'true' && github.ref_name != 'integration-v2' && github.head_ref != 'integration-v2' }}",
 "sdkPackStep": false
}
typecheck {
 "push": {
  "branches": [
   "integration-v2"
  ]
 },
 "pull_request": null,
 "dispatch": true,
 "concurrency": "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}",
 "sdkPackStep": false
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • ⚠️ packages/util/src/cross-spawn-spawner.ts:234 - The new proc.stdin.on(&#34;error&#34;) listener is registered before NodeSink.fromWritable attaches its own listener, and it rethrows every non-EPIPE error. EventEmitter calls listeners in registration order, so any non-EPIPE stdin error that happens while the sink is still writing now throws out of emit first. The sink's onError never runs, so the error that used to become a typed PlatformError (fromWritable(stdin)) now becomes an uncaught exception that can crash the process. Example: an ERR_STREAM_WRITE_AFTER_END or ERR_STREAM_DESTROYED error during an active stdin stream, or a Windows EOF pipe error. Fix: keep the listener from preempting the sink. Make it ignore EPIPE without rethrowing anything, or rethrow only when it is the sole remaining error listener (proc.stdin.listenerCount(&#34;error&#34;) === 1). Either way the sink still maps errors that happen while it is attached.
  • ℹ️ .github/workflows/test.yml:121 - The 'Verify packed workerd SDK' step is removed rather than fixed. The comment explains why: the fork publishes only CLI packages, and @opencode/core depends on source-only @shuvcode/* workspace plugins (confirmed in packages/core/package.json). This is a deliberate tradeoff that matches the goal of getting the workflows green. It does mean CI no longer checks @opencode/sdk packability.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 4 of 7 scenarios driven live against the product
Scenario Result Live Evidence
The 'Verify compiled service lifecycle' step (Bun single build plus service-smoke.ts) passes: two services start, registration appears under state/shuvcode, auth and the plugin hot-reload checks succe… ✅ pass live service-smoke-bun.log (exit 0)
The 'Verify Node build' step (build-node plus service-smoke.ts --node) passes ✅ pass live service-smoke-node.log (exit 0)
Failure check: the old smoke script, which reads state/opencode, fails against the same shuvcode binary, so the path fix is what makes the step pass ✅ pass live service-smoke-base-script.log: 'Compiled service did not publish registration', exit 1
A child process that closes stdin and exits with code 3 before late input arrives reports exit code 3 instead of crashing with an uncaught EPIPE ✅ pass live epipe-test-without-fix.log (fails with an uncaught EPIPE) vs epipe-test-with-fix.log (passes); process-test.log 28/28
Both workflows trigger on pushes to integration-v2, on all pull requests and on manual runs; the default-branch concurrency group and the E2E gate use integration-v2, and the unpackable SDK step is go… ⏸️ untested no The earlier payload only parsed the workflow YAML (workflow-semantics.txt). GitHub Actions never ran these triggers, so there is no live result. Confirming it needs real workflow runs, which belong to…
Both workflows go green on GitHub Actions for integration-v2 and for PRs once re-enabled ⏸️ untested no This needs a push and real GitHub Actions runs, which the outer pipeline's push and CI phases own. This test phase isn't allowed to push or trigger workflows.
A non-EPIPE stdin error while the stream is still writing still becomes a PlatformError instead of an uncaught throw (the review-round fix) ⏸️ untested no No public interface here can reliably make a running child's stdin pipe produce an error other than EPIPE, such as ERR_STREAM_DESTROYED. Testing it would need fault injection inside the spawner.
  • cd packages/cli &amp;&amp; bun run script/build.ts --single --skip-install &amp;&amp; env -u OPENCODE_CONFIG_DIR bun run script/service-smoke.ts (exit 0)
  • cd packages/cli &amp;&amp; bun run script/build-node.ts --single --skip-install --outdir=dist/node &amp;&amp; env -u OPENCODE_CONFIG_DIR bun run script/service-smoke.ts --node (exit 0)
  • Ran the base-commit service-smoke.ts (reads state/opencode) against the same compiled binary: it fails with 'Compiled service did not publish registration'
  • cd packages/core &amp;&amp; bun test test/process/process.test.ts: 28 pass
  • Put the base-commit cross-spawn-spawner.ts back temporarily and ran bun test test/process/process.test.ts -t &#34;exits before reading stdin&#34;: fails with an uncaught EPIPE. Restored the fix: passes
  • Parsed .github/workflows/test.yml and typecheck.yml with Bun.YAML.parse and checked the triggers, the concurrency group, the E2E gate and whether the SDK pack step is present
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Fork CI notes

  • Removed check: Verify packed workerd SDK (bun turbo verify:package --filter=@opencode/sdk) is dropped from test.yml. The fork publishes only CLI packages, and its @opencode/core depends on private, source-only @shuvcode/* plugins that have no packable build.
  • Runner differences from upstream (Blacksmith → GitHub ubuntu-latest): the unit job now installs ripgrep. The PowerShell shell-tool tests get a longer timeout because pwsh starts cold on every GitHub runner.
  • Known flake: e2e/regression/timeline-history.spec.ts › "mounts every part once and in order while paging to the start" is sensitive to CPU starvation. In one PR run it failed all 3 attempts; the next run passed. Locally, 9 of 10 runs fail with 5 workers on 4 CPUs, and all 6 pass with 2 workers. When an older page lands under load, the viewport sometimes isn't kept anchored and skips rows. This is upstream app code that this PR doesn't touch.
  • Job times on this PR: unit 8m39s, e2e 12m20s, typecheck 2m13s. All are under 20 minutes.

@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Unit is red, so this does not leave the test workflow green yet.

@opencode/core#test failed 23 tests (Ripgrep > … and search tools > …) on run 37187212772. Each one dies in RipgrepBinary while downloading ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz. packages/core/test/lib/effect.ts replaces fetch and rejects that request (test attempted network request). The runner has no rg on PATH, and the test home has no cached binary, so any core run that reaches those tests fails the same way. Typecheck passed. The compiled service lifecycle and Node smoke steps passed. The new stdin exit-code test is not in the failure list.

The integration-v2 triggers, state/shuvcode and data/shuvcode/log paths, and the stdin EPIPE listener match the stated behavior.

Put ripgrep 15.1.0 on PATH before bun turbo test (the downloader uses which("rg") first), or seed Global.Path.bin with that binary. A distro ripgrep package can be older than 15.1.0.

…Ripgrep and search-tool tests. The fork's workflow runs on GitHub's `ubuntu-latest`, which has no `rg`; upstream uses Blacksmith runners. Without `rg`, the code tries to download ripgrep from GitHub, and the core test harness blocks outside network requests, so the tests fail with "test attempted network request". I reproduced this locally by hiding `rg` from PATH: test/ripgrep.test.ts went 0 pass / 11 fail with the same error. With `rg` present, ripgrep.test.ts and tool-search.test.ts pass (27/27). Fix: the unit job's apt step in `.github/workflows/test.yml` now installs `ffmpeg ripgrep` (renamed "Install ffmpeg and ripgrep", with a short comment). The workflow YAML parses, and actionlint reports only an info-level note that was already there. e2e (not fixed, no code change): One test fails on all three attempts: `e2e/regression/timeline-history.spec.ts` › "mounts every part once and in order while paging to the start". It is an upstream spec that this PR doesn't touch. It fails at the poll after a scroll step (line 114). The CI video and screenshot show the timeline reaching the very top of the session, then not changing until the 10s poll times out. So some timeline part was never mounted earlier in the scroll, and the trace doesn't record which one. Locally the test passes against the production build in local time and with TZ=UTC. It also passes under 4×, 6× and 8× CPU throttling (repeated runs). At 12× it fails at the same line, but there a single slow DOM read times out, which is a different mechanism from CI. I also checked one guess: that the test's 100ms "settled" check fires before an older page renders. Instrumented runs showed the page always rendered first, so that's ruled out locally. The only confirmed difference is the runner: fork CI uses GitHub-hosted `ubuntu-latest`, upstream uses Blacksmith. This PR only changes workflow branch names, the service-smoke state path and stdin EPIPE handling in util; nothing in the app timeline. So I made no speculative change to the test or the app. The e2e failure may be a real virtualizer issue that only shows on slower runners, or a runner-specific test problem. Diagnosing it needs CI-side data, such as the missing part IDs from the GitHub runner. `mobile-session.spec.ts` was flaky once in the same run and passed on retry
… PowerShell test in `packages/core/test/tool-shell.test.ts` ("PowerShell legacy: ordinary syntax reuses approvals: Write-Output "$(Write-Output hello)""). The previous round's ripgrep failures are gone. Cause: the first time pwsh starts on a machine it can take close to 5s, and the GitHub runner always starts it cold. These tests had two 5s limits: Bun's default per-test timeout and the `Effect.timeout(Duration.seconds(5))` in the `runPermissionCommand` helper. So the cold start used up the budget and the command was killed with SIGTERM. Warm pwsh takes about 0.13s. I hit the same failure locally on a cold first run (failed at 5118ms); later runs passed. To reproduce it reliably, I pointed SHELL_SCAN_PWSH at a wrapper that waits 6s before starting the real pwsh: the original file fails ("timed out after 5000ms") and the fixed file passes. Fix (test file only): the PowerShell tests in both loops now pass `{ timeout: 15_000 }`, like other tests in this file, and the helper's limit went from 5s to 10s. Raising only the test timeout still failed locally, because the helper cut it off at about 5s. Most of the diff is Prettier re-indenting the two PowerShell test calls; I put back three unrelated lines Prettier had also changed. Checks: `bun test test/tool-shell.test.ts` gives 108 pass, 0 fail, and `bun typecheck` in packages/core passes. I did not run the whole repo test suite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant