Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docker/.env.example.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# .env.example — index

Container env knobs, each commented. API keys, `DASHBOARD_PORT`, `PI_GATEWAY_PORT`, `PI_GATEWAY_BIND`, `TUNNEL_ENABLED`, `ZROK_TOKEN`, `PI_SPAWN_STRATEGY`, `MEM_LIMIT`, `PI_WORKSPACES`, `PI_DASHBOARD_PIN_DIRS`, `PI_GATEWAY_TCP` (gateway TCP listener; ON here because external pi sessions cannot reach the in-container socket). See change: docker-packaging. See change: add-pi-gateway-transport-identity.
26 changes: 13 additions & 13 deletions docker/AGENTS.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docker/Dockerfile.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Dockerfile — index

Multi-stage. base: `node:24-bookworm-slim` + tmux/jq/git/curl/ripgrep/fd-find/poppler-utils/build-essential/python3 + zrok (ARG `ZROK_VERSION`). app: non-root pi UID 1000, global `@earendil-works/pi-coding-agent@0.84.1` (pinned; old `@mariozechner` scope frozen). See change: update-pi-core-0-84-adopt-apis, pnpm install+build, npm link, purge build-essential/python3. EXPOSE 8000 9999. VOLUME `/home/pi/.pi` `/home/pi/.zrok2` — preceded by an explicit `mkdir` + `chown pi:pi` of both, because the `VOLUME` instruction creates them as ROOT whatever `USER` is in force and a fresh named volume inherits that ownership, which made `docker compose up` on a clean machine EACCES-restart-loop (masked in E2E by `user: "root"`). COPYs `supervise-daemon.sh` alongside `entrypoint.sh`. ENTRYPOINT `entrypoint.sh`. base also installs `gh` (GitHub CLI, ARG `GH_VERSION`) + `jj` (jujutsu, ARG `JJ_VERSION`, musl static binary, no checksum manifest); app installs `openspec` global alongside pi. base also `COPY tsconfig.base.json tsconfig.json scripts patches` before `pnpm install` (packages extend `../../tsconfig.base.json`; postinstall runs `scripts/maybe-patch-package.cjs` + `scripts/fix-pty-permissions.cjs`; patch-package replays `patches/`). zrok checksum grep uses `grep -E "[ /]${asset}$"` (checksum lines prefix asset with `./`). zrok v2 (`ZROK_VERSION=2.0.4`): tarball ships `zrok2`; extract `zrok2` + `ln -s zrok2 zrok`; smoke `zrok2 version` && `zrok version`. See change: support-zrok-v2. `COPY qa/fixtures ./qa/fixtures` so test-entrypoint stages faux extension. app also installs the flow-plugin e2e peers globally (ARG `PI_FLOWS_VERSION` `@blackbelt-technology/pi-flows`, ARG `PI_ANTHROPIC_MESSAGES_VERSION` `@blackbelt-technology/pi-anthropic-messages`); test-entrypoint.sh selectively wires them per PI_TEST_PEERS. See change: add-flow-plugin-e2e-tests. `corepack enable` pins `pnpm@11.15.1` (root `packageManager`); build `COPY package.json pnpm-lock.yaml pnpm-workspace.yaml` then `pnpm install --frozen-lockfile` + `pnpm run build` (pnpm re-resolves the platform optional-dep tree from the lockfile, so the old npm #4828 `rm -f package-lock.json` lockfile-nuke hack is gone); global tool installs (`npm install -g`) stay npm. See change: docker-packaging. See change: adopt-pnpm-for-dev-ci. See change: docker-test-harness. See change: add-e2e-faux-model-roundtrip. See change: selectable-tool-output-links. See change: default-node-24-keep-22-floor. See change: align-pi-080.6-docker. code-server install layer removed (ARG `CODE_SERVER_VERSION` gone). See change: remove-external-editor-integration. app also installs ARG `PI_DASHBOARD_SUBAGENTS_VERSION` `@blackbelt-technology/pi-dashboard-subagents` globally — the ONLY producer of `tool_execution_update` (its `extensions/agent.ts` drives `createProgressEmitter` → `progress.schedule(details)` into the tool `onProgress`). Absent it, the collapse scenarios P2/F4 have no workload and pass VACUOUSLY. See change: collapse-superseded-tool-execution-updates.
3 changes: 3 additions & 0 deletions docker/TESTING.md.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# TESTING.md — index

Runbook for disposable isolated test harness. Quick start. Isolation-guarantee table: home-lock, mDNS, ports, `~/.pi` state. Path-parity explanation. Overlay-vs-copy-mode tradeoff. Fixtures-vs-mount usage. UI-only-vs-e2e key seeding. agent-browser manual-QA entry. See change: docker-test-harness.
3 changes: 3 additions & 0 deletions docker/compose.test.cap.yml.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# compose.test.cap.yml — index

Overlay-mode capability layer. Grants `cap_add: [SYS_ADMIN]` for `mount -t overlay` in test-entrypoint.sh. test-up.sh layers it only in overlay mode; omits when `TEST_COPY_MODE=1` (copy mode runs no added capability). See change: docker-test-harness.
8 changes: 8 additions & 0 deletions docker/compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ services:
# coexist (first-registration-wins). Requires PI_E2E_SEED=1. Unset =
# harness unchanged (real subagents).
PI_SYNTH_AGENT_TICKS: "${PI_SYNTH_AGENT_TICKS:-}"
# Bridge subagent-timeline strip (change: reduce-subagent-details-payload,
# D2). `0` forwards every frame UNSTRIPPED — the push arm of the
# pull-vs-push A/B, and the anti-vacuity arm proving the strip is what
# makes the pushes thin. Read per call by the bridge, but a container env
# var is fixed for the life of the harness, so each arm is a SEPARATE
# test-up. Unset = strip ON (production default).
# See change: verify-subagent-pull-under-load.
PI_DASHBOARD_SUBAGENT_STRIP: "${PI_DASHBOARD_SUBAGENT_STRIP:-}"

volumes:
# Path-parity: host CWD as the read-only overlay lower layer. The
Expand Down
3 changes: 3 additions & 0 deletions docker/compose.test.yml.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# compose.test.yml — index

Test overlay on `compose.yml`. Sets `PI_DASHBOARD_NO_MDNS=1`, `DASHBOARD_PORT="${DASHBOARD_PORT:-18000}"`, `PI_GATEWAY_PORT="${PI_GATEWAY_PORT:-18999}"` (de-hardcoded, env-driven), `PI_GATEWAY_BIND=127.0.0.1`, `TUNNEL_ENABLED=false`. Per-worktree image tag `pi-dashboard:${TEST_IMAGE_TAG:-local}`, `restart: "unless-stopped"` (matches base compose.yml; was "no": dashboard daemon is container PID 1, so `POST /api/restart` exited the container and nothing revived the harness. unless-stopped still honours explicit `docker stop`; `test-down.sh` runs `compose down` regardless. Trade-off: crash-looping container respawns instead of staying dead; `test-up.sh` health gate still fails the run when dashboard never answers). No `cap_add` here — CAP_SYS_ADMIN layered by `compose.test.cap.yml` in overlay mode only. Overrides `pi-state` volume to tmpfs (ephemeral). Mounts `HOST_CWD` ro lower at `/mnt/test-lower`, tmpfs upper/work, fixtures source at `/fixtures-src`, `test-entrypoint.sh` bind. entrypoint `test-entrypoint.sh`. Runs `user: root` + `HOME=/home/pi` so entrypoint can `mkdir`/`mount` overlay at `/` while state lands in pi-state tmpfs. Passes `PI_E2E_SEED` env to container (`PI_E2E_SEED: "${PI_E2E_SEED:-}"`); default empty keeps harness UI-only. Bind-mounts `../qa/fixtures:/app/qa/fixtures:ro` so test-entrypoint stages faux fixture against warm image without rebuild. Passes `PI_E2E_TRUSTED_NETWORKS: "${PI_E2E_TRUSTED_NETWORKS:-}"` (optional comma-separated override for the PI_E2E_SEED trustedNetworks seed; empty => trust any peer). Passes `PI_TEST_PEERS: "${PI_TEST_PEERS:-}"` (flow-plugin e2e peer selector: both\|no-am\|legacy\|bad-registration; empty => no flow-peer wiring). See change: docker-test-harness. See change: parallelize-test-harness. See change: add-e2e-spawn-scenarios. See change: add-e2e-faux-model-roundtrip. See change: add-flow-plugin-e2e-tests. Passes `PI_E2E_OAUTH` + `PI_E2E_OAUTH_BASE` env through to container (PI_E2E_OAUTH=1 seeds resolvable github provider + bypassUrls before boot so /auth/* routes exist for oauth-redirect-base.spec.ts; requires PI_E2E_SEED=1; unset = harness unchanged). See change: config-override-oauth-redirect-base. Passes `PI_SYNTH_AGENT_TICKS: "${PI_SYNTH_AGENT_TICKS:-}"` (throttle-cadence arm: 1 => test-entrypoint stages the synthetic Agent-tick producer and SKIPS the subagents producer; requires PI_E2E_SEED=1; unset = real subagents). See change: reduce-bridge-tick-bandwidth. Passes `PI_DASHBOARD_SUBAGENT_STRIP: "${PI_DASHBOARD_SUBAGENT_STRIP:-}"` (bridge subagent-timeline strip; `0` = forward every frame UNSTRIPPED = the push arm + anti-vacuity arm of the pull-vs-push A/B. Bridge reads it per call, but a container env var is fixed for the harness life, so each arm is a SEPARATE test-up; unset/empty = strip ON). See change: verify-subagent-pull-under-load.
3 changes: 3 additions & 0 deletions docker/compose.yml.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# compose.yml — index

Base compose. Service `pi-dashboard`, `init:true`, env-driven ports, named volumes `pi-state`→`/home/pi/.pi` + `zrok-state`→`/home/pi/.zrok2`, tmpfs `/tmp`, healthcheck `/api/health`, mem limit. Three commented volume perf profiles (default/performance/ephemeral). Sets `PI_GATEWAY_TCP: "${PI_GATEWAY_TCP:-1}"` — the TCP listener is opt-in since the transport change; the container keeps it with bridge auth mandatory (D10b). See change: docker-packaging. See change: add-pi-gateway-transport-identity.
3 changes: 3 additions & 0 deletions docker/entrypoint.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# entrypoint.sh — index

Runs `seed-auth.js`, seeds spawnStrategy into config.json first-run-only, starts tmux server, enrolls zrok v2 headless when `ZROK_TOKEN` set + not already enrolled (`zrok2 enable "$ZROK_TOKEN" --headless`, idempotent via `~/.zrok2/environment.json`), runs pi-dashboard with env→flag mapping (`DASHBOARD_PORT`→`--port`, `PI_GATEWAY_PORT`→`--pi-port`, `TUNNEL_ENABLED=0`→`--no-tunnel`), then SUPERVISES the detached daemon via `supervise-daemon.sh` instead of `exec`ing it: `pi-dashboard start` returns as soon as the server detaches, so PID 1 exited and the container cycled every ~30s under `restart: unless-stopped` (invisible in E2E, which runs `test-entrypoint.sh` and supervises itself). `PI_ENTRYPOINT_NO_SUPERVISE=1` restores the old exec-and-return for a wrapping caller. See change: docker-packaging, support-zrok-v2, add-pi-gateway-transport-identity.
3 changes: 3 additions & 0 deletions docker/lib-ports.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# lib-ports.sh — index

Sourced pure-helper lib. Exports `derive_hash(cwd)` (cksum), `derive_project(cwd)` -> `pi-dash-test-<hash>`, `is_free(port)` (bash /dev/tcp check), `find_free_in_window(start,lo,hi)` (wrap scan, cap=window size). Windows: dashboard 18000-18999, gateway 19000-19999. See change: parallelize-test-harness.
3 changes: 3 additions & 0 deletions docker/supervise-daemon.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# supervise-daemon.sh — index

`supervise_daemon <pidfile> [label]` — keeps PID 1 alive for a DETACHED dashboard daemon, sourced by BOTH entrypoints so the deployment and the harness cannot drift (the loop living only in `test-entrypoint.sh` is what let the container ship unsupervised). Waits for the pidfile (a cold jiti start outruns the launcher's 30s readiness window), re-reads it every 5s so `POST /api/restart` survives under a new pid, tolerates `RESTART_GRACE_TICKS` (24 x5s) of downtime, and forwards TERM/INT to the CURRENT owner. Deliberately not a foreground server: PID 1 being the server makes `/api/restart` kill the container. See change: add-pi-gateway-transport-identity.
3 changes: 3 additions & 0 deletions docker/test-down.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# test-down.sh — index

Teardown. Re-derives `COMPOSE_PROJECT_NAME` from `$PWD` via lib-ports.sh cksum. `docker compose -p <project> -f compose.yml -f compose.test.yml down -v`. Warns on malformed `.pi-test-harness.json` but continues. Removes `.pi-test-harness.json` after down. Drops tmpfs `pi-state` + overlay upper. Host pristine. See change: docker-test-harness. See change: parallelize-test-harness.
3 changes: 3 additions & 0 deletions docker/test-entrypoint.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# test-entrypoint.sh — index

Test entrypoint wrapper. `HOST_CWD` set → mounts overlayfs (lower `/mnt/test-lower` ro, upper/work tmpfs) at identical `${HOST_CWD}`; `TEST_COPY_MODE=1` falls back to `cp -a` onto tmpfs (no CAP_SYS_ADMIN). Copies `/fixtures-src` to `/fixtures` tmpfs, git init + commit each VCS fixture via a loop (`sample-git sample-hook-ok sample-hook-fail` — the hook fixtures added for change friendlier-worktree-init). Launches base `entrypoint.sh` in background. Fail-fast smoke: curl `/api/health` 200 + one `/ws` WebSocket connect via Node 24 global WebSocket; exits non-zero on failure. Runs base `entrypoint.sh` (launches DETACHED `pi-dashboard start` daemon), then smoke check, then keeps PID 1 alive by polling `~/.pi/dashboard/server.pid` until daemon exits; SIGTERM/INT forwarded to daemon. Tolerates base launcher readiness-timeout non-zero exit (detached daemon survives); polls `/api/health` up to ~90s itself. curl probes bounded `--connect-timeout 1 --max-time 2`. Copy-mode workspace `cp` fail-fast (no error swallow). `PI_E2E_SEED=1` → before base entrypoint, seeds fake never-valid anthropic oauth credential to `~/.pi/agent/auth.json` (flips providersReady, clears LandingPage onboarding gate), seeds trustedNetworks to `~/.pi/dashboard/config.json` so the browser's non-loopback source IP clears createNetworkGuard for `/api/browse` directory listing + `/api/providers`. NOW DEFAULTS TO `0.0.0.0/0`, narrowable via `PI_E2E_TRUSTED_NETWORKS` (comma-separated): the old RFC1918-only list (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) assumed published-port traffic is always SNAT'd through a private bridge gateway, which is FALSE on a VPN'd host — behind Cloudflare WARP the observed peer was the PUBLIC 172.67.221.13, outside 172.16.0.0/12 (spans only 172.16-172.31), so every browser request 403'd (“Network not allowed” + “Server offline”) and every scenario spec died in `pinDirectory`. Trust boundary is the disposable, localhost-published, RAM-backed container, not an IP range. See change: compact-warm-replay-stream (harness fix); both no-op if files exist so base seed-auth.js + config seed skip. `PI_E2E_SEED=1` also stages faux fixture as global extension `~/.pi/agent/extensions/faux-provider/index.ts` + sibling `faux-scenarios.ts`; seeds `defaultModel=faux/faux-1` into `~/.pi/dashboard/config.json` + `~/.pi/agent/settings.json` (merge, no clobber); symlinks `/app/node_modules` into staged extension dir so fixture resolves `@earendil-works/pi-ai`; no-op when present; default OFF. See change: docker-test-harness. See change: add-e2e-spawn-scenarios. See change: add-e2e-faux-model-roundtrip. PI_E2E_SEED=1 also seeds config.json#modelProxy (enabled + one apiKey, hash=sha256 of fixed pi-proxy-e2e-oauth-filter-* key, scopes ["all"]) so /v1/* reachable by tests/e2e/model-proxy-oauth-filter.spec.ts. See change: filter-oauth-incompatible-models. PI_E2E_SEED=1 also seeds the faux role-preset (`/app/qa/fixtures/faux-roles.json`, `_comment` stripped) to `~/.pi/agent/providers.json` (all roles -> faux/faux-1). When `PI_TEST_PEERS` set (both\|no-am\|legacy\|bad-registration): registers the baked pi-flows engine into settings.json packages[] (+ bare `pi-flows` node_modules symlink under /fixtures/sample-git for the bridge tier-1 probe) so flows discover/run, and symlinks the baked anthropic-messages peer into /fixtures/sample-git/node_modules under the scoped name (both/bad-registration), the legacy `@pi/anthropic-messages` (legacy), or not at all (no-am); bad-registration also exports PI_DASHBOARD_DISABLE_PLUGIN_BRIDGE_PACKAGES_WRITE=1 so the bridge stays out of packages[]. When PI_TEST_PEERS set it ALSO: pre-trusts SESSION_CWD in ~/.pi/agent/trust.json (the .pi/flows project resources make /fixtures/sample-git trust-requiring; a headless RPC session would block on pi's "Trust project folder?" prompt -> REGISTER_TIMEOUT); bumps config.json spawnRegisterTimeoutMs=90000 (pi-flows engine jiti-compile on a cold spawn can exceed the 30s default); and warms the pi-flows jiti compile cache in the BACKGROUND (`pi --list-models &`, never delays health). See change: add-flow-plugin-e2e-tests. `PI_E2E_SEED=1` also seeds a decoration-mismatched local install at `/fixtures/local-pkg/image-fit-extension` (`package.json#name` = `@blackbelt-technology/pi-image-fit-extension`, dir basename decorated differently so the pure-string basename rule cannot match) and registers that PATH in settings.json `packages[]` → lands in `activeSources`, exercising the fs-aware name fallback that drives the card's Active/Remove button. Consumed by tests/e2e/recommended-local-name-match.spec.ts. See change: match-local-installs-by-package-name. Fixture MUST be a LOADABLE extension: `type:module` + `pi.extensions:["index.js"]` (not `main`) + an inert `index.js` default export — pi loads every `packages[]` entry at session start and an entry-less manifest is fatal. package.json + index.js written only when absent; settings registration always ensured. See change: restore-ask-user-tool-state-on-reconnect. `PI_E2E_SEED=1` also seeds two plugin-page fixture states into `~/.pi/dashboard/plugins/`: `e2e-broken` (`server` entry throws → `PluginStatus.error`) and `e2e-needs-req` (`requires.piExtensions: ["pi-e2e-absent-extension"]` → `missingRequirements`); and `e2e-dependent` (`dependsOn: ["e2e-needs-req"]` → disabling the dependency raises the cascade-confirm dialog; no monorepo plugin declares `dependsOn`, so the cascade path is otherwise unreachable at L3). All three claim `settings-section` so they earn a plugin page + nav child. Each FILE is guarded by its own `-f` check so a partial fixture heals on the next run. Idempotent (each skips when its package.json is present). Consumed by tests/e2e/plugin-settings-pages.spec.ts. See change: plugin-settings-pages. PI_E2E_SEED also stages `qa/fixtures/e2e-notify.ext.ts` → `~/.pi/agent/extensions/e2e-notify/` (the `ctx.ui.notify` L3 driver). `register_flows` now pins `/app/packages/extension` FIRST in `settings.json#packages` — tool-name precedence is FIRST-registration-wins, and pi-flows' own `ask_user` ({question,type}) otherwise shadows the dashboard's ({method,title}), failing every faux ask scenario. See change: split-notify-from-prompt-request. `PI_E2E_OAUTH=1` (opt-in; unset = harness unchanged): before base entrypoint + daemon launch, seeds `auth.providers.github` (`e2e-client-id`/`e2e-client-secret`) + `auth.secret` + `bypassUrls:["/"]` + `redirectBaseUrl` (`PI_E2E_OAUTH_BASE`, default `https://pi-e2e-a.example.com`) into config.json. MUST be before launch: `registerAuthPlugin` returns early on empty boot provider registry → no /auth/* route, no `_reloadAuth` (design D6). Spec cannot seed itself: pi-state is RAM-backed tmpfs, every container start hands server fresh empty ~/.pi and discards `PUT /api/config` writes. github = only built-in provider resolving with NO network I/O (static endpoints, no OIDC discovery) → works offline. `bypassUrls:["/"]` mandatory: Playwright-host requests arrive NON-loopback; prefix matches every URL so armed gate denies nothing on SHARED harness. Consumed by tests/e2e/oauth-redirect-base.spec.ts; set by tests/e2e/global-setup.ts (`PI_E2E_OAUTH: process.env.PI_E2E_OAUTH ?? "1"`). See change: config-override-oauth-redirect-base. `register_subagents` registers the baked `pi-dashboard-subagents` into `settings.json#packages` + a `node_modules` symlink under SESSION_CWD. Runs UNCONDITIONALLY inside the PI_TEST_PEERS block (not per-arm): collapse scenarios need the tick producer in every peer shape, and it registers no `ask_user`, so it cannot disturb the first-registration-wins precedence `register_flows` guards (bridge stays pinned FIRST). Adds one more TS extension to the per-session jiti compile set — on a COLD cache (e.g. after `docker volume prune`) the first turn can exceed a spec's 30 s budget; the existing background `pi --list-models` warm covers it, so let the warm finish before driving the UI. See change: collapse-superseded-tool-execution-updates. `PI_SYNTH_AGENT_TICKS=1` stages `qa/fixtures/faux-agent-ticks.ext.ts` → `~/.pi/agent/extensions/faux-agent-ticks/` (synthetic `Agent`-tick producer for the throttle cadence L3 rows) and SKIPS `register_subagents` — the synthetic `Agent` tool owns the tool name (first-registration-wins) and the two never coexist. See change: reduce-bridge-tick-bandwidth.
3 changes: 3 additions & 0 deletions docker/test-up.sh.AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# test-up.sh — index

Spin-up. Sources lib-ports.sh. Exports `HOST_CWD=$PWD`. Derives stable port pair + `COMPOSE_PROJECT_NAME` from HOST_CWD hash. Honours `DASHBOARD_PORT`+`PI_GATEWAY_PORT` override as pair (exactly one set = exit 1). Idempotent re-up reuses ports when project containers run. Exports `TEST_IMAGE_TAG=$COMPOSE_PROJECT_NAME` (per-worktree baked image tag; falls back `:local`). Writes `${HOST_CWD}/.pi-test-harness.json`. Sets `PI_GATEWAY_BIND=127.0.0.1`, `TUNNEL_ENABLED=false`. Runs `docker compose -p "$COMPOSE_PROJECT_NAME" -f compose.yml -f compose.test.yml up`, wrapped in a bounded bind-collision retry (MAX_BIND_RETRIES=5, re-derives port pair on `port is already allocated`; skipped when ports pinned verbatim). Layers `compose.test.cap.yml` only in overlay mode; omits when `TEST_COPY_MODE=1`. Banner shows chosen URL + project. Exports `PI_TEST_PEERS="${PI_TEST_PEERS:-}"` (flow-plugin e2e peer selector) passed through to the container. See change: docker-test-harness. See change: parallelize-test-harness. See change: add-flow-plugin-e2e-tests.
Loading
Loading