diff --git a/AGENTS.md b/AGENTS.md index c738b919..3034c7f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -202,4 +202,4 @@ Mỗi finding khi review (subagent fresh-context, bot, hoặc người) phải - **Vòng 1:** Sửa các finding hợp lý ở mọi mức. - **Từ vòng 2 trở đi:** Trong PR hiện tại chỉ sửa P0/P1, mọi finding về bảo mật hoặc dữ liệu, lỗi trong hành vi mà chính PR này thêm vào, và required check đang đỏ. Các P2/P3 còn lại gom vào **một** issue follow-up duy nhất; trả lời từng thread kèm link issue đó rồi resolve thread. - Một lượt review của bot bị rate-limit, bị bỏ qua (skipped) hoặc không có nội dung **không phải là review**. Không chờ và không tính là đã review. -- Cổng review thật sự là **review bằng subagent fresh-context** (Mục 0.2). Required check `Typecheck & Build Monorepo` phải xanh trước khi merge (ruleset `dev-main-light` trên `dev` và `main`: chặn xóa nhánh và force-push, không yêu cầu approval). Riêng `main` có thêm ruleset `main-release`: bắt buộc qua PR, chỉ cho merge commit, và phải xanh thêm `PR into main comes from dev` và `main must not lead dev` (Mục 0.6). +- Cổng review thật sự là **review bằng subagent fresh-context** (Mục 0.2). Các required check `Typecheck & Build Monorepo`, `E2E (Playwright)` và `Secret scan (gitleaks)` phải xanh trước khi merge (ruleset `dev-main-light` trên `dev` và `main`: chặn xóa nhánh và force-push, không yêu cầu approval). Riêng `main` có thêm ruleset `main-release`: bắt buộc qua PR, chỉ cho merge commit, và phải xanh thêm `PR into main comes from dev` và `main must not lead dev` (Mục 0.6). diff --git a/Docs/Resources/RELEASE.md b/Docs/Resources/RELEASE.md index ebad64b0..fcfb5c3d 100644 --- a/Docs/Resources/RELEASE.md +++ b/Docs/Resources/RELEASE.md @@ -11,12 +11,12 @@ This is the release workflow for `rate.vn`. It follows the same model as DOS/DOS ## Normal release -1. Feature PRs go into `dev` as usual: review, green `Typecheck & Build Monorepo`, squash merge. +1. Feature PRs go into `dev` as usual: review, green required checks (`Typecheck & Build Monorepo`, `E2E (Playwright)`, `Secret scan (gitleaks)`, `task-claim`), squash merge. 2. Check `dev` on its Vercel Preview deployment. 3. Open the promotion PR from `dev` itself, not from a copy branch: `gh pr create --repo DOS/Rate --base main --head dev --title "chore(release): promote dev to main"` List the PRs being released in the body. -4. Wait for the required checks on the promotion PR: `Typecheck & Build Monorepo`, `PR into main comes from dev`, `main must not lead dev`. +4. Wait for the required checks on the promotion PR: `Typecheck & Build Monorepo`, `E2E (Playwright)`, `Secret scan (gitleaks)`, `PR into main comes from dev`, `main must not lead dev`. `node scripts/ops/promote.mjs` does steps 3 to 5 in one command: it opens the promotion PR from `dev` (or reuses the open one) with the released PRs listed, then merges it with a merge commit once its checks pass. `--no-merge` leaves it open. 5. Promotions are batched and need no per-release question: the agent promotes at the first checkpoint reached: the backlog runs out, 10 or more PRs are waiting on `dev`, the oldest unpromoted change is more than 24 hours old, or the agent is ending a long working session. Promote only when `dev` head CI is green and no PR is mid-way through fixing a production bug. The agent merges it with a merge commit: `gh pr merge --repo DOS/Rate --merge`. JOY does not merge PRs; never ask JOY to click Merge. **Never squash or rebase a promotion.** Squashing creates a commit on `main` that can never exist on `dev`, and the next promotion then diffs from a stale merge base. 6. Vercel builds `main` and assigns `rate.vn`. Verify the live revision (the deployment's Git SHA equals `main`), `/api/health`, the main routes and a browser check before calling the release done. `node scripts/ops/verify-live.mjs --wait 900` checks the first three: `/api/health` reports the deployment's `commit`, which must equal `main`, and `checks.database` must be `ok`. It re-reads `main` on every poll. Only when the live commit is behind `main` does it print `vercel promote --scope metados`; when live is ahead of or diverged from `main` it says to do nothing. Never redeploy or promote a deployment built from another commit: that rolls the release back. `promote` does not rebuild, and it turns production domain auto-assignment back on after a `vercel rollback`. To check a build before it takes traffic, create it from Git with `autoAssignCustomDomains: false` (Vercel API `POST /v13/deployments` with `gitSource` ref `main` and `target: "production"`), verify it with `vercel curl --deployment `, then `vercel promote `. Promoting an existing production deployment does not rebuild. @@ -44,7 +44,7 @@ This is the release workflow for `rate.vn`. It follows the same model as DOS/DOS - **Build guard:** `vercel.json` runs `scripts/vercel-production-guard.mjs --vercel` before every Vercel build. A production build fails unless `VERCEL_GIT_PROVIDER=github`, the repository is `DOS/Rate` and `VERCEL_GIT_COMMIT_REF=main`. A CLI production upload fails at build time from any branch, `main` included, because Vercel reports no Git source for CLI builds (observed on a remote CLI build: `git=:/@`). So does any other build where Vercel does not report a GitHub source. Preview builds are not affected. Tests: `node --test scripts/vercel-production-guard.test.mjs` (runs in CI). - **Validate PR Base** (`.github/workflows/validate-pr-base.yml`): a PR into `main` must have head `dev` from this repository, or carry `emergency-hotfix`. - **Branch Divergence** (`.github/workflows/branch-divergence.yml`): fails when `main` has a non-merge commit that `dev` lacks. `.github/branch-divergence-baseline.txt` should stay empty. -- **GitHub rulesets:** `dev-main-light` blocks deletion and force push on `dev` and `main` and requires `Typecheck & Build Monorepo`. `main-release` on `main` requires a PR, allows only merge commits, and requires the two checks above. +- **GitHub rulesets:** `dev-main-light` blocks deletion and force push on `dev` and `main` and requires `Typecheck & Build Monorepo`, `E2E (Playwright)` and `Secret scan (gitleaks)`. `main-release` on `main` requires a PR, allows only merge commits, and requires the two checks above. The build guard is a guardrail against mistakes, not a security boundary. Anyone with production deploy rights on the Vercel project can get past it, for example with `--build-env` values that imitate a Git build or by uploading an edited `vercel.json`. Doing that breaks the rule above. The only hard limit is Vercel RBAC: a Developer role without the "Full Production Deployment" permission can reach production only by merging to `main`. Whether the `metados` team's plan offers that control has not been verified yet. diff --git a/Docs/Resources/ROADMAP.md b/Docs/Resources/ROADMAP.md index 3e94ebd3..6b4bb535 100644 --- a/Docs/Resources/ROADMAP.md +++ b/Docs/Resources/ROADMAP.md @@ -55,7 +55,7 @@ Hệ thống **Rate Platform (`rate.vn`)** vận hành trên kiến trúc 4 tầ - [x] Dependabot security updates và CodeQL default setup đã bật. - [x] Quét secret bù trừ: gitleaks quét commit của mỗi PR vào `dev`/`main` (`DOS/Rate#305`). - [ ] Secret Scanning + Push Protection: repo public nên miễn phí, nhưng API trả `422` "blocked by an enterprise policy"; cần chủ enterprise mở policy hoặc gắn cấu hình "Secret Protection enabled" của org cho repo. - - [ ] Branch Rulesets: `dev-main-light` và `main-release` đã bật lại sau khi làm sạch lịch sử (`#153`); còn thiếu `Secret scan (gitleaks)` trong danh sách check bắt buộc (cần chủ repo cho phép). + - [x] Branch Rulesets: `dev-main-light` và `main-release` đã bật lại sau khi làm sạch lịch sử (`#153`); `dev-main-light` bắt buộc `Typecheck & Build Monorepo`, `E2E (Playwright)` và `Secret scan (gitleaks)` trên `dev` và `main` (JOY cho phép ngày 2026-10-25, `#1073`). - [x] **6.6. Khóa Đường Ghi Công Khai Theo Người Dùng & Phiên Cookie (`DOS/Rate#155`, `#194` - Audit K1, K2, C4, T13):** - `POST /api/v1/rate/reviews` và `/watchlist` bắt buộc đăng nhập, gọi RPC dưới quyền người dùng (không còn service role), không báo thành công giả, mã lỗi RPC → 403/409/422/429. Client service role tách sang `@repo/shared-data/server` (ESLint cấm trong `apps/web`). - Phiên đăng nhập lưu trong cookie qua `@supabase/ssr`; `middleware.ts` → `proxy.ts` (Next.js 16) refresh phiên; request dùng cookie phải cùng origin. diff --git a/apps/web/e2e/input-labels.spec.ts b/apps/web/e2e/input-labels.spec.ts new file mode 100644 index 00000000..8ca2c665 --- /dev/null +++ b/apps/web/e2e/input-labels.spec.ts @@ -0,0 +1,41 @@ +import { expect, test } from '@playwright/test'; + +// A placeholder is not a label: it disappears as soon as the user types. +// Every visible field must be named by a