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
4 changes: 2 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dist-ssr
coverage
.git
.claude
# The self-host bundle's only entry point is src/server.ts (scripts/build-selfhost.mjs) and `npm ci`
# The self-host bundle's only entry point is src/server.ts (scripts/build-selfhost.ts) and `npm ci`
# only ever sees the root package*.json (copied before the rest of the tree) — the loopover-ui
# workspace app and the test suite are never read during the image build, so keep both out of the
# build context entirely (measured: ~11MB of this repo's ~22MB tracked-file footprint).
Expand All @@ -26,7 +26,7 @@ auth.json
**/auth.json
# The review-enrichment service (REES) is a separate Railway service with its own Dockerfile — keep it out of
# the engine image. EXCEPT analyzer-metadata.json: the main engine's own code (src/review/enrichment-analyzers-
# taxonomy.ts) imports it directly, so excluding it wholesale breaks `scripts/build-selfhost.mjs`'s esbuild
# taxonomy.ts) imports it directly, so excluding it wholesale breaks `scripts/build-selfhost.ts`'s esbuild
# bundle (module resolution failure at build time, not a runtime gap -- caught by the "build + boot smoke test"
# workflow, which is path-gated and doesn't run on every PR).
review-enrichment
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,14 @@ jobs:
# and the pack check only inspects the tarball. Root src/ cannot
# affect it, so it is intentionally NOT a trigger here.
- 'packages/loopover-mcp/**'
- 'scripts/check-mcp-package.mjs'
- 'scripts/check-mcp-package.ts'
- 'package-lock.json'
engine:
- 'packages/loopover-engine/**'
- 'package-lock.json'
miner:
- 'packages/loopover-miner/**'
- 'scripts/check-miner-package.mjs'
- 'scripts/check-miner-package.ts'
- 'package-lock.json'
# No dedicated build/pack script (unlike mcp/engine/miner above) -- discovery-index is a normal
# vitest-covered workspace package (packages/discovery-index/src/**/*.ts is in vitest.config.ts's
Expand Down Expand Up @@ -217,7 +217,7 @@ jobs:
- 'control-plane/**'
- '.github/workflows/ci.yml'
# Both miner-package test files are self-contained w.r.t. root src/**, the same trust boundary as
# mcpCliHarness above: check-miner-package.test.ts only spawns scripts/check-miner-package.mjs as a
# mcpCliHarness above: check-miner-package.test.ts only spawns scripts/check-miner-package.ts as a
# real subprocess (node:child_process + vitest, nothing else), and miner-calibration-types.test.ts
# only imports the scaffolded packages/loopover-miner/lib/calibration.ts -- neither ever loads
# root src/ in-process. Mirrors mcpCliHarness's filter shape for the same reason (tooling/config
Expand Down Expand Up @@ -927,7 +927,7 @@ jobs:
# across every push-to-main run). key never actually matches (no save ever uses this literal
# string); it exists only so the step always falls through to the restore-keys prefix match,
# picking whatever the most recent refresh wrote. A cache MISS here is always safe: see
# scripts/compute-test-shards.mjs's fallback -- it splits evenly across shards when no timing
# scripts/compute-test-shards.ts's fallback -- it splits evenly across shards when no timing
# data is available for a file (or none at all), the same balance vitest's own --shard already
# gives today, so this can only make shard balance better than today's baseline, never worse.
- name: Restore test timing cache
Expand Down Expand Up @@ -1036,11 +1036,11 @@ jobs:
# shards, every run sampled. Deliberately not applied to the scoped-selection branch above:
# that file set isn't known until vitest resolves --changed itself, so a precomputed
# assignment can't cover it without duplicating vitest's own dependency-graph resolution here.
# compute-test-shards.mjs enforces its own hard invariant (the union of all 3 shards' files
# compute-test-shards.ts enforces its own hard invariant (the union of all 3 shards' files
# must exactly equal the discovered file set, no file missing or duplicated) and refuses to
# write output at all if that's ever violated, so a bug here fails this step loudly rather
# than silently dropping a test file from CI.
node scripts/compute-test-shards.mjs --shards=3 --timing=test-timing.json --output=shard-assignment.json
node --experimental-strip-types scripts/compute-test-shards.ts --shards=3 --timing=test-timing.json --output=shard-assignment.json
mapfile -t SHARD_FILES < <(node -e "console.log(JSON.parse(require('fs').readFileSync('shard-assignment.json','utf8'))['${{ matrix.shard }}'].join('\n'))")
npm run test:coverage -- --maxWorkers=4 "${SHARD_FILES[@]}" --reporter=default --reporter=blob --reporter=junit --outputFile.blob=blob-report/report-${{ matrix.shard }}.blob --outputFile.junit=reports/junit/vitest.xml "${EXCLUDE_ARGS[@]}"
fi
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/orb-beta-release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Automated ORB (self-host container image, ghcr.io/jsonbored/loopover-selfhost) beta channel.
# Daily (or on demand via workflow_dispatch), checks whether any image-relevant commit has landed
# since the last orb-v tag (scripts/check-orb-release-due.mjs / scripts/orb-release-core.ts) and,
# since the last orb-v tag (scripts/check-orb-release-due.ts / scripts/orb-release-core.ts) and,
# if so, cuts the next `orb-vX.Y.Z-beta.N` tag and dispatches release-selfhost.yml to build + publish
# it -- fully unattended: that workflow's `environment:` routes an actual beta version to
# `release-beta` (no required reviewers), while a stable/rc version still requires the human-gated
Expand Down Expand Up @@ -40,11 +40,11 @@ jobs:

