Skip to content

Commit bfb9c6d

Browse files
committed
Merge origin/main into claude/org-creation-http-500-teams-gocypm
Picks up #3666's CI caching fix ('stop PR-side cache churn'), which the workflow used by this PR's run predates — the run stalled for 50+ minutes on 'Run affected tests' across two attempts on separate runners.
2 parents 553f1bf + 984396b commit bfb9c6d

12 files changed

Lines changed: 748 additions & 50 deletions

File tree

.changeset/auth-route-ledger.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@objectstack/plugin-auth": patch
3+
"@objectstack/client": patch
4+
---
5+
6+
test(plugin-auth): enumerate better-auth's route table — the `/auth/**` wildcard becomes 55 exact rows (#3656)
7+
8+
The widest hole the #3642 capstone measured. That guard reports how many SDK
9+
calls match only a `**` prefix family rather than a resolvable route, and the
10+
answer was 60 of ~196 — with 54 on `* /auth/**`, the largest and most
11+
security-relevant namespace in the client. `auth.me` builds
12+
`/api/v1/auth/get-session`; a prefix claim cannot tell you better-auth still
13+
calls it that, and better-auth is a third-party dependency on its own release
14+
cadence (this repo already chased its 1.7 column drift in #3624 / #3647).
15+
16+
`plugin-auth` mounts it with a single catch-all, so there are no per-route
17+
registration calls to capture the way tranche 3 captured
18+
`registerStorageRoutes`. The seam is `auth.api`: every better-auth endpoint
19+
carries `.path` and `.options.method`, so a live instance is the route table.
20+
21+
`auth-route-ledger.ts` reads it, in two halves checked differently on purpose:
22+
23+
- **55 reviewed rows** — every route the SDK calls, each naming its client
24+
method, checked strictly against the live table. This is the rename detector.
25+
- **129-path mounted-surface inventory** — checked for exact equality both
26+
ways, so a version bump that adds publicly-mounted auth endpoints becomes a
27+
reviewable CI diff. Machine-maintained rather than reviewed prose: demanding
28+
a rationale for all 129 would make every better-auth upgrade a hundred-row
29+
review and the ledger would rot into rubber-stamping.
30+
31+
Enumeration is config-dependent, so the inventory is pinned at the
32+
configuration enabling every plugin the SDK targets — the maximal surface —
33+
with the participating `OS_*` env vars cleared so a developer's shell cannot
34+
produce a spurious diff. Mutation-checked: renaming a ledgered route fails the
35+
suite naming it.
36+
37+
The capstone guard now includes this ledger in its union and prefers exact rows
38+
over wildcard families when matching — without that ordering fix every
39+
`/auth/*` URL would still have been absorbed by `* /auth/**` and the new ledger
40+
would have changed nothing. Wildcard-only matches fall **60 → 3**; the ratchet
41+
moves with them. What remains is `* /ai/**`, whose routes `service-ai` builds
42+
at plugin start.
43+
44+
No runtime change: a ledger, a guard, and the header/audit-doc notes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
---
3+
4+
CI-only: turbo cache for the Release job, console-dist cache keyed on the objectui pin, restore-only turbo caches on PRs (main seeds them), CodeQL off pull_request (main push + weekly schedule), spec coverage moved to a nightly workflow. Releases nothing.

.github/workflows/ci.yml

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,13 @@ jobs:
111111
restore-keys: |
112112
${{ runner.os }}-pnpm-store-v3-
113113
114-
- name: Setup Turbo cache
115-
uses: actions/cache@v6
114+
# Restore-only on PRs: PR-side saves (~5 turbo entries per push) churned
115+
# the repo's 10 GB Actions cache pool and evicted the main-branch seeds —
116+
# observed as sudden cold-cache spikes (Build Core 51s → 4m30s). PRs fall
117+
# back to main's entries via the prefix restore-keys; only main pushes
118+
# save (the "Save Turbo cache" step at the end of the job).
119+
- name: Restore Turbo cache
120+
uses: actions/cache/restore@v6
116121
with:
117122
path: .turbo/cache
118123
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
@@ -142,25 +147,25 @@ jobs:
142147
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }}
143148
run: pnpm turbo run test --affected --filter=!@objectstack/dogfood --concurrency=4
144149

