11name : CI
22
33on :
4+ pull_request :
45 push :
56 branches : [master, main]
6- pull_request :
7+ schedule :
8+ # Nightly full pipeline: complete unit test suite, Docker e2e, and host
9+ # behavior e2e for both harnesses.
10+ - cron : ' 37 2 * * *'
711
812env :
913 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 : true
1014
11- # Pipeline shape on every push to master and on every PR:
15+ # Two lanes:
16+ #
17+ # PR FAST LANE (every pull_request) — one consolidated job, target <2 min.
18+ # The repo has a single self-hosted runner, so separate jobs would queue
19+ # behind each other and multiply the wall time; everything runs inside one
20+ # job instead:
21+ #
22+ # pr-fast-gate:
23+ # 1. biome lint on all workspace packages (plugin, pi-plugin, cli)
24+ # 2. plugin fast unit subset (src/config + src/agents — schema guards,
25+ # permission model, project-security, removed-config-keys drift)
26+ # 3. pi-plugin full unit tests (fast, ~40s measured)
27+ # 4. cli unit tests
28+ # 5. dashboard typecheck + lint + test + frontend build
29+ # (config-parity gate: schema/manifest drift fails here)
30+ # 6. plugin + pi-plugin + cli builds
1231#
13- # check-plugin, check-pi-plugin (unit tests + lint + typecheck, parallel)
32+ # Typecheck on all packages (~2-4 min on this box) and the full plugin unit
33+ # suite (~570s) move OFF the PR path — they run in the full lane. Lint and
34+ # the builds stay: they are the cheap, high-signal packaging gates.
35+ #
36+ # FULL LANE (push to master/main + nightly) — everything that used to gate
37+ # every PR, with the same gating structure as before:
38+ #
39+ # check-plugin, check-pi-plugin (full unit + lint + typecheck, parallel)
1440# ↓
1541# e2e-opencode, e2e-pi (Docker install + smoke, parallel — gated by unit)
1642# ↓
2349# historian publish behavior, tag-owner collision, synthetic todowrite, cross-harness memory,
2450# etc. Spawns real `opencode serve` / Pi subprocesses against an embedded mock provider.
2551# Catches cache-stability + correctness regressions that the smoke layer cannot see.
26- #
27- # Docker e2e was previously in a separate workflow (e2e-docker.yml). Folding it here means
28- # every PR and master push exercises the full unit → Docker → host gauntlet.
52+
53+ concurrency :
54+ group : ci-${{ github.workflow }}-${{ github.ref }}
55+ cancel-in-progress : true
2956
3057jobs :
31- check-plugin :
32- name : Check (plugin)
58+ pr-fast-gate :
59+ name : PR fast gate
3360 runs-on : self-hosted
61+ if : github.event_name == 'pull_request'
62+ timeout-minutes : 10
3463 steps :
3564 - uses : actions/checkout@v5
3665
@@ -41,21 +70,46 @@ jobs:
4170 - name : Install dependencies
4271 run : bun install
4372
44- - name : TypeScript typecheck
45- run : bun run typecheck
46-
47- - name : Lint
73+ # Lint every workspace package first — cheapest high-signal gate.
74+ - name : Lint (all packages)
4875 run : bun run lint
4976
50- - name : Build
51- run : bun run build
77+ # Fast unit subset: src/config + src/agents carry the config-schema
78+ # and permission-model guards (incl. project-security and
79+ # removed-config-keys drift checks) — the regressions a PR can most
80+ # plausibly introduce. Full unit suite runs in the full lane.
81+ - name : Plugin unit tests (fast subset)
82+ run : bun run --cwd packages/plugin test src/config src/agents
5283
53- - name : Test
54- run : bun run test
84+ - name : Pi-plugin unit tests
85+ run : bun run --cwd packages/pi-plugin test
5586
56- check-pi-plugin :
57- name : Check (pi-plugin)
87+ - name : CLI unit tests
88+ run : bun run --cwd packages/cli test
89+
90+ # Dashboard: config-parity test fails the PR if the plugin config
91+ # schema drifts from the dashboard ConfigEditor coverage manifest.
92+ - name : Dashboard typecheck
93+ run : bun run --cwd packages/dashboard typecheck
94+
95+ - name : Dashboard lint
96+ run : bun run --cwd packages/dashboard lint
97+
98+ - name : Dashboard tests (config parity)
99+ run : bun run --cwd packages/dashboard test
100+
101+ - name : Dashboard build (frontend)
102+ run : bun run --cwd packages/dashboard build
103+
104+ # Packaging gate: all three shippable packages must build.
105+ - name : Build (plugin + pi-plugin + cli)
106+ run : bun run build
107+
108+ check-plugin :
109+ name : Check (plugin)
58110 runs-on : self-hosted
111+ if : github.event_name != 'pull_request'
112+ timeout-minutes : 25
59113 steps :
60114 - uses : actions/checkout@v5
61115
@@ -67,20 +121,25 @@ jobs:
67121 run : bun install
68122
69123 - name : TypeScript typecheck
70- run : bun run --cwd packages/pi-plugin typecheck
124+ run : bun run typecheck
71125
72126 - name : Lint
73- run : bun run --cwd packages/pi-plugin lint
127+ run : bun run lint
74128
75129 - name : Build
76- run : bun run --cwd packages/pi-plugin build
130+ run : bun run build
77131
132+ # Full workspace unit suite (plugin + pi-plugin + cli) — the plugin
133+ # suite alone measured ~570s, which is why it lives here and not on
134+ # the PR fast lane.
78135 - name : Test
79- run : bun run --cwd packages/pi-plugin test
136+ run : bun run test
80137
81- check-dashboard :
82- name : Check (dashboard )
138+ check-pi-plugin :
139+ name : Check (pi-plugin )
83140 runs-on : self-hosted
141+ if : github.event_name != 'pull_request'
142+ timeout-minutes : 25
84143 steps :
85144 - uses : actions/checkout@v5
86145
@@ -91,27 +150,23 @@ jobs:
91150 - name : Install dependencies
92151 run : bun install
93152
94- # Frontend-only checks (no Rust/Tauri needed). The key gate is the test
95- # step, which runs config-parity.test.ts — it fails the build if the
96- # plugin config schema gains/renames/removes a field the dashboard's
97- # ConfigEditor coverage manifest doesn't account for, so the form can't
98- # silently drift out of sync with the schema again.
99153 - name : TypeScript typecheck
100- run : bun run --cwd packages/dashboard typecheck
154+ run : bun run --cwd packages/pi-plugin typecheck
101155
102156 - name : Lint
103- run : bun run --cwd packages/dashboard lint
157+ run : bun run --cwd packages/pi-plugin lint
104158
105- - name : Test
106- run : bun run --cwd packages/dashboard test
159+ - name : Build
160+ run : bun run --cwd packages/pi-plugin build
107161
108- - name : Build (frontend)
109- run : bun run --cwd packages/dashboard build
162+ - name : Test
163+ run : bun run --cwd packages/pi-plugin test
110164
111165 e2e-opencode :
112166 name : E2E (OpenCode, Docker)
113167 runs-on : self-hosted
114168 needs : [check-plugin]
169+ if : github.event_name != 'pull_request'
115170 timeout-minutes : 25
116171 steps :
117172 - uses : actions/checkout@v5
@@ -145,6 +200,7 @@ jobs:
145200 name : E2E (Pi, Docker)
146201 runs-on : self-hosted
147202 needs : [check-pi-plugin]
203+ if : github.event_name != 'pull_request'
148204 timeout-minutes : 25
149205 steps :
150206 - uses : actions/checkout@v5
@@ -184,6 +240,7 @@ jobs:
184240 # Gated on Docker e2e: no point exercising the deep behavior suite if
185241 # the simpler install+smoke path is broken.
186242 needs : [e2e-opencode]
243+ if : github.event_name != 'pull_request'
187244 # 90m: on the shared self-hosted box the SDK's per-call latency is
188245 # amplified ~300x under co-tenant load (measured: session.create 100ms
189246 # quiet vs 31s loaded). Test-level timeouts are 300s; the job budget
@@ -249,6 +306,7 @@ jobs:
249306 name : E2E (Pi, host behavior)
250307 runs-on : self-hosted
251308 needs : [e2e-pi]
309+ if : github.event_name != 'pull_request'
252310 timeout-minutes : 40
253311 steps :
254312 - uses : actions/checkout@v5
0 commit comments