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
2 changes: 2 additions & 0 deletions .kiro/steering/out-of-scope-backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,5 @@
| B59 | 2026-09-09 · review PR #458 (parity gate) | bug (CI guard không fire) | `.github/workflows/docs-i18n-sync.yml` · `scripts/docs-i18n/gate-changed-parity.mjs` | **Gate parity bỏ qua đúng ca nó cần bắt.** Workflow lấy danh sách file bằng `git diff --name-only --diff-filter=d`, mà `--diff-filter=d` **loại file bị xoá**. Một PR xoá riêng `docs/vi/x.md` để bản EN mồ côi nhưng **không cung cấp file doc nào** cho gate → danh sách rỗng → gate exit 0 và PR xanh. Rename cũng cùng lỗ, qua đường path cũ đã biến mất. Bản thân script **đã có** xử lý orphan từ đầu — nó chỉ không bao giờ nhận được path đó, nên đây là class "guard có mà không fire" giống B31/B36/B53/B56: đọc code script thì thấy đúng, đọc workflow mới thấy sai | medium | `fixed` | Bỏ `--diff-filter=d` và dùng `--no-renames` để luôn nhận cả path cũ lẫn mới; gate tự phân loại theo số locale còn sống: cả hai còn → so parity, còn một → **đỏ** (orphan), không còn bên nào → **xanh** (cho doc về hưu, không cần override). Tách `relsFromChangedFiles` + `classifyPairs` thành hàm thuần và thêm `scripts/__tests__/gate-changed-parity.test.mjs` (11 ca `node --test`: add một bên, edit hai bên, xoá một bên mỗi phía, xoá cả hai, rename hai bên, rename một bên, cộng hai ca dựng **repo git thật** để chứng minh workflow giữ deletion và path cũ của rename). Đã kiểm âm: coi half-deleted là hợp lệ → 5 ca đỏ. `scripts:test` đã nằm trong `check:all` của CI |
| B61 | 2026-08-31 · rà workflow trên main sau batch dependabot (rebase 2026-09-06) | vuln (availability) | `packages/runtime/src/index.ts` · `apps/cms/src/middleware/runtime.ts` · `apps/cms/wrangler.toml` | **Mọi deploy Cloudflare bị Cloudflare từ chối** kể từ khi bullmq 6 vào main (#421, 2026-08-30): `Uncaught Error: Could not determine sql-loader directory path`, validation error 10021. Nguyên nhân: `packages/runtime/src/index.ts` re-export **cả** `./adapters/docker` (và `./factory` = `createRuntime`, và `./leader-lock` import `ioredis` như **value**), còn `middleware/runtime.ts` import `createRuntime` từ barrel → bundle Worker kéo theo cả cây docker gồm `bullmq`. BullMQ 6 thêm backend Postgres, và `dist/esm/postgres/sql-loader.js` gọi `getDirname()` ở **top level module**, throw khi không có `__dirname` lẫn frame `file:///` — đúng môi trường Worker đã bundle. Worker throw lúc khởi tạo ⇒ script bị từ chối. **Vì sao mọi gate đều xanh:** `pnpm build` chạy `wrangler deploy --dry-run`, nó *bundle mà không instantiate*; job `build` của CI vì thế không thể thấy class lỗi "throw ở top level". Đây cũng là chỗ tôi kết luận sai khi rà #421 — coi "build pass + round-trip Redis thật" là đủ, trong khi chưa có gì boot Worker. Chỉ `deploy-cms.yml` (dev/staging) đỏ; production do `release.yml` sở hữu (chỉ chạy khi tag) nên chưa vỡ nhưng sẽ vỡ ở lần cắt release tới | critical | `fixed` | Tách entry point: root barrel chỉ còn thứ an toàn cho Worker; `@lumibase/runtime/docker` (adapter docker) và `@lumibase/runtime/node` (`createRuntime` + leader lock) là subpath riêng. `middleware/runtime.ts` static-import **chỉ** `createCloudflareRuntime`, nhánh docker đi qua `await import()`. Nhưng dynamic import **vẫn là static edge** với bundler (esbuild inline target — đo được: bundle vẫn chứa bullmq), nên thêm `[alias]` trong `wrangler.toml` map subpath docker sang stub `runtime-docker-unavailable.ts` (throw kèm mô tả nếu nhánh bất khả thi kia chạy). Kết quả đo: upload Worker **8848.98 KiB → 6085.28 KiB** (số trước lấy từ log deploy đỏ cuối cùng, run 33953796948 ngày 2026-09-05; số sau là build của PR này), 0 dấu vết bullmq/ioredis/aws-sdk/sql-loader ở cả 3 env. Gate mới: `pnpm verify:worker-bundle` (assert thành phần bundle — **đây** mới là hàng rào thật, đã test âm: thêm lại re-export docker → đỏ đúng) + `pnpm verify:worker-startup` (boot Worker bằng workerd). **Lưu ý đã đo:** startup gate **KHÔNG** bắt được ca này — với docker cố tình bundle lại, `wrangler dev` vẫn boot và trả `/health` 500, vì fallback quét stack `file:///` của bullmq thành công ở local mà thất bại ở Worker deploy; nên đừng đọc "startup xanh" thành "deploy được". Cả hai chạy trong CI job `worker-bundle`. Bonus: `serve.ts` trước đây inject runtime bằng middleware thứ hai đặt **sau** `withRuntime`, nên Docker mode dựng **hai** runtime mỗi process (hai kết nối Redis, hai pg pool) rồi bỏ một — nay dùng `setRuntimeFactory`, còn một Issue #459. |
| B60 | 2026-09-09 · review PR #456 (§4a docs) | task (docs debt) | `docs/{en,vi}/features/agent-harness-layer.md` | Bản VI của `agent-harness-layer.md` **không phải bản dịch** của bản EN mà là một tài liệu khác: một đề xuất 8 mục có số, 10 heading so với 25 của EN, dài **25%** bản EN, có bảng và code fence mà EN không có và thiếu 253 inline-code identifier EN có. Front matter vẫn ghi `syncStatus: human-translated`, và `docs:i18n:detect` xếp cặp này up-to-date vì hash còn khớp — đúng điểm mù B37/B54/B58 nhưng ở quy mô cả file. PR #456 thêm phần approval claim/quarantine/reopen vào **cả hai** bên trong cùng commit nên không làm tệ thêm, và stamp bằng `--allow-structure-drift` kèm lý do | medium | `open` | Dịch lại bản VI từ bản EN hiện tại (hoặc quyết định bản VI là một tài liệu roadmap riêng và **đổi tên** nó, bỏ `translatedFrom`/`syncStatus` để nó không còn giả vờ là một nửa của cặp). Việc dịch thuần, không đụng code. **Cập nhật 2026-09-10:** parity gate (#458) đã merge và chặn PR #456 vì cặp này; đã thêm waiver `<!-- check-parity: allow headings code-fences inline-code links tables bulk -->` vào đầu bản VI để gate không chặn thay đổi không liên quan. Waiver ghi nhận hiện trạng chứ không chấp nhận nó — **gỡ waiver khi làm xong mục này**, lúc đó cặp phải qua parity mà không cần miễn trừ |
| B62 | 2026-09-11 · verify batch dependabot (#466/#465/#464/#417) | task (deps hygiene) | `apps/shell/package.json` ↔ `apps/shell/src-tauri/Cargo.lock` | Bốn dependency npm `@tauri-apps/*` của `apps/shell` **không thể ảnh hưởng gì** nhưng vẫn sinh PR dependabot đều đặn, và tệ hơn là tạo cảm giác sai rằng JS↔Rust đang được giữ đồng bộ. App này **không có source JS/TS nào** (chỉ `src-tauri/`; `frontendDist` trỏ sang build của `apps/studio` — contract C3), và grep `@tauri-apps/plugin` trên toàn `apps/` + `packages/` trả **0 hit**: plugin chỉ được dùng từ Rust (`src-tauri/src/lib.rs` — `tauri_plugin_dialog::DialogExt`, `tauri_plugin_updater::UpdaterExt`, `tauri_plugin_deep_link::DeepLinkExt`). Phía thật sự chạy là crate do Cargo resolve, mà `Cargo.toml` khai `"2"` nên `Cargo.lock` mới là nơi chốt version — hiện `tauri-plugin-dialog 2.7.1` / `deep-link 2.4.9` / `updater 2.10.1`, trong khi #466 nâng phía npm lên `^2.7.3` / `^2.4.10` / `^2.11.0`. Nghĩa là con số trong `package.json` mô tả một thứ không được cài để dùng. Không phải bug đang gây hỏng — nhưng đúng class "guard/khai báo đọc thì đúng, thực tế không nói gì" của B20/B31/B53 | low | `open` | Chọn một: (a) xoá 4 dep npm `@tauri-apps/plugin-*` khỏi `apps/shell/package.json` (giữ `@tauri-apps/cli`, và `@tauri-apps/api` nếu tauri CLI cần để cảnh báo lệch version) rồi ghi một dòng trong `apps/shell/README.md` rằng plugin sống ở Cargo; hoặc (b) giữ chúng nhưng thêm gate so `Cargo.lock` với `package.json` để hai bên không trôi lệch âm thầm. Kèm theo: `Cargo.lock` chưa được refresh sau nhiều đợt bump — `cargo update -p tauri-plugin-dialog` v.v. nên nằm trong quy trình bump shell (liên quan B35: `release-apps.yml` không được PR nào exercise) |
| B63 | 2026-09-11 · verify batch dependabot (#417 TypeScript 7) | task (deps · blocked upstream) | `packages/{sdk,contracts,cli,mcp-server,extension-sdk,create-lumibase}` (tsup `--dts`) · `apps/{landing,consumer}` (lint) | **TypeScript 7.0.2 chưa nâng được, và lý do không phải code của repo.** Đo trực tiếp trên nhánh thử: `tsc --noEmit` xanh **18/18** package sau khi bỏ 2 chỗ `baseUrl` (đã land, xem CHANGELOG), và nhanh hơn hẳn — **8.5s so với 2m9s** trên TS 5.9.3. Nhưng hai chỗ vỡ, cả hai là upstream từ chối tường minh: (1) `pnpm lint` chết ngay lúc `require` với **`Error: typescript-eslint does not support TS 7.0.`** (`typescript-eslint@8.70.0`, peer `>=4.8.4 <6.1.0`, vào cây qua `eslint-config-next`) → job CI `Lint stable packages` đỏ cho `apps/landing` + `apps/consumer`; (2) `tsup --dts` chết với `TypeError: Cannot read properties of undefined (reading 'useCaseSensitiveFileNames')` bên trong `rollup-plugin-dts`, vì TS 7 **không còn export compiler API**: entry `.` của package trỏ `./lib/version.cjs` và `require('typescript')` trả **đúng 2 key** (`version`, `versionMajorMinor`). Đã kiểm âm để chắc nguyên nhân là DTS chứ không phải tsup: cùng lệnh tsup **bỏ `--dts`** build xong trong **80ms**. Hệ quả là build của **cả 6 package được publish** đứt — trong đó type của `@lumibase/sdk` là surface đã freeze cho 1.0 (`v1-release-criteria.md` §2/§6), nên đây là chặn cứng chứ không phải bất tiện. Những thứ **vẫn chạy** trên TS 7: `next build` (landing), `vite build` + prerender (docs), build Worker qua wrangler (cms), `drizzle-kit` | medium | `open` (chặn bởi upstream) | Đừng merge #417 ở dạng hiện tại. Ba đường, không loại trừ nhau: (a) **chờ TS 7.1**, bản mà release notes hứa có API programmatic ổn định — rẻ nhất, và là điều kiện để `typescript-eslint` + `rollup-plugin-dts` ra bản hỗ trợ; (b) **cài song song** theo đúng hướng dẫn của TS 7 (`typescript: "npm:@typescript/typescript6@^6.0.2"` cho tsup/eslint, `@typescript/native` cho `tsc` trong CI) — mua được cú tăng tốc typecheck ~15x ngay, nhưng thêm một trục drift phải canh, và DoD §2e nói rõ hai major của cùng một tool trong một workspace là drift đắt; (c) rời DTS sang thứ không cần TS API. Việc nên làm **trước** khi thử lại: lên TS 6 và dọn sạch mọi cảnh báo deprecation ở đó (repo đang ở 5.9.3, tức nhảy 5→7 biến mọi deprecation của 6 thành lỗi cứng một lượt). Đã dọn trước một phần: hai `baseUrl` (TS5102) đã bỏ, `tsconfig.base.json` vốn đã dùng `moduleResolution: Bundler` + `esModuleInterop: true` + `strict: true` + `target ES2022` nên không đụng nhóm option bị xoá |
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,61 @@ Source: [github.com/khuepm/lumibase](https://github.com/khuepm/lumibase) · Webs

## [Unreleased]

### Changed

- **Dependency batch: 27 minor/patch bumps, Vitest 5, Framer Motion 13.** The
group bump carries `zod` 4.4→4.6, `next` 16.3.3→16.3.4, `hono` 4.13.5→4.13.7,
`wrangler` 4.127→4.129, `bullmq` 6.3.1→6.3.4, `lucide-react` 1.34→1.41 and
`@types/node` 26.4.0→26.4.1 among others. Vitest moves to `5.0.0` across all
eleven packages that run it, and `apps/landing` to Framer Motion `13.2.0`.
Nothing in the public surface changes.

Two of the four upgrades needed a fix before they were honest, and both were
the same shape — a declaration that read correctly while the thing it
described did not happen:

- `pnpm.overrides.@types/react-dom` was an exact pin at `19.2.5` while the
bump raised `apps/{docs,landing,studio}` to `^19.2.7`. Overrides apply to
direct dependencies too, so the pin won and every lockfile importer still
recorded `specifier: 19.2.5` — the manifests claimed a version that was not
installed. `pnpm drift:check` caught it, which is the second time that gate
has paid for itself (the first was the `vite` 7-vs-8 incident it was built
for). The pin now moves with the manifests.
- `apps/docs` registered its Testing Library matchers through the bare
`@testing-library/jest-dom` entry, which declares them on the global
`jest.Matchers` interface. Vitest read that interface up to v4 and stopped
in v5, so under `5.0.0` every `toBeInTheDocument` / `toHaveAttribute` /
`toHaveClass` lost its type — fifteen `TS2339` errors that failed
`typecheck` **and** `build` while the tests themselves kept passing at
runtime. It now imports `@testing-library/jest-dom/vitest`, which is what
`apps/studio` already did; that difference is the whole reason only one of
the two apps broke.

Framer Motion 13's only breaking change is dropping `@emotion/is-prop-valid`
as an optional dependency, and this workspace has no CSS-in-JS at all, so it
could not apply. Because `apps/landing` has no component tests, that was
verified in a real browser rather than inferred: the page renders with 96
elements carrying Framer Motion's inline transform/opacity styles, no console
errors and no uncaught exceptions, against a 12.43 baseline of 95 on the same
1053-element tree.

- **`baseUrl` removed from the `apps/docs` and `apps/studio` tsconfigs.** Both
set it to `"."`, so dropping it changes nothing today: `paths` without
`baseUrl` resolve relative to the tsconfig, which is the same directory, and
Vite resolves `@/*` from its own `resolve.alias` regardless. TypeScript 7
removed the option outright (`TS5102`), so this is groundwork rather than a
fix — verified on 5.9.3 to keep `typecheck`, `build` and `lint` green.

TypeScript 7 itself is **not** adopted. Its native compiler works here — the
full workspace typechecks 18/18, and in 8.5s against 2m9s on 5.9.3 — but 7.0
ships without the programmatic compiler API, and two tools the build depends
on refuse to run: `typescript-eslint` throws `does not support TS 7.0` at
require time, failing lint for `apps/{landing,consumer}`, and `tsup --dts`
crashes inside `rollup-plugin-dts` reading `ts.sys.useCaseSensitiveFileNames`,
which breaks the build of all six published packages including the
semver-frozen `@lumibase/sdk` types. Tracked as `B63` in the out-of-scope
backlog, pending the stable API in 7.1.

### Fixed

- **A DB integration suite pointed at a database that is not there no longer
Expand Down
18 changes: 9 additions & 9 deletions apps/cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
"@lumibase/database": "workspace:*",
"@lumibase/runtime": "workspace:*",
"@lumibase/sdk": "workspace:*",
"@sentry/cloudflare": "^10.71.0",
"@sentry/cloudflare": "^10.73.0",
"drizzle-orm": "^0.45.2",
"graphql": "^17.0.2",
"graphql-yoga": "^5.22.0",
"hono": "^4.13.5",
"jose": "^6.2.10",
"hono": "^4.13.7",
"jose": "^6.2.12",
"maxmind": "5.0.7",
"nanoid": "^6.0.1",
"node-cron": "^4.6.0",
Expand All @@ -49,18 +49,18 @@
"prom-client": "^15.1.0",
"uuid": "^14.0.1",
"ws": "^8.21.3",
"zod": "^4.4.3"
"zod": "^4.5.4"
},
"devDependencies": {
"@cloudflare/workers-types": "^5.20260827.1",
"@types/node": "^26.4.0",
"@cloudflare/workers-types": "^5.20260907.1",
"@types/node": "^26.4.1",
"@types/nodemailer": "8.0.1",
"@types/ws": "^8.5.10",
"esbuild": "^0.28.0",
"fast-check": "^4.9.0",
"tsx": "^4.23.12",
"tsx": "^4.23.13",
"typescript": "^5.6.2",
"vitest": "^4.1.11",
"wrangler": "^4.127.0"
"vitest": "^5.0.0",
"wrangler": "^4.129.0"
}
}
4 changes: 2 additions & 2 deletions apps/consumer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"@lumibase/sdk": "workspace:^",
"next": "16.3.3",
"next": "16.3.4",
"react": "19.2.8",
"react-dom": "19.2.8"
},
Expand All @@ -20,7 +20,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^10",
"eslint-config-next": "16.3.3",
"eslint-config-next": "16.3.4",
"tailwindcss": "^4",
"typescript": "^5"
}
Expand Down
14 changes: 7 additions & 7 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
"@lumibase/contracts": "workspace:*",
"@lumibase/ui": "workspace:*",
"@shikijs/rehype": "^4.4.3",
"dompurify": "^3.4.14",
"dompurify": "^3.4.15",
"gray-matter": "^4.0.3",
"lucide-react": "^1.34.0",
"lucide-react": "^1.41.0",
"minisearch": "^7.1.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.18.2",
"react-router-dom": "^7.18.3",
"rehype-raw": "^7.0.0",
"rehype-slug": "^6.0.0",
"remark-frontmatter": "^5.0.0",
Expand All @@ -40,15 +40,15 @@
"@testing-library/jest-dom": "^7.0.0",
"@testing-library/react": "^16.3.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"@types/react-dom": "^19.2.7",
"@vitejs/plugin-react": "^6.1.1",
"autoprefixer": "^10.5.4",
"autoprefixer": "^10.5.5",
"fast-check": "^4.9.0",
"jsdom": "^30.0.1",
"postcss": "^8.5.26",
"postcss": "^8.5.28",
"tailwindcss": "^4.3.3",
"typescript": "^5.6.2",
"vite": "^8.2.0",
"vitest": "^4.1.11"
"vitest": "^5.0.0"
}
}
14 changes: 13 additions & 1 deletion apps/docs/src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import '@testing-library/jest-dom';
// Use the `/vitest` entry, not the bare `@testing-library/jest-dom` one.
//
// The bare entry declares its matchers on the global `jest.Matchers` interface.
// Vitest read that interface up to v4, so `toBeInTheDocument` and friends
// type-checked by accident; Vitest 5 stopped reading it (a library supporting
// both runners now has to augment `jest.Matchers` and `vitest.Matchers`
// separately). Under v5 the bare import leaves every matcher untyped —
// `Property 'toBeInTheDocument' does not exist on type 'Assertion<void,
// HTMLElement>'` across `LinkRewriter.test.tsx` and
// `analytics-consent.test.tsx`, which fails `typecheck` and `build` while the
// tests themselves still pass at runtime. `apps/studio` already imports the
// `/vitest` entry, so it was unaffected; this brings the two apps in step.
import '@testing-library/jest-dom/vitest';