145-
# Push to main: full run, but exclude spec's plain test task — the
146-
# coverage step below executes the exact same 250-file spec suite once,
147-
# with coverage. Previously the spec suite ran twice per push. Dogfood is
148-
# excluded for the same reason as the PR step: the Dogfood job runs it.
150+
# Push to main: full run. Spec's suite runs here plain (uninstrumented);
151+
# the coverage-instrumented pass moved to the nightly Spec Coverage
152+
# workflow (coverage-nightly.yml) — instrumentation added minutes to
153+
# every main push for a trend artifact that is consulted occasionally at
154+
# best. Dogfood is excluded for the same reason as the PR step: the
155+
# Dogfood job runs it.
149156
- name: Run all tests (push)
150157
if: github.event_name == 'push'
151-
run: pnpm turbo run test --filter=!@objectstack/spec --filter=!@objectstack/dogfood --concurrency=4
152-
153-
- name: Generate coverage report
154-
if: github.event_name == 'push'
155-
run: pnpm --filter @objectstack/spec test:coverage
156-
157-
- name: Upload coverage reports
158-
if: github.event_name == 'push'
159-
uses: actions/upload-artifact@v7
158+
run: pnpm turbo run test --filter=!@objectstack/dogfood --concurrency=4
159+
160+
# Seed the shared Turbo cache from main only (see the restore step
161+
# above). always(): keep the seed fresh even when a test fails, matching
162+
# the old actions/cache post-step behavior.
163+
- name: Save Turbo cache (main only)
164+
if: always() && github.event_name == 'push'
165+
uses: actions/cache/save@v6
160166
with:
161-
name: coverage-report
162-
path: packages/spec/coverage/
163-
retention-days: 30
167+
path: .turbo/cache
168+
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
164169

165170
dogfood:
166171
# Sharded 2-way: the suite is ~60 independent test files, each booting its
@@ -209,10 +214,11 @@ jobs:
209214
${{ runner.os }}-pnpm-store-v3-
210215
211216
# Shard-scoped key: the turbo test hash differs per shard (pass-through
212-
# args are part of the task hash), and two same-named matrix jobs would
213-
# otherwise race to save one cache entry per sha.
214-
- name: Setup Turbo cache
215-
uses: actions/cache@v6
217+
# args are part of the task hash). Restore-only on PRs — see the Restore
218+
# Turbo cache comment in the test job; the save step at the end of this
219+
# job seeds from main only.
220+
- name: Restore Turbo cache
221+
uses: actions/cache/restore@v6
216222
with:
217223
path: .turbo/cache
218224
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ matrix.shard }}-${{ github.ref_name }}-${{ github.sha }}
@@ -247,6 +253,15 @@ jobs:
247253
echo "::endgroup::"
248254
done
249255
256+
# Seed the shared Turbo cache from main only (see the restore step
257+
# above); shard-scoped key so the two matrix jobs don't collide.
258+
- name: Save Turbo cache (main only)
259+
if: always() && github.event_name == 'push'
260+
uses: actions/cache/save@v6
261+
with:
262+
path: .turbo/cache
263+
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ matrix.shard }}-${{ github.ref_name }}-${{ github.sha }}
264+
250265
dogfood-gate:
251266
# Stable required-check name for a SHARDED job (#3622 follow-up).
252267
#
@@ -312,8 +327,13 @@ jobs:
312327
restore-keys: |
313328
${{ runner.os }}-pnpm-store-v3-
314329
315-
- name: Setup Turbo cache
316-
uses: actions/cache@v6
330+
# Restore-only on PRs: PR-side saves (~5 turbo entries per push) churned
331+
# the repo's 10 GB Actions cache pool and evicted the main-branch seeds —
332+
# observed as sudden cold-cache spikes (Build Core 51s → 4m30s). PRs fall
333+
# back to main's entries via the prefix restore-keys; only main pushes
334+
# save (the "Save Turbo cache" step at the end of the job).
335+
- name: Restore Turbo cache
336+
uses: actions/cache/restore@v6
317337
with:
318338
path: .turbo/cache
319339
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
@@ -377,6 +397,14 @@ jobs:
377397
packages/spec/json-schema/
378398
retention-days: 30
379399