- name: Check whether an ORB beta is due
id: report
# check-orb-release-due.mjs imports orb-release-core.ts directly, so it needs tsx (not plain node) to
# resolve that local .ts import.
# check-orb-release-due.ts imports orb-release-core.ts directly via a `.js` specifier, so it needs tsx
# (not plain node) to resolve that local .ts import.
run: |
set -euo pipefail
npx tsx scripts/check-orb-release-due.mjs --json --output orb-release-due.json
npx tsx scripts/check-orb-release-due.ts --json --output orb-release-due.json
node <<'NODE'
const fs = require("node:fs");
const report = JSON.parse(fs.readFileSync("orb-release-due.json", "utf8"));
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/orb-stable-release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# doesn't fit release-please's directory-component model the way packages/loopover-mcp and
# packages/loopover-engine do (see mcp-release-please.yml). Same UX contract as those, hand-rolled: on the
# same schedule (or on demand), (re)compute the next stable version from conventional commits since the last
# STABLE orb-v tag (scripts/check-orb-stable-release-due.mjs / orb-release-core.ts's buildOrbStableReleaseReport)
# STABLE orb-v tag (scripts/check-orb-stable-release-due.ts / orb-release-core.ts's buildOrbStableReleaseReport)
# and keep a standing `release-orb-stable` branch + PR in sync with that proposal. Nothing ships until a
# maintainer reviews and merges it -- see orb-stable-release-tag.yml for what happens then. Never touches the
# daily fully-unattended beta channel (orb-beta-release.yml).
Expand Down Expand Up @@ -39,11 +39,11 @@ jobs:

- name: Check whether a stable ORB release is due
id: report
# check-orb-stable-release-due.mjs imports orb-release-core.ts directly, so it needs tsx (not plain
# node) to resolve that local .ts import.
# check-orb-stable-release-due.ts imports orb-release-core.ts directly via a `.js` specifier, so it
# needs tsx (not plain node) to resolve that local .ts import.
run: |
set -euo pipefail
npx tsx scripts/check-orb-stable-release-due.mjs --json --output orb-stable-release-due.json
npx tsx scripts/check-orb-stable-release-due.ts --json --output orb-stable-release-due.json
node <<'NODE'
const fs = require("node:fs");
const report = JSON.parse(fs.readFileSync("orb-stable-release-due.json", "utf8"));
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-miner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
run: npm run build --workspace @loopover/miner

# Reuses the exact allowlist/required-files/forbidden-content check test:ci already runs on
# every PR (scripts/check-miner-package.mjs) -- a dry-run pack, so it doesn't produce the real
# every PR (scripts/check-miner-package.ts) -- a dry-run pack, so it doesn't produce the real
# tarball this job packs+uploads below.
- name: Validate packed file list
run: npm run test:miner-pack
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-selfhost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
run: npm run build --workspace @loopover/engine

