ScienceDiscovery CI
Project-local skill for ScienceDiscovery.
CONTRIBUTING.md owns the layer entry points
(pnpm ci:ut, ci:st, ci:e2e, and the two UT tiers ci:ut:host /
ci:ut:guest), the writable CI_RESULTS_DIR / CI_RUNTIME_DIR overrides,
and the GitCode/GitHub repository split. .ci/README.md
documents the toolchain image and the tag catalog. This skill covers what the
pipelines do with those entry points: which platform runs which layer, how to
read a run on each platform, how to validate a workflow change, and how to
attribute a failure. The local gate before a merge request and the result
comment the bot posts on it are in
.agents/skills/create-pr/SKILL.md.
What runs where
Here, the CI e2e layer and pnpm ci:e2e mean the mocked browser subset.
E2E as a validation method also includes user journeys through public API,
CLI and local-stack product entry points; see the
E2E skill. Those journeys have their own documented
driver commands and are not automatically run by the browser layer. Adapter
smokes in ci:st are not E2E merely because they call a model. Keep layer
names, entry points and pipeline scheduling unchanged when reporting this
broader coverage.
Three pipelines exist and none runs everything.
| Pipeline |
Trigger |
UT |
ST |
Browser E2E subset |
Binary/resource output |
CodeArts — .codearts/workflow/codearts-pipeline.yml |
merge request to main on gitcode.com (open, update, reopen); update and push the PR source branch to start a fresh run |
both tiers: ci:ut:host on the runner, ci:ut:guest in a QEMU guest |
ci:st |
off; the journeys reach a browser under emulation but outrun timeouts sized for native speed |
x86_64 + aarch64 packages; smoke is host-dependent |
CodeArts resources — .codearts/workflow/codearts-resources-pipeline.yml on ci/codearts-resources |
push to ci/codearts-resources |
— |
— |
— |
checksum-pinned toolchains and QEMU image uploaded to stable OBS keys |
GitHub Actions — .github/workflows/ci.yml |
push to main, pull request, or workflow_dispatch on the mirror openJiuwen-ai/sciencediscovery |
full ci:ut |
ci:st |
mocked ci:e2e |
x86_64 + aarch64, smoke-gated |
CodeArts's default pool cannot create user namespaces, so the UT guest tier
cannot run directly there. The debug pipeline runs the unchanged ci:ut:guest
layer in a checksum-pinned, pre-provisioned Ubuntu guest under software-only
QEMU TCG. That guest is not a separate test project: it is where the guest UT
tier executes, and the PR result table shows both tiers as UT.
The host does the compiling. The job provisions the runner, runs
pnpm install --frozen-lockfile and pnpm build on native CPU, and
.ci/pack-workspace.sh hands the guest git archive HEAD plus the dependency
tree and build output as one payload. The guest streams it in and runs the
layer entry point, which for ut-guest has no install and no build step.
Emulated CPU is roughly an order of magnitude slower than native: the run
before this split spent 476 s building and 92 s installing inside the guest to
reach 142 s of tests. A self-hosted pool that passes a bubblewrap probe
remains preferable.
The mocked E2E group reuses that guest rather than a second E2E definition.
Its host runs the same pnpm ci:e2e entry point with
CI_E2E_PREPARE_ONLY=1, which installs .e2e and the pinned Chromium into
the checkout and stops before the stack; the guest runs pnpm ci:e2e with
CI_E2E_PREPARED=1 and owns the stack and the journeys. The CodeArts host
still never runs a journey itself: the browser and the Runner both need the
namespaces that pool denies. Expect this job to be slow — Chromium and the
Python service environments are provisioned and driven under TCG — and treat a
timeout as evidence for a self-hosted runner, not as a reason to weaken the
group.
The CodeArts debug workflow is a cache consumer, not a cache seeder. Its UT,
ST, binary, and QEMU jobs require the checksum-pinned OBS objects and fail
closed on a missing or invalid object instead of contacting external source
sites. The isolated ci/codearts-resources branch owns source fallback,
verification, and stable-key uploads; it is operational infrastructure and is
not intended to merge into main.
The CodeArts parent workflow also invokes the externally registered code-check
child (SCA, anti-poison, static analysis, blacklist), reads each task's public
result JSON independently, and renders one result comment with those four
statuses, UT, ST, and both debug binary jobs. A second CodeArts
pipeline, codearts-auto-merge-pipeline.yml, lands a merge request when a
CODEOWNERS member comments /merge on it, through GitCode's merge API with
merge_method=rebase (see the CodeArts reference). Both pipelines call build
tasks whose images, parameters and shell are configured on the CodeArts
console; .ci/codearts-console.reference.md mirrors that side, and changing
the console without changing it has already cost a wasted CI round. This repository
intentionally has no .gitcode/workflows/ pipeline; do not reintroduce
GitCode Actions unless the user changes that policy.
Shared rules
- Never weaken a sandbox assertion to make a pipeline green. Tests that
assert isolation or the sandbox's
/workspace view must run on a host where
bubblewrap can create namespaces.
- Call the
pnpm ci:* entry points, never their underlying commands. Do not
create a second platform-specific test definition.
- CodeArts bills pipelines and build tasks against separate quotas, and the
pipeline's is the one that runs out. No workflow step may run shell, clone,
or upload on a pipeline executor: every step is
uses: official_devcloud_cloudBuild against the generic run-shell task,
which checks the merge request out, rebases it, and calls a repository
script through .ci/codearts-build-dispatch.sh. Adding a CI layer means
adding a case to .ci/codearts-layer.sh, not editing a build task in the
console — build tasks cannot be changed from code. A build task's console
shell returns success even when the layer failed, so its OBS action can
upload the log: the CodeArts job status is green by construction and must
never be read as a result. .ci/codearts-verify.sh reads the recorded
exit-code objects back and is the only job that can turn a run red.
pnpm ci:selftest fails the build when a workflow step reaches for the
pipeline quota again, or when the result gate stops keying on that
verification.
- UT has exactly two tiers and no third bucket. Every UT case belongs to
ut:host (runs on an ordinary CI host, no sandbox) or to ut:guest (needs
a Linux guest kernel that grants user namespaces, so bubblewrap works), and
their union is all of UT. A new UT test joins the tier of the package it
lives in: a host-tier test may not depend on a guest capability, and a
guest-tier assertion may not be weakened so the test can move to the host
tier. Do not add a ci:ut:* entry point beside them; extend a tier
instead. pnpm ci:catalog:check fails closed on a UT case with no tier or
two, on a workspace package with tests that lands in both tiers or in
neither, on ci:ut no longer equalling the two tiers, and on a guest tier
that installs or builds. pnpm ci:selftest is that guard's regression
suite.
- Read the failing job log before theorising. If the platform log is not
accessible with the available credentials, ask for the log instead of
inferring the failure from a status badge.
- Preserve the real test exit code when adding artifact upload steps. Stage
the result, upload diagnostics, then restore that exit code.
- Reproduce a pipeline failure with the layer entry point that job ran
(
pnpm ci:ut:host for the CodeArts UT job, pnpm ci:ut for the GitHub
one), on a checkout of the commit the run tested, with CI_RESULTS_DIR /
CI_RUNTIME_DIR pointed somewhere writable. Each layer leaves run.log
and a summary under CI_RESULTS_DIR/<layer>/.
- Do not copy the parent
code_check job status into all four child rows.
Normalize each child JSON independently; only the explicit success aliases
documented in the CodeArts reference pass, and every other value fails
closed. Validate its detail link separately so a missing link does not
overwrite a valid status.
- For a CodeArts merge-request run, test the integration result rather than
the source branch snapshot: pin the downloaded target SHA and event source
SHA, then use
.ci/rebase-codearts-pr.sh to create a disposable rebased
checkout. Never push that rewritten commit. A conflict is a failed check.
Platform routing
- For
.github/workflows/, GitHub-hosted runner behavior, gh run, or GitHub
artifacts, read references/github.md completely
before acting.
- For
.codearts/workflow/, CodeArts runs on GitCode merge requests, OBS test
logs, or the CodeArts CCE runner, read
references/codearts.md completely before acting.
- For CodeArts OBS object keys, public URLs, uploads, run artifacts,
code-check JSON, or the verified toolchain cache, also read
references/codearts-obs.md completely before
acting.
- For cross-platform comparisons or changes, read every applicable reference.
Common failure signals
| Symptom |
Meaning |
bwrap: No permissions to create new namespace |
The host forbids user namespaces. Use only the repository's sandbox-free layer there; do not weaken Runner tests. |
QEMU reports could not load module for type tcg-accel-ops |
A workspace-extracted QEMU cannot find its modules. Point QEMU_MODULE_DIR at the extracted architecture-specific QEMU module directory. |
A guest job reports a cache miss for ScienceDiscovery-qemu-emulator-alpine-x86_64.tar |
The portable emulator was never published, or its checksum moved. Do not reassemble it in the test job — that is the 166 seconds this cache removed. Push or rerun ci/codearts-resources, read Verified published QEMU emulator: <sha256>, and update .ci/qemu-emulator.sha256 if the pinned Alpine packages changed. |
apk.static reports TLS: server certificate not trusted on the resource branch |
The static bootstrap has no X.509 trust store, so its package indexes are empty. Supply the checksum-pinned CA bundle through SSL_CERT_FILE; keep HTTPS and Alpine package-signature verification enabled. |
The merge request stays on ci-running with no result comment |
A job was killed by its CodeArts timeout rather than failing, so nothing recorded an exit code and neither result job's condition became true. Give each guest a QEMU_TIMEOUT_SECONDS inside its job budget so the wrapper always writes a result. |
QEMU boots but the guest emits no QEMU_SANDBOX_TEST_RESULT marker |
The VM timed out, failed before the guest harness ran, or could not shut down cleanly. Keep the job failed and read ut-guest/run.log. |
API test expects runner_exec, gets undefined |
An execution never ran; check sandbox availability first. |
BLOCKED: isolated E2E stack did not become healthy |
The Runner refused to serve; inspect the sandbox probe before application logs. |
The guest reports BLOCKED: ... expects a workspace its host already installed and built |
The host steps did not install and build before the guest started. Fix the host, never the guest: adding an install or build there is exactly what the split removed. |
A guest log shows Scope: all N workspace projects, or ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY |
pnpm's verify-deps-before-run ran and decided to reinstall. Its check keys node_modules/.pnpm-workspace-state-v1.json by the absolute project directories of the machine that installed, so a moved workspace always looks changed. Keep pnpm_config_verify_deps_before_run= — the pnpm_ prefix and the empty value both matter — in the guest environment; do not let the guest install instead. |
ERR_PNPM_OUTDATED_LOCKFILE |
pnpm-lock.yaml is behind a package.json; regenerate it with pnpm install --lockfile-only. |
| Playwright is green with fewer tests than expected |
A skip is not a pass. Check counts and not-passed titles; a BLOCKED precondition is reported as skipped. |
fatal: couldn't find remote ref refs/heads/<source> on a fork PR |
The job fetched a fork-only branch from the upstream repository. Fetch GitCode's upstream merge-request ref instead; see the CodeArts reference. |
The PR result table says COMPLETED |
A CodeArts lifecycle state leaked into user-facing output. Normalize each task to PASSED or FAILED in the parent workflow. |
One code-check JSON says FIALED, is missing, or contains an unknown status |
It is not a success alias. Fail that child closed to FAILED; do not reuse the parent or another child's status. |
A UT/ST public OBS link returns 403 after an early job failure |
The job failed before the upload step, so the object was never created. Probe the object and fall back to the GitCode Checks page. |
| A formal CodeArts job reports a stable OBS cache miss |
Do not add source fallback to the test/build job. Push or rerun ci/codearts-resources, verify its checksum-pinned upload, update the formal prebuilt-image pin when needed, then rerun the formal workflow. |
1---2name: ci3description: Read, diagnose, and change the CI pipelines: CodeArts on GitCode merge requests and GitHub Actions on the mirror. Use when a pipeline or job fails, when editing .codearts/workflow/ or .github/workflows/, when asking which platform runs which layer, when reproducing a pipeline failure locally, or when a job needs the bubblewrap sandbox. Running the layers before a merge request and reading the result comment on it belong to the create-pr skill.4---56# ScienceDiscovery CI78Project-local skill for **ScienceDiscovery**.910[CONTRIBUTING.md](../../../CONTRIBUTING.md) owns the layer entry points11(`pnpm ci:ut`, `ci:st`, `ci:e2e`, and the two UT tiers `ci:ut:host` /12`ci:ut:guest`), the writable `CI_RESULTS_DIR` / `CI_RUNTIME_DIR` overrides,13and the GitCode/GitHub repository split. [.ci/README.md](../../../.ci/README.md)14documents the toolchain image and the tag catalog. This skill covers what the15pipelines do with those entry points: which platform runs which layer, how to16read a run on each platform, how to validate a workflow change, and how to17attribute a failure. The local gate before a merge request and the result18comment the bot posts on it are in19[.agents/skills/create-pr/SKILL.md](../create-pr/SKILL.md).2021## What runs where2223Here, the CI `e2e` layer and `pnpm ci:e2e` mean the mocked **browser subset**.24E2E as a validation method also includes user journeys through public API,25CLI and local-stack product entry points; see the26[E2E skill](../e2e-testing/SKILL.md). Those journeys have their own documented27driver commands and are not automatically run by the browser layer. Adapter28smokes in `ci:st` are not E2E merely because they call a model. Keep layer29names, entry points and pipeline scheduling unchanged when reporting this30broader coverage.3132Three pipelines exist and none runs everything.3334| Pipeline | Trigger | UT | ST | Browser E2E subset | Binary/resource output |35| --- | --- | --- | --- | --- | --- |36| CodeArts — `.codearts/workflow/codearts-pipeline.yml` | merge request to `main` on gitcode.com (open, update, reopen); update and push the PR source branch to start a fresh run | both tiers: `ci:ut:host` on the runner, `ci:ut:guest` in a QEMU guest | `ci:st` | off; the journeys reach a browser under emulation but outrun timeouts sized for native speed | x86_64 + aarch64 packages; smoke is host-dependent |37| CodeArts resources — `.codearts/workflow/codearts-resources-pipeline.yml` on `ci/codearts-resources` | push to `ci/codearts-resources` | — | — | — | checksum-pinned toolchains and QEMU image uploaded to stable OBS keys |38| GitHub Actions — `.github/workflows/ci.yml` | push to `main`, pull request, or `workflow_dispatch` on the mirror `openJiuwen-ai/sciencediscovery` | full `ci:ut` | `ci:st` | mocked `ci:e2e` | x86_64 + aarch64, smoke-gated |3940CodeArts's default pool cannot create user namespaces, so the UT guest tier41cannot run directly there. The debug pipeline runs the unchanged `ci:ut:guest`42layer in a checksum-pinned, pre-provisioned Ubuntu guest under software-only43QEMU TCG. That guest is not a separate test project: it is where the guest UT44tier executes, and the PR result table shows both tiers as UT.4546The host does the compiling. The job provisions the runner, runs47`pnpm install --frozen-lockfile` and `pnpm build` on native CPU, and48`.ci/pack-workspace.sh` hands the guest `git archive HEAD` plus the dependency49tree and build output as one payload. The guest streams it in and runs the50layer entry point, which for `ut-guest` has no install and no build step.51Emulated CPU is roughly an order of magnitude slower than native: the run52before this split spent 476 s building and 92 s installing inside the guest to53reach 142 s of tests. A self-hosted pool that passes a bubblewrap probe54remains preferable.5556The mocked E2E group reuses that guest rather than a second E2E definition.57Its host runs the same `pnpm ci:e2e` entry point with58`CI_E2E_PREPARE_ONLY=1`, which installs `.e2e` and the pinned Chromium into59the checkout and stops before the stack; the guest runs `pnpm ci:e2e` with60`CI_E2E_PREPARED=1` and owns the stack and the journeys. The CodeArts host61still never runs a journey itself: the browser and the Runner both need the62namespaces that pool denies. Expect this job to be slow — Chromium and the63Python service environments are provisioned and driven under TCG — and treat a64timeout as evidence for a self-hosted runner, not as a reason to weaken the65group.66The CodeArts debug workflow is a cache consumer, not a cache seeder. Its UT,67ST, binary, and QEMU jobs require the checksum-pinned OBS objects and fail68closed on a missing or invalid object instead of contacting external source69sites. The isolated `ci/codearts-resources` branch owns source fallback,70verification, and stable-key uploads; it is operational infrastructure and is71not intended to merge into `main`.72The CodeArts parent workflow also invokes the externally registered code-check73child (SCA, anti-poison, static analysis, blacklist), reads each task's public74result JSON independently, and renders one result comment with those four75statuses, UT, ST, and both debug binary jobs. A second CodeArts76pipeline, `codearts-auto-merge-pipeline.yml`, lands a merge request when a77`CODEOWNERS` member comments `/merge` on it, through GitCode's merge API with78`merge_method=rebase` (see the CodeArts reference). Both pipelines call build79tasks whose images, parameters and shell are configured on the CodeArts80console; `.ci/codearts-console.reference.md` mirrors that side, and changing81the console without changing it has already cost a wasted CI round. This repository82intentionally has no `.gitcode/workflows/` pipeline; do not reintroduce83GitCode Actions unless the user changes that policy.8485## Shared rules86871. Never weaken a sandbox assertion to make a pipeline green. Tests that88 assert isolation or the sandbox's `/workspace` view must run on a host where89 bubblewrap can create namespaces.902. Call the `pnpm ci:*` entry points, never their underlying commands. Do not91 create a second platform-specific test definition.923. CodeArts bills pipelines and build tasks against separate quotas, and the93 pipeline's is the one that runs out. No workflow step may run shell, clone,94 or upload on a pipeline executor: every step is95 `uses: official_devcloud_cloudBuild` against the generic `run-shell` task,96 which checks the merge request out, rebases it, and calls a repository97 script through `.ci/codearts-build-dispatch.sh`. Adding a CI layer means98 adding a case to `.ci/codearts-layer.sh`, not editing a build task in the99 console — build tasks cannot be changed from code. A build task's console100 shell returns success even when the layer failed, so its OBS action can101 upload the log: the CodeArts job status is green by construction and must102 never be read as a result. `.ci/codearts-verify.sh` reads the recorded103 `exit-code` objects back and is the only job that can turn a run red.104 `pnpm ci:selftest` fails the build when a workflow step reaches for the105 pipeline quota again, or when the result gate stops keying on that106 verification.1074. UT has exactly two tiers and no third bucket. Every UT case belongs to108 `ut:host` (runs on an ordinary CI host, no sandbox) or to `ut:guest` (needs109 a Linux guest kernel that grants user namespaces, so bubblewrap works), and110 their union is all of UT. A new UT test joins the tier of the package it111 lives in: a host-tier test may not depend on a guest capability, and a112 guest-tier assertion may not be weakened so the test can move to the host113 tier. Do not add a `ci:ut:*` entry point beside them; extend a tier114 instead. `pnpm ci:catalog:check` fails closed on a UT case with no tier or115 two, on a workspace package with tests that lands in both tiers or in116 neither, on `ci:ut` no longer equalling the two tiers, and on a guest tier117 that installs or builds. `pnpm ci:selftest` is that guard's regression118 suite.1195. Read the failing job log before theorising. If the platform log is not120 accessible with the available credentials, ask for the log instead of121 inferring the failure from a status badge.1226. Preserve the real test exit code when adding artifact upload steps. Stage123 the result, upload diagnostics, then restore that exit code.1247. Reproduce a pipeline failure with the layer entry point that job ran125 (`pnpm ci:ut:host` for the CodeArts UT job, `pnpm ci:ut` for the GitHub126 one), on a checkout of the commit the run tested, with `CI_RESULTS_DIR` /127 `CI_RUNTIME_DIR` pointed somewhere writable. Each layer leaves `run.log`128 and a summary under `CI_RESULTS_DIR/<layer>/`.1298. Do not copy the parent `code_check` job status into all four child rows.130 Normalize each child JSON independently; only the explicit success aliases131 documented in the CodeArts reference pass, and every other value fails132 closed. Validate its detail link separately so a missing link does not133 overwrite a valid status.1349. For a CodeArts merge-request run, test the integration result rather than135 the source branch snapshot: pin the downloaded target SHA and event source136 SHA, then use `.ci/rebase-codearts-pr.sh` to create a disposable rebased137 checkout. Never push that rewritten commit. A conflict is a failed check.138139## Platform routing140141- For `.github/workflows/`, GitHub-hosted runner behavior, `gh run`, or GitHub142 artifacts, read [references/github.md](references/github.md) completely143 before acting.144- For `.codearts/workflow/`, CodeArts runs on GitCode merge requests, OBS test145 logs, or the CodeArts CCE runner, read146 [references/codearts.md](references/codearts.md) completely before acting.147- For CodeArts OBS object keys, public URLs, uploads, run artifacts,148 code-check JSON, or the verified toolchain cache, also read149 [references/codearts-obs.md](references/codearts-obs.md) completely before150 acting.151- For cross-platform comparisons or changes, read every applicable reference.152153## Common failure signals154155| Symptom | Meaning |156| --- | --- |157| `bwrap: No permissions to create new namespace` | The host forbids user namespaces. Use only the repository's sandbox-free layer there; do not weaken Runner tests. |158| QEMU reports `could not load module for type tcg-accel-ops` | A workspace-extracted QEMU cannot find its modules. Point `QEMU_MODULE_DIR` at the extracted architecture-specific QEMU module directory. |159| A guest job reports a cache miss for `ScienceDiscovery-qemu-emulator-alpine-x86_64.tar` | The portable emulator was never published, or its checksum moved. Do not reassemble it in the test job — that is the 166 seconds this cache removed. Push or rerun `ci/codearts-resources`, read `Verified published QEMU emulator: <sha256>`, and update `.ci/qemu-emulator.sha256` if the pinned Alpine packages changed. |160| `apk.static` reports `TLS: server certificate not trusted` on the resource branch | The static bootstrap has no X.509 trust store, so its package indexes are empty. Supply the checksum-pinned CA bundle through `SSL_CERT_FILE`; keep HTTPS and Alpine package-signature verification enabled. |161| The merge request stays on `ci-running` with no result comment | A job was killed by its CodeArts timeout rather than failing, so nothing recorded an exit code and neither result job's condition became true. Give each guest a `QEMU_TIMEOUT_SECONDS` inside its job budget so the wrapper always writes a result. |162| QEMU boots but the guest emits no `QEMU_SANDBOX_TEST_RESULT` marker | The VM timed out, failed before the guest harness ran, or could not shut down cleanly. Keep the job failed and read `ut-guest/run.log`. |163| API test expects `runner_exec`, gets `undefined` | An execution never ran; check sandbox availability first. |164| `BLOCKED: isolated E2E stack did not become healthy` | The Runner refused to serve; inspect the sandbox probe before application logs. |165| The guest reports `BLOCKED: ... expects a workspace its host already installed and built` | The host steps did not install and build before the guest started. Fix the host, never the guest: adding an install or build there is exactly what the split removed. |166| A guest log shows `Scope: all N workspace projects`, or `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY` | pnpm's `verify-deps-before-run` ran and decided to reinstall. Its check keys `node_modules/.pnpm-workspace-state-v1.json` by the absolute project directories of the machine that installed, so a moved workspace always looks changed. Keep `pnpm_config_verify_deps_before_run=` — the `pnpm_` prefix and the empty value both matter — in the guest environment; do not let the guest install instead. |167| `ERR_PNPM_OUTDATED_LOCKFILE` | `pnpm-lock.yaml` is behind a `package.json`; regenerate it with `pnpm install --lockfile-only`. |168| Playwright is green with fewer tests than expected | A skip is not a pass. Check counts and not-passed titles; a BLOCKED precondition is reported as skipped. |169| `fatal: couldn't find remote ref refs/heads/<source>` on a fork PR | The job fetched a fork-only branch from the upstream repository. Fetch GitCode's upstream merge-request ref instead; see the CodeArts reference. |170| The PR result table says `COMPLETED` | A CodeArts lifecycle state leaked into user-facing output. Normalize each task to `PASSED` or `FAILED` in the parent workflow. |171| One code-check JSON says `FIALED`, is missing, or contains an unknown status | It is not a success alias. Fail that child closed to `FAILED`; do not reuse the parent or another child's status. |172| A UT/ST public OBS link returns `403` after an early job failure | The job failed before the upload step, so the object was never created. Probe the object and fall back to the GitCode Checks page. |173| A formal CodeArts job reports a stable OBS cache miss | Do not add source fallback to the test/build job. Push or rerun `ci/codearts-resources`, verify its checksum-pinned upload, update the formal prebuilt-image pin when needed, then rerun the formal workflow. |