400+
# Seed the shared Turbo cache from main only (see the restore step above).
401+
- name: Save Turbo cache (main only)
402+
if: always() && github.event_name == 'push'
403+
uses: actions/cache/save@v6
404+
with:
405+
path: .turbo/cache
406+
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
407+
380408
build-docs:
381409
name: Build Docs
382410
needs: filter

.github/workflows/codeql.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
name: CodeQL Security Analysis
22

3+
# Deliberately NOT on pull_request: the analysis took ~4½ runner-minutes per
4+
# PR sync, and everything a PR introduces is analyzed on main within minutes
5+
# of the merge anyway (multi-agent merge cadence). Alerts surface on main's
6+
# runs + the weekly deep pass instead of blocking/occupying PR runners.
7+
# If branch protection ever required the "Analyze (javascript)" check, that
8+
# requirement must be dropped along with this trigger.
39
on:
410
push:
511
branches:
612
- main
7-
pull_request:
8-
branches:
9-
- main
1013
schedule:
1114
# Run at 02:00 UTC every Monday
1215
- cron: '0 2 * * 1'
1316

14-
# A superseded analysis of an outdated commit has no value — cancel it. PR runs
15-
# group by PR number; push/schedule runs group by ref, so a newer main push
16-
# cancels only an in-flight main analysis (the newer commit gets analyzed).
17+
# A superseded analysis of an outdated commit has no value — cancel it. A newer
18+
# main push cancels only an in-flight main analysis (the newer commit still
19+
# gets analyzed).
1720
concurrency:
18-
group: codeql-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
21+
group: codeql-${{ github.workflow }}-${{ github.ref }}
1922
cancel-in-progress: true
2023

2124
jobs:
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Spec coverage, nightly. Moved off the per-push path: the coverage-
2+
# instrumented spec suite added minutes of instrumentation overhead to every
3+
# main push (ci.yml previously excluded spec's plain test task and ran this
4+
# instead), for a trend artifact that is consulted occasionally at best. The
5+
# plain (uninstrumented) spec suite still runs on every main push in ci.yml's
6+
# Test Core; this workflow owns the coverage report.
7+
name: Spec Coverage
8+
9+
on:
10+
workflow_dispatch:
11+
schedule:
12+
- cron: '0 5 * * *' # 05:00 UTC nightly
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
coverage:
19+
name: Spec coverage report
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 30
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v7
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@v7
28+
with:
29+
node-version: '20'
30+
31+
- name: Enable Corepack
32+
run: corepack enable
33+
34+
- name: Get pnpm store directory
35+
shell: bash
36+
run: |
37+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
38+
39+
# Restore-only: scheduled runs read main's store cache; the per-push
40+
# workflows own saving it.
41+
- name: Restore pnpm cache
42+
uses: actions/cache/restore@v6
43+
with:
44+
path: ${{ env.STORE_PATH }}
45+
key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }}
46+
restore-keys: |
47+
${{ runner.os }}-pnpm-store-v3-
48+
49+
- name: Install dependencies
50+
run: pnpm install --frozen-lockfile
51+
52+
# The spec package sits at the root of the workspace graph (no workspace
53+
# deps) and its suite reads src/ directly, so no build step is needed —
54+
# same reason lint.yml's typecheck gates run pre-build.
55+
- name: Generate coverage report
56+
run: pnpm --filter @objectstack/spec test:coverage
57+
58+
- name: Upload coverage report
59+
uses: actions/upload-artifact@v7
60+
with:
61+
name: coverage-report
62+
path: packages/spec/coverage/
63+
retention-days: 30