- name: Build self-host bundle for release
run: node scripts/build-selfhost.mjs --all
run: node --experimental-strip-types scripts/build-selfhost.ts --all

- name: Validate release source map
run: node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/selfhost.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Self-host stack CI (#980/#982). Provides integration coverage the main CI can't:
# 1. Postgres integration test — needs a real PG service container
# 2. Self-host bundle build validation (build-selfhost.mjs)
# 2. Self-host bundle build validation (build-selfhost.ts)
# 3. Docker image build + container smoke test (/health, /ready, /metrics)
# Unit tests and typecheck are NOT duplicated here — the main CI validate job covers them.
name: self-host
Expand All @@ -11,7 +11,7 @@ on:
paths:
- "src/selfhost/**"
- "src/server.ts"
- "scripts/build-selfhost.mjs"
- "scripts/build-selfhost.ts"
- "scripts/validate-selfhost-sourcemap.ts"
- "Dockerfile"
- "docker-compose.yml"
Expand All @@ -27,7 +27,7 @@ on:
paths:
- "src/selfhost/**"
- "src/server.ts"
- "scripts/build-selfhost.mjs"
- "scripts/build-selfhost.ts"
- "scripts/validate-selfhost-sourcemap.ts"
- "Dockerfile"
- "docker-compose.yml"
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:
run: PG_TEST_URL=postgres://postgres:devpw@localhost:5432/loopover npx vitest run test/integration/selfhost-pg.test.ts

- name: Build the self-host bundle
run: node scripts/build-selfhost.mjs
run: node --experimental-strip-types scripts/build-selfhost.ts

- name: Validate self-host source map
run: node scripts/validate-selfhost-sourcemap.ts
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-timing-refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Test timing refresh

# Pulls per-test-file historical duration data from Codecov's Test Analytics API (see
# scripts/fetch-test-timing.ts) and caches it for validate-tests' duration-aware shard bin-packer
# (scripts/compute-test-shards.mjs, ci.yml's "Test with coverage" step) to consume. Runs on a schedule
# (scripts/compute-test-shards.ts, ci.yml's "Test with coverage" step) to consume. Runs on a schedule
# rather than per-PR: Codecov doesn't publish a numeric rate limit for this read endpoint, and this
# repo's PR volume (hundreds/day) makes "query fresh on every PR" a real risk of hitting one, for data
# that doesn't meaningfully change run-to-run anyway.
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN npm ci --ignore-scripts
RUN npm --workspace @loopover/engine run build
# --all: bundle every dependency into one self-contained dist/server.mjs, so the runtime image needs no
# node_modules (≈10× smaller). The bundle has zero `cloudflare:*` imports (stubbed at build), so no loader.
RUN node scripts/build-selfhost.mjs --all
RUN node --experimental-strip-types scripts/build-selfhost.ts --all
RUN node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts

# --- runtime base: slim, non-root -----------------------------------------------------------------------
Expand Down Expand Up @@ -69,7 +69,7 @@ ARG INSTALL_VISUAL_REVIEW=false
COPY package*.json ./
RUN if [ "$INSTALL_VISUAL_REVIEW" = "true" ]; then npm install puppeteer-core@22.13.1 --ignore-scripts; fi
# sharp (#4370): esbuild marks it `external` in the --all bundle (a native per-platform binary can't be
# bundled into dist/server.mjs, see scripts/build-selfhost.mjs), so it must be installed separately here,
# bundled into dist/server.mjs, see scripts/build-selfhost.ts), so it must be installed separately here,
# same reason as puppeteer-core above -- but unconditional (not behind an opt-in build-arg): it's a core
# dependency of the vision-image-downscale path, not an optional external-sidecar feature. --ignore-scripts
# is safe here: sharp's own platform binary ships as an npm `optionalDependencies` entry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,7 @@ scripts above pointed at an older target:
`deploy-selfhost-prebuilt.sh`.

<Callout variant="warn" title="Migrations are forward-only">
This repo has no down-migration convention — `scripts/check-migrations.mjs` only enforces a
This repo has no down-migration convention — `scripts/check-migrations.ts` only enforces a
contiguous, non-colliding numbering, not a reverse path. If a migration has already run forward
against the live database, rolling back the app code is **not safe in general**: older code can
break against a newer schema (a dropped/renamed column, a NOT NULL column it never writes, a
Expand Down
2 changes: 1 addition & 1 deletion apps/loopover-ui/content/docs/self-hosting-releases.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ docker compose up -d loopover`}
There is no dedicated rollback command. Roll back by re-running `scripts/deploy-selfhost-image.sh` pinned to the prior image tag or digest (or `scripts/deploy-selfhost-prebuilt.sh` against an older checkout) — the same script you upgrade with, pointed backward.

<Callout variant="warn" title="Migrations are forward-only">
This repo has no down-migration convention (`scripts/check-migrations.mjs` and `migrations/` only
This repo has no down-migration convention (`scripts/check-migrations.ts` and `migrations/` only
ever add forward). If a migration already ran forward before you need to roll back, reverting the
app image does not revert the schema — the rolled-back code now runs against a newer schema than
it expects. Keep backups and read release notes for migration changes before upgrading a live
Expand Down
16 changes: 8 additions & 8 deletions apps/loopover-ui/src/lib/selfhost-env-reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [
},
{
name: "OBSERVABILITY_SMOKE_POLL_MS",
firstReference: "scripts/smoke-observability-traces.mjs",
firstReference: "scripts/smoke-observability-traces.ts",
},
{
name: "OBSERVABILITY_SMOKE_TIMEOUT_MS",
firstReference: "scripts/smoke-observability-traces.mjs",
firstReference: "scripts/smoke-observability-traces.ts",
},
{
name: "OLLAMA_AI_API_KEY",
Expand Down Expand Up @@ -499,11 +499,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [
},
{
name: "SELFHOST_BUNDLE_ALL",
firstReference: "scripts/build-selfhost.mjs",
firstReference: "scripts/build-selfhost.ts",
},
{
name: "SELFHOST_SERVICE",
firstReference: "scripts/smoke-observability-traces.mjs",
firstReference: "scripts/smoke-observability-traces.ts",
},
{
name: "SELFHOST_SETUP_TOKEN",
Expand Down Expand Up @@ -617,8 +617,8 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [
"| `MAINTENANCE_ADMISSION_MAX_LIVE_PENDING` | `src/selfhost/maintenance-admission.ts` |",
"| `MAINTENANCE_ADMISSION_MAX_PENDING` | `src/selfhost/maintenance-admission.ts` |",
"| `MIGRATIONS_DIR` | `src/server.ts` |",
"| `OBSERVABILITY_SMOKE_POLL_MS` | `scripts/smoke-observability-traces.mjs` |",
"| `OBSERVABILITY_SMOKE_TIMEOUT_MS` | `scripts/smoke-observability-traces.mjs` |",
"| `OBSERVABILITY_SMOKE_POLL_MS` | `scripts/smoke-observability-traces.ts` |",
"| `OBSERVABILITY_SMOKE_TIMEOUT_MS` | `scripts/smoke-observability-traces.ts` |",
"| `OLLAMA_AI_API_KEY` | `src/selfhost/ai.ts` |",
"| `OLLAMA_AI_BASE_URL` | `src/selfhost/ai.ts` |",
"| `OLLAMA_AI_MODEL` | `src/selfhost/ai.ts` |",
Expand Down Expand Up @@ -665,8 +665,8 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [
"| `REVIEW_AUDIT_S3_ENDPOINT` | `src/server.ts` |",
"| `REVIEW_AUDIT_S3_REGION` | `src/server.ts` |",
"| `REVIEW_AUDIT_S3_SECRET_ACCESS_KEY` | `src/server.ts` |",
"| `SELFHOST_BUNDLE_ALL` | `scripts/build-selfhost.mjs` |",
"| `SELFHOST_SERVICE` | `scripts/smoke-observability-traces.mjs` |",
"| `SELFHOST_BUNDLE_ALL` | `scripts/build-selfhost.ts` |",
"| `SELFHOST_SERVICE` | `scripts/smoke-observability-traces.ts` |",
"| `SELFHOST_SETUP_TOKEN` | `src/selfhost/preflight.ts` |",
"| `SENTRY_DSN` | `src/selfhost/sentry.ts` |",
"| `SENTRY_ENVIRONMENT` | `src/selfhost/otel.ts` |",
Expand Down
Loading
Loading