// Vitest's jsdom environment does not always expose `window.localStorage` in this
// monorepo, but the analytics-consent wiring tests need a working in-memory store.
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"types": ["vite/client", "vitest/globals"],
"baseUrl": ".",
// No `baseUrl`: TypeScript 7 removed it (TS5102). Dropping it is a no-op
// here because it was `"."` — `paths` without `baseUrl` resolve relative to
// this tsconfig, which is the same directory. Vite resolves `@/*` from its
// own `resolve.alias` in vite.config.ts, so the bundler is unaffected.
"paths": {
"@/*": ["./src/*"]
},
Expand Down
18 changes: 9 additions & 9 deletions apps/landing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@
},
"dependencies": {
"@lumibase/analytics-consent": "workspace:*",
"framer-motion": "^12.43.0",
"framer-motion": "^13.2.0",
"lenis": "^1.3.26",
"lucide-react": "^1.34.0",
"next": "^16.3.3",
"lucide-react": "^1.41.0",
"next": "^16.3.4",
"ogl": "^1.0.11",
"react": "^19.2.8",
"react-dom": "^19.2.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.3.3",
"@types/node": "^26.4.0",
"@types/node": "^26.4.1",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"autoprefixer": "^10.5.4",
"@types/react-dom": "^19.2.7",
"autoprefixer": "^10.5.5",
"eslint": "^10",
"eslint-config-next": "^16.3.3",
"postcss": "^8.5.26",
"eslint-config-next": "^16.3.4",
"postcss": "^8.5.28",
"tailwindcss": "^4.3.3",
"typescript": "^5.6.2",
"vitest": "^4.1.11"
"vitest": "^5.0.0"
}
}
Loading