.github/workflows/lint.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ jobs:
145145
# package from scratch on every run (~4½ min) while ci.yml's jobs — which
146146
# do carry this cache — finished the same build in under a minute. Same
147147
# key scheme as ci.yml so the fallback prefix can also hit main's caches.
148-
- name: Setup Turbo cache
149-
uses: actions/cache@v6
148+
# Restore-only on PRs (same policy as ci.yml): PR-side saves churned the
149+
# 10 GB Actions cache pool and evicted the main seeds; only main pushes
150+
# save (the "Save Turbo cache" step at the end of the job).
151+
- name: Restore Turbo cache
152+
uses: actions/cache/restore@v6
150153
with:
151154
path: .turbo/cache
152155
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
@@ -243,3 +246,11 @@ jobs:
243246
# so it runs after the build step alongside the other consumer gates.
244247
- name: Check skills TypeScript examples compile
245248
run: pnpm --filter @objectstack/spec run check:skill-examples
249+
250+
# Seed the shared Turbo cache from main only (see the restore step above).
251+
- name: Save Turbo cache (main only)
252+
if: always() && github.event_name == 'push'
253+
uses: actions/cache/save@v6
254+
with:
255+
path: .turbo/cache
256+
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}

.github/workflows/release.yml

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ jobs:
4848
restore-keys: |
4949
${{ runner.os }}-pnpm-store-v3-
5050
51+
# This job ran the full workspace build cold on every main push (~4½
52+
# min) — the same omission lint.yml's typecheck job had. Same key scheme
53+
# as ci.yml. Runs only on main pushes, so plain save+restore is right
54+
# here (this job is one of the cache seeders).
55+
- name: Setup Turbo cache
56+
uses: actions/cache@v6
57+
with:
58+
path: .turbo/cache
59+
key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
60+
restore-keys: |
61+
${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-
62+
${{ runner.os }}-turbo-${{ github.job }}-
63+
5164
- name: Install dependencies
5265
run: pnpm install --frozen-lockfile
5366

@@ -57,13 +70,34 @@ jobs:
5770
- name: Build
5871
run: pnpm run build
5972

73+
# The vendored Console dist is a pure function of the objectui pin (and
74+
# of the build script itself) — yet it was cloned + vite-built from
75+
# scratch on every main push (~3 min). Cache the finished dist keyed on
76+
# exactly those inputs; the stamp check below still verifies whatever
77+
# dist ends up in place, restored or fresh.
78+
- name: Cache vendored Console dist (keyed on the objectui pin)
79+
id: console-dist-cache
80+
uses: actions/cache@v6
81+
with:
82+
path: packages/console/dist
83+
key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}
84+
6085
- name: Build vendored @objectstack/console SPA
6186
# Clones objectstack-ai/objectui at the SHA pinned in .objectui-sha,
6287
# builds @object-ui/console, and copies dist/ into
6388
# packages/console/dist/. Must run before publish so the
64-
# prepublishOnly guard in @objectstack/console passes.
89+
# prepublishOnly guard in @objectstack/console passes. Skipped when the
90+
# cache above restored a dist built from the same pin.
91+
if: steps.console-dist-cache.outputs.cache-hit != 'true'
6592
run: bash scripts/build-console.sh
6693

94+
# Belt-and-suspenders for the cache path: build-console.sh stamps the
95+
# objectui SHA it built from into dist/.objectui-sha; check:console-sha
96+
# fails loudly if the dist in place (restored or fresh) drifts from the
97+
# pin.
98+
- name: Verify Console dist stamp matches pin
99+
run: pnpm check:console-sha
100+
67101
- name: Downstream backward-compat smoke (live hotcrm)
68102
# Pre-publish gate (#2035): the about-to-publish @objectstack/spec must
69103
# not break a real third-party consumer pinned to a published release.

0 commit comments

Comments
 (0)