@cyberskill/design 1.2.0 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/DESIGN.md +4 -3
  2. package/README.md +20 -19
  3. package/SKILL.md +5 -5
  4. package/THIRD-PARTY-NOTICES.md +35 -0
  5. package/VERSION +1 -1
  6. package/_ds_bundle.js +701 -1342
  7. package/_esm/cs.mjs +2 -2
  8. package/_esm/react.mjs +6 -1
  9. package/_vendor/NOTICE.md +16 -0
  10. package/_vendor/babel/README.md +7 -0
  11. package/_vendor/babel/babel.min.js +4 -0
  12. package/_vendor/react/README.md +3 -0
  13. package/_vendor/react/react-dom.production.min.js +1 -0
  14. package/_vendor/react/react.production.min.js +1 -0
  15. package/assets/aurora-hoa.webp +0 -0
  16. package/assets/aurora-kim.webp +0 -0
  17. package/assets/aurora-moc.webp +0 -0
  18. package/assets/aurora-tho.webp +0 -0
  19. package/assets/aurora-thuy.webp +0 -0
  20. package/base/ai.css +8 -3
  21. package/base/collections.css +10 -4
  22. package/base/components.css +5 -2
  23. package/base/controls.css +58 -21
  24. package/base/data.css +1 -1
  25. package/base/feedback.css +9 -2
  26. package/base/forms.css +1 -1
  27. package/base/glass.css +1 -1
  28. package/base/interaction.css +6 -2
  29. package/base/navigation.css +1 -1
  30. package/base/reset.css +7 -1
  31. package/base/responsive.css +13 -12
  32. package/base/shell.css +8 -2
  33. package/components/_i18n/i18n.js +28 -3
  34. package/components/_i18n/strings.js +42 -24
  35. package/components/ai/ChatMessage.d.ts +2 -0
  36. package/components/ai/ChatMessage.jsx +8 -4
  37. package/components/ai/ConfidenceMeter.d.ts +2 -0
  38. package/components/ai/HumanReviewGate.d.ts +2 -0
  39. package/components/ai/PromptInput.d.ts +2 -0
  40. package/components/ai/TypingIndicator.d.ts +2 -0
  41. package/components/ai/ai-conversation.card.html +3 -3
  42. package/components/ai/ai-lumi.card.html +3 -3
  43. package/components/ai/ai.card.html +3 -3
  44. package/components/brand/brand.card.html +3 -3
  45. package/components/button/buttons.card.html +3 -3
  46. package/components/data/Image.jsx +18 -2
  47. package/components/data/Sortable.d.ts +5 -1
  48. package/components/data/Sortable.jsx +42 -9
  49. package/components/data/Sortable.prompt.md +4 -2
  50. package/components/data/Terminal.jsx +3 -2
  51. package/components/data/Tooltip.d.ts +2 -0
  52. package/components/data/Tooltip.jsx +6 -3
  53. package/components/data/Tree.d.ts +2 -1
  54. package/components/data/Tree.jsx +137 -16
  55. package/components/data/Tree.prompt.md +1 -1
  56. package/components/data/collections.card.html +3 -3
  57. package/components/data/data.card.html +3 -3
  58. package/components/datatable/DataTable.d.ts +24 -1
  59. package/components/datatable/DataTable.jsx +78 -5
  60. package/components/datatable/DataTable.prompt.md +2 -1
  61. package/components/datatable/datatable.card.html +20 -16
  62. package/components/dialog/dialog.card.html +3 -3
  63. package/components/feedback/Alert.d.ts +2 -0
  64. package/components/feedback/Alert.jsx +8 -2
  65. package/components/feedback/EmptyState.d.ts +2 -0
  66. package/components/feedback/EmptyState.jsx +6 -3
  67. package/components/feedback/ProgressBar.d.ts +2 -0
  68. package/components/feedback/ProgressBar.jsx +6 -3
  69. package/components/feedback/Toast.jsx +15 -1
  70. package/components/feedback/Toast.prompt.md +2 -1
  71. package/components/feedback/feedback-2.card.html +3 -3
  72. package/components/feedback/feedback.card.html +3 -3
  73. package/components/forms/Cascader.jsx +7 -2
  74. package/components/forms/FileUpload.d.ts +2 -0
  75. package/components/forms/Form.d.ts +3 -1
  76. package/components/forms/Form.jsx +9 -1
  77. package/components/forms/Form.prompt.md +1 -1
  78. package/components/forms/Mentions.d.ts +1 -1
  79. package/components/forms/Mentions.jsx +59 -7
  80. package/components/forms/Mentions.prompt.md +3 -1
  81. package/components/forms/Rating.d.ts +3 -2
  82. package/components/forms/Rating.jsx +34 -3
  83. package/components/forms/Rating.prompt.md +1 -1
  84. package/components/forms/Select.d.ts +2 -0
  85. package/components/forms/Select.jsx +6 -2
  86. package/components/forms/Slider.d.ts +2 -0
  87. package/components/forms/Slider.jsx +5 -2
  88. package/components/forms/Switch.d.ts +2 -0
  89. package/components/forms/Switch.jsx +7 -9
  90. package/components/forms/Toggle.d.ts +2 -0
  91. package/components/forms/Toggle.jsx +14 -2
  92. package/components/forms/forms-2.card.html +3 -3
  93. package/components/forms/forms.card.html +3 -3
  94. package/components/icon/Icon.d.ts +1 -1
  95. package/components/icon/Icon.prompt.md +1 -1
  96. package/components/icon/icon.card.html +3 -3
  97. package/components/logo/logo.card.html +3 -3
  98. package/components/navigation/CommandPalette.d.ts +2 -0
  99. package/components/navigation/Menu.d.ts +2 -0
  100. package/components/navigation/Menu.jsx +60 -6
  101. package/components/navigation/Steps.d.ts +2 -0
  102. package/components/navigation/Steps.jsx +6 -3
  103. package/components/navigation/Tabs.d.ts +2 -0
  104. package/components/navigation/Tabs.jsx +6 -3
  105. package/components/navigation/Toolbar.d.ts +3 -2
  106. package/components/navigation/Toolbar.jsx +117 -11
  107. package/components/navigation/Toolbar.prompt.md +1 -1
  108. package/components/navigation/navigation.card.html +3 -3
  109. package/components/navigation/shell.card.html +3 -3
  110. package/components/overlays/ContextMenu.d.ts +2 -0
  111. package/components/overlays/ContextMenu.jsx +6 -3
  112. package/components/overlays/Drawer.d.ts +2 -0
  113. package/components/overlays/alertdialog.card.html +3 -3
  114. package/components/overlays/overlays.card.html +3 -3
  115. package/components/textfield/textfield.card.html +3 -3
  116. package/dist/styles.min.css +312 -64
  117. package/docs/ci-cd.md +18 -13
  118. package/docs/consumer-grant.md +3 -0
  119. package/docs/consuming.md +7 -7
  120. package/docs/contrast-report.md +79 -1
  121. package/docs/conventions.md +17 -4
  122. package/docs/decisions.md +40 -2
  123. package/docs/deploy.md +17 -6
  124. package/docs/design-styles.md +1 -1
  125. package/docs/quality-gates.md +19 -11
  126. package/docs/release-runbook.md +78 -0
  127. package/docs/sync.md +1 -1
  128. package/docs/vi/ci-cd.md +16 -13
  129. package/docs/vi/consumer-grant.md +3 -0
  130. package/docs/vi/consuming.md +7 -7
  131. package/docs/vi/contrast-report.md +79 -1
  132. package/docs/vi/conventions.md +18 -5
  133. package/docs/vi/decisions.md +39 -2
  134. package/docs/vi/deploy.md +17 -6
  135. package/docs/vi/quality-gates.md +18 -11
  136. package/docs/vi/release-runbook.md +78 -0
  137. package/docs/vi/sync.md +1 -1
  138. package/docs/viewer.html +2 -2
  139. package/fonts/OFL.txt +98 -0
  140. package/guidelines/atomic-view.html +2 -2
  141. package/guidelines/brand-imagery.html +1 -1
  142. package/guidelines/elements-variants.html +5 -5
  143. package/guidelines/spacing-breakpoints.html +2 -1
  144. package/guidelines/surfaces-glass.html +1 -1
  145. package/llms.txt +4 -4
  146. package/package.json +28 -8
  147. package/templates/app-shell/AppShell.dc.html +3 -3
  148. package/templates/app-shell/content-schema.json +7 -0
  149. package/templates/article/Article.dc.html +3 -3
  150. package/templates/article/content-schema.json +7 -0
  151. package/templates/auth/Auth.dc.html +26 -10
  152. package/templates/auth/content-schema.json +25 -0
  153. package/templates/dashboard/Dashboard.dc.html +4 -4
  154. package/templates/dashboard/content-schema.json +7 -0
  155. package/templates/delivery-change-note/DeliveryChangeNote.dc.html +2 -2
  156. package/templates/delivery-runbook/DeliveryRunbook.dc.html +2 -2
  157. package/templates/email-safe/email-announcement.html +2 -2
  158. package/templates/email-safe/email-dunning.html +2 -2
  159. package/templates/email-safe/email-investor.html +2 -2
  160. package/templates/email-safe/email-launch.html +2 -2
  161. package/templates/email-safe/email-newsletter.html +2 -2
  162. package/templates/email-safe/email-status.html +2 -2
  163. package/templates/email-safe/email-transactional.html +2 -2
  164. package/templates/finance-dunning-email/FinanceDunningEmail.dc.html +2 -2
  165. package/templates/finance-expense-report/FinanceExpenseReport.dc.html +2 -2
  166. package/templates/finance-invoice/FinanceInvoice.dc.html +2 -2
  167. package/templates/finance-quote/FinanceQuote.dc.html +4 -4
  168. package/templates/hr-interview-kit/HrInterviewKit.dc.html +1 -1
  169. package/templates/marketing-case-study/MarketingCaseStudy.dc.html +1 -1
  170. package/templates/marketing-page/MarketingPage.dc.html +3 -3
  171. package/templates/marketing-page/content-schema.json +1 -0
  172. package/templates/settings/Settings.dc.html +3 -3
  173. package/templates/settings/content-schema.json +7 -0
  174. package/tokens/element-seeds.json +2 -2
  175. package/tokens/elements.css +193 -19
  176. package/tokens/native/CSTokens.kt +3 -2
  177. package/tokens/native/CSTokens.swift +3 -2
  178. package/tokens/native/cs_tokens.dart +3 -2
  179. package/tokens/provenance.json +8 -8
  180. package/tokens/spacing.css +2 -1
  181. package/tokens/tokens.dtcg.json +19 -14
  182. package/tokens/tokens.js +16 -15
  183. package/tokens/tokens.json +15 -14
  184. package/ui_kits/deck/index.html +1 -1
  185. package/ui_kits/status-hub/StatusHub.jsx +1 -1
  186. package/ui_kits/status-hub/login.html +52 -10
  187. package/ui_kits/status-hub/project.html +1 -1
  188. package/ui_kits/status-hub/settings.html +1 -1
  189. package/ui_kits/website/Website.jsx +1 -1
  190. package/ui_kits/website/careers.html +2 -2
  191. package/ui_kits/website/chat.html +1 -1
  192. package/ui_kits/website/site.css +1 -1
  193. package/ui_kits/website/work.html +2 -2
  194. package/assets/aurora-hoa.png +0 -0
  195. package/assets/aurora-kim.png +0 -0
  196. package/assets/aurora-moc.png +0 -0
  197. package/assets/aurora-thuy.png +0 -0
  198. package/docs/decisions/container-query-migration.md +0 -18
  199. package/docs/decisions/overlay-manager-design.md +0 -101
  200. package/docs/plans/cyberskill-design-system-audit-and-12-month-evolution-plan.md +0 -1490
  201. package/docs/status/assets/favicon.svg +0 -5
  202. package/docs/status/assets/status-legacy.css +0 -399
  203. package/docs/status/assets/status-legacy.js +0 -505
  204. package/docs/status/assets/status.css +0 -644
  205. package/docs/status/assets/status.js +0 -1308
  206. package/docs/status/data/status-feed.json +0 -1
  207. package/docs/status/data/task/TASK-REL-001.js +0 -1
  208. package/docs/status/data/task/TASK-REL-002.js +0 -1
  209. package/docs/status/index.html +0 -46
  210. package/docs/status/roadmap.html +0 -4
  211. package/docs/status/status-legacy.html +0 -122
  212. package/docs/tasks/BACKLOG.md +0 -26
  213. package/docs/tasks/_state/commit-links.yaml +0 -14
  214. package/docs/tasks/release/TASK-REL-001-launch-1-1-0/spec.md +0 -55
  215. package/docs/tasks/release/TASK-REL-002-republish-1-1-1/spec.md +0 -54
@@ -0,0 +1,78 @@
1
+ # Release runbook (authoritative)
2
+
3
+ One page for how a release happens and how to tell it worked. Consolidates areas 21/23. Related detail: `docs/ci-cd.md`, `docs/deploy.md`, `docs/decisions.md` §7 / §13.
4
+
5
+ ## Trigger
6
+
7
+ | Path | When |
8
+ |------|------|
9
+ | **Auto** | Push to `main` with Conventional Commits → `version.yml` bumps `VERSION`, stamps artifacts, commits `chore(release): X.Y.Z`, tags `vX.Y.Z` |
10
+ | **Force** | `Release-As: X.Y.Z` trailer on a commit, or `workflow_dispatch` on `version.yml` with a level |
11
+ | **Manual publish** | `workflow_dispatch` on `npm-publish.yml` (also runs on `v*` tags) |
12
+
13
+ Never push/deploy/merge without an explicit operator instruction when operating as an agent.
14
+
15
+ ## What fires (happy path)
16
+
17
+ ```
18
+ main push (feat/fix/…)
19
+ → version.yml (bump VERSION + stamps + bundle + tag vX.Y.Z)
20
+ → npm-publish.yml (OIDC Trusted Publishing; post-publish version + dist-tags.latest)
21
+ → Vercel (host) (build:site → .vercel-static; headers from vercel.json)
22
+ → sbom.yml on tag (CycloneDX artifact)
23
+ ```
24
+
25
+ Scorecard runs on a weekly schedule + `main` pushes (`.github/workflows/scorecard.yml`).
26
+
27
+ ## Success signals
28
+
29
+ 1. GitHub Actions: `version` green; `npm-publish` green (**not** soft-skip) with registry presence **and** `dist-tags.latest === VERSION` (FIND-094).
30
+ 2. `npm view @cyberskill/design version` **and** `npm view @cyberskill/design dist-tags.latest` both equal root `VERSION`.
31
+ 3. `npm-hello-smoke` green on the tip (fail-closed registry install of that VERSION — FIND-087).
32
+ 4. Production: `/` is Storybook; `/VERSION` is plain semver; `/_audit/run.html` fast board green.
33
+ 5. Artifacts: `cyclonedx-sbom` on the tag run; Scorecard SARIF on Security / Actions artifacts.
34
+ 6. Post-deploy Health protocol in `docs/deploy.md`.
35
+
36
+ ## Historical unpublished tags (do not backfill)
37
+
38
+ | Tag | Status | Operator action |
39
+ |-----|--------|-----------------|
40
+ | `v1.2.1` | Historical git tag; **not** published to npm as `latest` | Leave unpublished |
41
+ | `v1.3.0` | Historical git tag; **not** published to npm as `latest` | Leave unpublished |
42
+ | `v1.3.1` (current tip) | **Publish target** when operator instructs | Publish **only** `1.3.1` as `dist-tags.latest` |
43
+
44
+ Do **not** publish `1.2.1` or `1.3.0` to close registry drift — close FIND-102 by publishing **`1.3.1` only** (operator-gated). npm `latest` may still read `1.2.0` until that publish lands.
45
+
46
+ ## Failure / recovery
47
+
48
+ | Symptom | Action |
49
+ |---------|--------|
50
+ | `npm-publish` 403 / EOTP / ENEEDAUTH / 404 / 402 on tag or `workflow_dispatch` | **Fail-closed.** Fix Trusted Publisher binding for repo **`cyberskill-official/design`** (FIND-088) / 2FA / OIDC; re-run on the tag. Soft-skip must **not** report success for these. |
51
+ | Soft-skip `already_published` | Expected no-op — confirm registry; do not treat as success if you intended a new version. |
52
+ | Soft-skip `missing_secrets` | Expected only **off** GitHub Actions (local). On GHA release this is a hard fail. |
53
+ | `version.yml` push soft-warn | Set `PUSH_TOKEN` or allow Actions to push past branch protection (see `docs/ci-cd.md`). |
54
+ | `npm-hello-smoke` red (VERSION missing on registry) | Expected until publish lands (FIND-087). Follow **Incident: unpublished release** below — do not set `NPM_HELLO_ALLOW_LOCAL_PACK` on the canonical repo. |
55
+ | Nightly `design-system-gates` red | GitHub notifies the default workflow subscribers; see “Scheduled-job alerting” below. Re-run `workflow_dispatch`. |
56
+ | Partial Vercel deploy | Re-run deploy; follow Health re-run protocol; do not invent live header claims without `curl -sSI`. |
57
+
58
+ ## Incident: unpublished release (FIND-100 / FIND-101 / FIND-107)
59
+
60
+ Use when git/`VERSION`/site are ahead of npm `dist-tags.latest` (e.g. tip `1.3.1`, registry still `1.2.0`).
61
+
62
+ 1. **Confirm skew** — `cat VERSION`; `npm view @cyberskill/design version`; `npm view @cyberskill/design dist-tags.latest`; note git tags `v1.2.1` / `v1.3.0` / `v1.3.1` as historical vs publish target.
63
+ 2. **Do not** treat a green `npm-publish` soft-skip (or a green `npm-hello` local-pack proof) as evidence the version is public — smoke is fail-closed; publish auth errors hard-fail on GHA.
64
+ 3. **Fix Trusted Publisher** if needed — npm package settings must list org/repo **`cyberskill-official/design`**, workflow **`npm-publish.yml`**, and the correct environment (FIND-088). Classic `NPM_TOKEN` is disallowed. CI uses a clean OIDC userconfig (no empty `_authToken` from `actions/setup-node`) so Trusted Publishing can exchange the GitHub OIDC token.
65
+ 4. **Operator-gated publish** — only after an explicit instruction: re-run `npm-publish.yml` on tag **`v1.3.1`** (or `workflow_dispatch` at that tip). Publish target is **`1.3.1` only**; do not backfill `1.2.1` / `1.3.0`.
66
+ 5. **Verify** — `npm view @cyberskill/design@1.3.1 version` and `dist-tags.latest` both `1.3.1`; re-run `npm-hello-smoke` (or wait for the next gates run) to green.
67
+ 6. **Rollback / deprecate (if a bad version reached `latest`)** — `npm deprecate @cyberskill/design@<bad> "…"` and/or `npm dist-tag add @cyberskill/design@<good> latest`. npm does not allow unpublishing recent public versions in the normal case — prefer deprecate + retag `latest`. Site/Vercel rollback is independent (`docs/deploy.md`).
68
+
69
+ ## Scheduled-job alerting (area 27)
70
+
71
+ - Nightly cron `0 3 * * *` on `design-system-gates.yml`.
72
+ - On schedule failure the workflow’s `notify-schedule-failure` job (`needs` every scheduled job — FIND-108) emits an Actions error annotation and fails when `contains(needs.*.result, 'failure')`, which triggers **GitHub’s native workflow-failure email** to repository watchers / the last actor as configured under Settings → Notifications.
73
+ - Optional: add a repo Actions variable / secret for Slack webhook later; do not require it for green CI.
74
+ - Operators should watch the Actions badge in `docs/ci-cd.md` and enable “Actions” notification for failed workflows.
75
+
76
+ ## Product vs CyberOS
77
+
78
+ See `docs/decisions.md` §13. The npm package and portable tree are the product; `.cyberos/` is orchestration only.
package/docs/sync.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Repo ↔ Claude Design — round-trip fidelity & two-way sync
2
2
 
3
- The GitHub repo (`cyberskill-official/design-system`) is the **source of truth**. Claude Design sessions are ephemeral project filesystems — this doc is how they stay in sync without losing work.
3
+ The GitHub repo (`cyberskill-official/design`) is the **source of truth**. Claude Design sessions are ephemeral project filesystems — this doc is how they stay in sync without losing work.
4
4
 
5
5
  ## Does the repo restore the full system intact?
6
6
 
package/docs/vi/ci-cd.md CHANGED
@@ -6,24 +6,25 @@
6
6
 
7
7
  ## Badge
8
8
 
9
- [![Design System Gates](https://github.com/cyberskill-official/design-system/actions/workflows/design-system-gates.yml/badge.svg)](https://github.com/cyberskill-official/design-system/actions/workflows/design-system-gates.yml)
9
+ [![Design System Gates](https://github.com/cyberskill-official/design/actions/workflows/design-system-gates.yml/badge.svg)](https://github.com/cyberskill-official/design/actions/workflows/design-system-gates.yml)
10
10
 
11
11
  ## Những gì đã nối (`.github/workflows/design-system-gates.yml`)
12
12
 
13
13
  1. **`fast-gates`** — `npm ci` + Playwright Chromium cache, serve repo, mở `_audit/run.html` headless (`_audit/ci/run-gates.mjs`), fail khi hard-gate fail. Upload import-report khi fail. Hàng mới trên board (hardening Th7 2026 thêm 8: token-format-parity, version-stamp, support-runtime-identity, package-exports-integrity, template-lang-parity, dtcg-typing, design-md-parity, bundle-freshness) được nhận tự động — runner đọc `window.__run`, không phải danh sách gate phía job.
14
14
  2. **`token-provenance`** — kiểm Node không trình duyệt: natives + `provenance.json` khớp sha-256 nguồn DTCG.
15
15
  3. **`unit-tests`** — `npm run test:unit` (11 contract test Node thuần; nối vào CI bởi hardening Th7 2026 — trước đó chỉ local). Gồm `test-element-packs` (freshness `node scripts/generate-element-packs.mjs --check`).
16
- 4. **`node-prechecks`** — authority Node không trình duyệt: `_audit/ci/check-bundle-freshness.mjs` (nguồn chân lý bundle-freshness — khám phá source đầy đủ gồm file mới/xóa; hàng board chỉ re-hash file ghi trong header) và `scripts/generate-design-md.mjs --check` (`DESIGN.md` gốc byte-equals regeneration).
16
+ 4. **`node-prechecks`** — authority Node không trình duyệt: `_audit/ci/check-bundle-freshness.mjs` (nguồn chân lý bundle-freshness — khám phá source đầy đủ gồm file mới/xóa; hàng board chỉ re-hash file ghi trong header), `scripts/generate-design-md.mjs --check` (`DESIGN.md` gốc byte-equals regeneration), `scripts/flatten-styles.mjs --check` + `scripts/generate-contrast-report.mjs --check` (FIND-026), và `_audit/ci/check-manifest-coverage.mjs` (FIND-078).
17
+ 4b. **`storybook-build`** — `npm run build:storybook` rồi `test-storybook-freshness.mjs --require` (FIND-074 / FIND-075). Storybook gãy sẽ fail CI trước deploy.
17
18
  5. **`docs-consistency-blocker`** — merge blocker `docs-consistency` + `bilingual-parity`.
18
19
  6. **`whole-set-audits`** — quyết định B của owner: mọi push/PR, cộng nightly `0 3 * * *` và `workflow_dispatch` (responsive + language + theme overflow, ~15–20 phút).
19
20
  7. **`figma-variables-push`** — trên push `main` + thủ công. **`FIGMA_TOKEN` / `FIGMA_FILE_KEY` trống → soft-skip** (exit 0 + report — cùng trung thực với Code Connect). Quyết định A (non-Enterprise): Variables REST cũng **soft-skip khi API 403** (và lỗi plan/scope liên quan) sau khi secret chứng minh mở file. Soft-skip ≠ sync Variables live. Xem `docs/figma.md`.
20
21
  8. **`code-connect`** — trên PR + `main` + thủ công. Quyết định 1C: dry-run luôn (config + 105 mapping); publish soft-skip khi thiếu `FIGMA_TOKEN` / `FIGMA_FILE_KEY` hoặc API 403/404/429. Xem `docs/figma.md`.
21
- 9. **`regenerate-tokens`** (pull request) + **`regenerate-tokens-push`** (push `main` / schedule / thủ công) — path filter phủ element seeds/packs (`element-seeds.json`, `elements.css`, mirror JSON/JS/DTCG, `generate-element-packs.mjs`) cộng natives / `VERSION`. Regenerate chạy `npm run tokens:elements` rồi `node _audit/ci/generate-native-tokens.mjs`. Trên **pull request** job chạy với `contents: read`, không bao giờ push, và **exit 1** khi có drift: chạy cả hai ở local rồi commit. Chỉ job song sinh push/schedule/thủ công giữ `contents: write` và auto-commit với `DS_PUSH_TOKEN` (hoặc `github.token`).
22
- 10. **`npm-hello-smoke`** — chứng minh consumer registry: `cd examples/npm-hello && npm ci && npm run smoke` (**hard fail**; package public). Path-filtered trên push/PR khi `examples/npm-hello/**`, bề mặt publish package, hoặc workflow này đổi; luôn chạy trên schedule / `workflow_dispatch`.
22
+ 9. **`regenerate-tokens`** (pull request) + **`regenerate-tokens-push`** (push `main` / schedule / thủ công) — path filter phủ element seeds/packs (`element-seeds.json`, `elements.css`, mirror JSON/JS/DTCG, `generate-element-packs.mjs`) cộng natives / `VERSION`. Regenerate chạy `npm run tokens:elements` rồi `node _audit/ci/generate-native-tokens.mjs`. Trên **pull request** job chạy với `contents: read`, không bao giờ push, và **exit 1** khi có drift: chạy cả hai ở local rồi commit (freshness bắt buộc trên PR). Chỉ job song sinh push/schedule/thủ công giữ `contents: write` và có thể auto-commit với `PUSH_TOKEN` (hoặc `github.token`). Auto-commit **không** dùng `[skip ci]` — tip được push phải chạy lại bảng gate (FIND-021 / TASK-IMP-008); lần chạy thứ hai deterministic là no-op.
23
+ 10. **`npm-hello-smoke`** — chứng minh consumer registry cho `@cyberskill/design@$VERSION` (**fail-closed**, FIND-087): `examples/npm-hello` → `npm install` từ registry + `npm run smoke`. Nếu VERSION **không** có trên registry, job **fail** (không soft-skip pack proof trên `cyberskill-official/design`). Fork chỉ được opt-in pack local khi đặt biến repo **`NPM_HELLO_ALLOW_LOCAL_PACK=true`**. Path-filtered trên push/PR khi `examples/npm-hello/**`, bề mặt publish package, hoặc workflow này đổi; luôn chạy trên schedule / `workflow_dispatch`.
23
24
 
24
- Workflow riêng **`version`** (`.github/workflows/version.yml`): mỗi push lên `main`, bump `VERSION` theo Conventional Commits + stamp artifact; commit `chore(release): X.Y.Z`, push, và tạo tag `vX.Y.Z`. Bỏ qua chính commit release (an toàn vòng lặp). Owner có thể force level qua `workflow_dispatch` hoặc trailer `Release-As:`. Cần `contents: write` (cùng pattern `DS_PUSH_TOKEN` như `regenerate-tokens-push` nếu branch protection chặn `github.token`).
25
+ Workflow riêng **`version`** (`.github/workflows/version.yml`): mỗi push lên `main`, bump `VERSION` theo Conventional Commits + stamp artifact; rồi regen natives/provenance hash và `npm run build:bundle` để tip sạch gate; commit `chore(release): X.Y.Z`, push, và tạo tag `vX.Y.Z`. Bỏ qua chính commit release (an toàn vòng lặp). Owner có thể force level qua `workflow_dispatch` hoặc trailer `Release-As:`. Cần `contents: write`. Checkout dùng `secrets.PUSH_TOKEN || github.token` — nếu branch protection yêu cầu status checks (hoặc chặn `github.token`), đặt repository secret **`PUSH_TOKEN`** (fine-grained PAT, Contents: Read and write); không thì bump tính được nhưng push soft-warn và không land.
25
26
 
26
- Workflow riêng **`npm-publish`** (`.github/workflows/npm-publish.yml`): `workflow_dispatch` + tag `v*` (thường do `version.yml` tạo); pack dry-run luôn; publish qua **npm Trusted Publishing (OIDC)** (`id-token: write`, không `NPM_TOKEN` trên bước publish). Soft-skip khi auth / 403 / 404 / EOTP / conflict phiên bản. License **UNLICENSED**. Trusted Publisher trên npmjs phải khớp filename workflow `npm-publish.yml`. Publishing access package: **Require 2FA and disallow tokens** (OIDC vẫn chạy; token classic bị từ chối).
27
+ Workflow riêng **`npm-publish`** (`.github/workflows/npm-publish.yml`): `workflow_dispatch` + tag `v*` (thường do `version.yml` tạo); pack dry-run luôn; publish qua **npm Trusted Publishing (OIDC)** (`id-token: write`, không `NPM_TOKEN` trên bước publish). Soft-skip chỉ **`already_published`** và `missing_secrets` thật sự **ngoài GHA**. Trên tag / `workflow_dispatch`, **ENEEDAUTH** / **404** / **402** / **403** / **EOTP** **fail closed**. Sau publish assert `npm view @cyberskill/design@VERSION` **và** `dist-tags.latest === VERSION` (FIND-094). License **UNLICENSED**. Trusted Publisher trên npmjs phải khớp filename workflow `npm-publish.yml` cho repo **`cyberskill-official/design`** (FIND-088). Publishing access package: **Require 2FA and disallow tokens** (OIDC vẫn chạy; token classic bị từ chối).
27
28
 
28
29
  Workflow riêng **`native-store`** (`.github/workflows/native-store.yml`): PR + `main` (path-filtered) + `workflow_dispatch`; dry-run scaffold luôn; kiểm signed-release soft-skip khi thiếu `ASC_KEY_ID` / `ASC_ISSUER_ID` / `ASC_KEY_P8` / `PLAY_SERVICE_ACCOUNT_JSON`. **Không bao giờ submit** lên App Store / Play — sample vẫn là sample. Xem `examples/native/README.md`.
29
30
 
@@ -42,16 +43,18 @@ Node **22** trên runner (tránh deprecation action Node 20). Cache key Playwrig
42
43
 
43
44
  ## Quyền auto-commit token
44
45
 
45
- Chỉ `regenerate-tokens-push` yêu cầu `permissions: contents: write`; job song sinh cho pull request `regenerate-tokens` giữ `contents: read` và checkout không lưu credential, nên PR không bao giờ kích hoạt push của bot. Nếu org khóa default token read-only, đặt repository secret `DS_PUSH_TOKEN` (fine-grained PAT, Contents read/write) hoặc mở write tại org Actions settings. Xem ghi chú trước trong git history / org settings.
46
+ `regenerate-tokens-push` và `version.yml` yêu cầu `permissions: contents: write` và checkout với `secrets.PUSH_TOKEN || github.token`. Job song sinh pull request `regenerate-tokens` giữ `contents: read` và không bao giờ push. Nếu ruleset/branch protection yêu cầu status checks trước khi push (hoặc khóa default Actions token), đặt repository secret **`PUSH_TOKEN`** (fine-grained PAT, Contents: Read and write trên repo này) hoặc cho phép GitHub Actions bypass ruleset. Không có thì bot push soft-warn và exit 0; drift nội dung vẫn hard-fail qua `token-provenance` / fast-gates / `node-prechecks`.
46
47
 
47
- ## Branch protection (khuyến nghị)
48
+ ## Branch protection (khuyến nghị) — NV-18.1 ngoài repo
48
49
 
49
- Require status checks trên `main` trước merge:
50
+ **NV-18.1 (assessment):** check nào **bắt buộc** trước khi merge `main`, admin có bị ràng buộc không, và CODEOWNERS / review / conversation-resolution có được enforce — đều là **cài đặt GitHub, không nằm trong repo**. Trạng thái quan sát từ clone: **không biết / xác nhận ngoài băng**. Không bịa giá trị ruleset live từ YAML workflow.
51
+
52
+ **Khuyến nghị** (chỉ tài liệu — áp dụng trong GitHub Settings → Branches / Rulesets, hoặc `gh api` admin):
50
53
 
51
54
  - `fast-gates`
52
55
  - `docs-consistency-blocker`
53
56
 
54
- Tùy chọn: `whole-set-audits` (dài). Cấu hình dưới repo **Settings → Branches** hoặc qua `gh api` (admin).
57
+ Tùy chọn: `whole-set-audits` (dài).
55
58
 
56
59
  ## Chạy local
57
60
 
@@ -74,11 +77,11 @@ node _audit/ci/generate-native-tokens.mjs
74
77
  - **Hàng visual / component baseline side-by-side** — chỉ advisory (drift đánh giá bằng mắt). So sánh `%` pixel Playwright là gate **hard** (job `pixel-diff` + hàng Pixel CI trên board).
75
78
  - **Figma Variables** — soft-skip khi thiếu secret (cùng trung thực với Code Connect) hoặc write Variables gặp non-Enterprise / API **403**; report artifact ghi lại skip. Soft-skip ≠ sync live.
76
79
  - **Code Connect publish** — soft-skip khi thiếu secret hoặc API 403/404/429 (Quyết định 1C).
77
- - **npm publish** — Trusted Publishing (OIDC) trên `npm-publish.yml`; soft-skip khi auth / conflict (Quyết định 1C).
80
+ - **npm publish** — Trusted Publishing (OIDC) trên `npm-publish.yml`; soft-skip khi `already_published` / `missing_secrets` ngoài GHA; trên GHA release **ENEEDAUTH** / **404** / **402** / **403** / **EOTP** fail closed + kiểm version **và** `dist-tags.latest` sau publish (Quyết định §7 / FIND-020 / FIND-094).
78
81
  - **Native store signed release** — soft-skip khi thiếu `ASC_*` / `PLAY_SERVICE_ACCOUNT_JSON` (Quyết định 1C); submit store vẫn tắt.
79
- - **npm-hello registry smoke** — **hard fail** (`examples/npm-hello` → `npm ci` + `npm run smoke`); chứng minh đường install public `@cyberskill/design` (không soft-skip).
82
+ - **npm-hello registry smoke** — **fail-closed** (`examples/npm-hello` → install registry + `npm run smoke`); thiếu `@cyberskill/design@$VERSION` thì job fail (FIND-087). Pack proof local chỉ opt-in trên fork (`NPM_HELLO_ALLOW_LOCAL_PACK`).
80
83
 
81
- Job `figma-variables-push` hay `code-connect` xanh — và do đó badge hay hàng Status xanh — chỉ có nghĩa job exit 0; nó **không** chứng minh sync Figma live, vì soft-skip và publish thật nhìn từ ngoài giống hệt nhau. Mở report artifact của run (`figma-push-report.json` / `code-connect-report.json`) để biết cái nào đã xảy ra.
84
+ Job `figma-variables-push` hay `code-connect` vẫn exit 0 khi soft-skip (Quyết định 1C), nhưng FIND-095 làm outcome hiện trên trang run công khai qua `$GITHUB_STEP_SUMMARY` kèm annotation riêng (`::error::` trên schedule/main, `::warning::` nơi khác) — soft-skip không còn xanh im lặng giống publish thật. Report artifact (`figma-push-report.json` / `code-connect-report.json`) vẫn mang chi tiết.
82
85
 
83
86
  ## Soft-skip dry-run (local)
84
87
 
@@ -19,10 +19,13 @@ CyberSkill cấp cho **CyberSkill** (công ty và các team kỹ thuật sản p
19
19
 
20
20
  Package vẫn **UNLICENSED**. Cài từ npm registry **không** tự cấp quyền redistribution. Redistribution ngoài các sản phẩm trên — gồm publish fork, đổi license, hoặc ship package như dependency của sản phẩm bên thứ ba không liên quan — cần **grant viết thêm** ghi trong file này (hoặc văn bản thay thế).
21
21
 
22
+ **Ngoại lệ bên thứ ba:** font OFL trong `fonts/`, runtime MIT trong `_vendor/`, và các asset liệt kê ở `THIRD-PARTY-NOTICES.md` giữ license **upstream**. Khẳng định UNLICENSED và grant này **không** thay thế các điều khoản đó; người redistribute phải tuân `fonts/OFL.txt` và file notices.
23
+
22
24
  ## Ghi chú phạm vi
23
25
 
24
26
  - **Clone / subtree / link tĩnh** repo này cho cùng các sản phẩm trên được cover giống npm install.
25
27
  - **Tooling chỉ-host** (Storybook, `_audit/`) không nằm trong tarball npm và không redistribution theo grant này.
28
+ - **Notices bên thứ ba:** xem `THIRD-PARTY-NOTICES.md` và `fonts/OFL.txt` ở root.
26
29
  - Thêm sản phẩm CyberSkill mới: cập nhật `docs/products.md` (+ VI) qua quyết định maintainer, rồi thêm hàng sản phẩm tại đây trong cùng change.
27
30
  - Duyệt **team/client bên ngoài**: thêm stanza grant có ngày bên dưới (team/org, sản phẩm đã nêu, liên hệ). Không invent ánh xạ product → element tại đây — giữ ở `docs/products.md`.
28
31
 
@@ -54,7 +54,7 @@ import { Button, TextField } from "@cyberskill/design";
54
54
  import "@cyberskill/design/styles.css";
55
55
  ```
56
56
 
57
- Thêm `transpilePackages: ["@cyberskill/design"]` trong Next.js (JSX ship dạng source). React và react-dom là **peerDependencies** — app của bạn cung cấp. Types publish dùng `export type *` (**TypeScript 5.0+**). **Không** import `_esm/cs.mjs` (hay `@cyberskill/design/legacy`) vào bundler SSR; đường đó self-ensure React qua CDN và side-load `_ds_bundle.js` chỉ cho browser.
57
+ Thêm `transpilePackages: ["@cyberskill/design"]` trong Next.js (JSX ship dạng source). Entry mặc định (`_esm/react.mjs`) là **barrel `"use client"`** — Server Component App Router có thể `import { Button } from "@cyberskill/design"` mà không cần shim client từng import. React và react-dom là **peerDependencies** — app của bạn cung cấp. Types publish dùng `export type *` (**TypeScript 5.0+**). **Không** import `_esm/cs.mjs` (hay `@cyberskill/design/legacy`) vào bundler SSR; đường đó self-ensure React qua CDN và side-load `_ds_bundle.js` chỉ cho browser.
58
58
 
59
59
  **Browser / no-build:** import `@cyberskill/design/legacy` (`_esm/cs.mjs`) hoặc tiếp tục dùng đường cây tĩnh bên dưới. Tarball đã publish là **cả cây portable** (styles, tokens, components, templates, guidelines, docs, UI kits) — không phải subset “chỉ lib” tối thiểu. Tooling chỉ-host (Storybook, `_audit/`) không nằm trong `files[]`.
60
60
 
@@ -64,11 +64,11 @@ Thêm `transpilePackages: ["@cyberskill/design"]` trong Next.js (JSX ship dạng
64
64
 
65
65
  ## Adopt (hai đường, cộng shortcut module)
66
66
 
67
- **1. Static / prototype / mock — link stylesheet.** Copy `styles.css` (+ `tokens/`, `base/`, `fonts/` mà nó `@import`, hoặc serve cả cây) và link; bạn có mọi token `--cs-*`, class component `.cs-*`, và bề mặt Liquid Glass. Compose bằng class (xem `templates/kitchen-sink.html`). Copy mọi asset bạn tham chiếu từ `assets/`.
67
+ **1. Static / prototype / mock — link stylesheet.** Cho **static production**, link bản flatten `dist/styles.min.css` (cũng `@cyberskill/design/styles.min.css`) — một file, không waterfall `@import`. Giữ `styles.css` (+ `tokens/`, `base/`, `fonts/` mà nó `@import`) làm đường source/dev dễ đọc, hoặc serve cả cây. Cách nào cũng có mọi token `--cs-*`, class component `.cs-*`, và bề mặt Liquid Glass. Compose bằng class (xem `templates/kitchen-sink.html` và `examples/static-hello/`). Copy mọi asset bạn tham chiếu từ `assets/`.
68
68
 
69
- **2. React production — nạp compiled bundle.** Link `styles.css` và `<script src="_ds_bundle.js">`, rồi đọc component từ namespace. **Resolve theo prefix, không hardcode:** bundle expose `window.CyberSkillDesignSystem_<projectId>`, và hậu tố 6-hex đó do compiler gán và **đổi khi import vào project khác**:
69
+ **2. React production — nạp compiled bundle.** Link `dist/styles.min.css` (hoặc `styles.css` khi dev) và `<script src="_ds_bundle.js">`, rồi đọc component từ namespace. **Resolve theo prefix, không hardcode:** bundle expose `window.CyberSkillDesignSystem_<projectId>`, và hậu tố 6-hex đó do compiler gán và **đổi khi import vào project khác**:
70
70
  ```html
71
- <link rel="stylesheet" href="<path>/styles.css">
71
+ <link rel="stylesheet" href="<path>/dist/styles.min.css">
72
72
  <script src="<path>/_ds_bundle.js"></script>
73
73
  <script>
74
74
  const CS = window[Object.keys(window).find(k => /^CyberSkillDesignSystem_/.test(k))];
@@ -76,7 +76,7 @@ Thêm `transpilePackages: ["@cyberskill/design"]` trong Next.js (JSX ship dạng
76
76
  </script>
77
77
  ```
78
78
  Đây đúng là những gì `_audit/consumer-smoke-test.html` chạy (và assert xanh) — và `ds-base.js` của templates làm tương tự, publish alias ổn định `window.CyberSkillDS`.
79
- **2b. React bundler-native (Next / Vite / SSR) — entry package mặc định.** `import { Button, TextField } from "@cyberskill/design"` resolve tới `_esm/react.mjs` (cũng export như `@cyberskill/design/react`). Re-export mọi component từ JSX source với **React là peer external** — không CDN, không side-load `_ds_bundle.js`. Bundler của bạn phải transpile package (Next: `transpilePackages: ["@cyberskill/design"]`). Vẫn tự link `styles.css`. Regenerated bằng `npm run build:react-entry`; parity được gate bởi `package-exports-integrity` + `test:react-entry`.
79
+ **2b. React bundler-native (Next / Vite / SSR) — entry package mặc định.** `import { Button, TextField } from "@cyberskill/design"` resolve tới `_esm/react.mjs` (cũng export như `@cyberskill/design/react`). File generated bắt đầu bằng `"use client"` để consumer App Router / RSC import component tương tác trực tiếp. Re-export mọi component từ JSX source với **React là peer external** — không CDN, không side-load `_ds_bundle.js`. Bundler của bạn phải transpile package (Next: `transpilePackages: ["@cyberskill/design"]`). Vẫn tự link styles (`styles.min.css` khuyến nghị cho sheet static production). Regenerated bằng `npm run build:react-entry`; parity được gate bởi `package-exports-integrity` + `test:react-entry`.
80
80
 
81
81
  **2c. ESM browser legacy — một import, không build.** `import { Button, TextField } from "@cyberskill/design/legacy"` (hoặc `"<path>/_esm/cs.mjs"`) — module tự ensure React qua **umd-react@19.2.8** (CDN pin SRI; React 19 chính thức không còn UMD; bỏ qua khi đã có `window.React`), side-load `_ds_bundle.js` một lần, resolve namespace theo prefix, và re-export mọi component (`_audit/esm-smoke-test.html` giữ danh sách export khóa với manifest). Vẫn tự link `styles.css`. **Không dùng cho Next/SSR.** App bundler vẫn cung cấp peer `react` / `react-dom` (`^18 || ^19`).
82
82
 
@@ -84,7 +84,7 @@ Thêm `transpilePackages: ["@cyberskill/design"]` trong Next.js (JSX ship dạng
84
84
 
85
85
  **Token máy đọc được.** `tokens/tokens.json` + `tokens/tokens.js` (ESM) + `tokens/tokens.dtcg.json` (W3C DTCG, cho Tokens Studio/Style Dictionary) expose mọi token nhóm theo category + map theme/element — cho pipeline native/mobile/design-tool. **Native build ship sẵn** trong `tokens/native/` (SwiftUI `CSTokens.swift` · Compose `CSTokens.kt` · Flutter `cs_tokens.dart`) kèm `tokens/provenance.json` (release, source sha-256, quy tắc chuyển đổi, sha-256 từng target); gate `token-pipeline` giữ chúng khóa với nguồn DTCG.
86
86
 
87
- **HTML tĩnh / không React / không build tooling.** Link `styles.css` và compose bằng class `.cs-*` — catalog đầy đủ trong `templates/kitchen-sink.html`.
87
+ **HTML tĩnh / không React / không build tooling.** Link `dist/styles.min.css` (production) hoặc `styles.css` (source/dev) và compose bằng class `.cs-*` — catalog đầy đủ trong `templates/kitchen-sink.html`.
88
88
 
89
89
  **Font, kể cả mặt chữ display.** Cả ba họ chữ brand đều self-host trong `fonts/` và khai báo ở `tokens/fonts.css` (được `styles.css` `@import`): **Be Vietnam Pro** (`--cs-font-family-ui`), **JetBrains Mono** (`--cs-font-family-mono`), và **Space Grotesk** (`--cs-font-family-display`, variable 300–700, có subset tiếng Việt). Mặt chữ display là **opt-in** — không thứ gì trong DS trỏ vào nó. Sản phẩm muốn mặt chữ tiêu đề thêm `class="cs-display-face"` (hoặc đặt `--cs-heading-family: var(--cs-font-family-display)`) trên scope; các utility heading theo sau, body copy vẫn ở mặt chữ UI.
90
90
 
@@ -92,7 +92,7 @@ Consumer bỏ qua `styles.css` để tự kiểm soát font loading (`cyberskill
92
92
 
93
93
  ## Bốn trục
94
94
 
95
- Đặt Theme (`data-theme` — light / dark / system), Element (`data-cs-element` + `data-cs-variant`), Language (`lang` / Language tweak trên template), và Style (`data-cs-style`) trên một container; mọi thứ bên trong re-skin không cần đổi code (xem `templates/playground.html`). Mặc định: `light · tho · en · liquid-glass` (vắng theme / style ≡ các mặc định đó). Bilingual: component resolve chuỗi từ `lang` (`lang="vi"` trên mọi container → tiếng Việt đầy đủ).
95
+ Đặt Theme (`data-theme` — light / dark / system), Element (`data-cs-element` + `data-cs-variant`), Language (`lang` / Language tweak trên template), và Style (`data-cs-style`) trên một container; mọi thứ bên trong re-skin không cần đổi code (xem `templates/playground.html`). Mặc định: `light · tho · vi · liquid-glass` (vắng theme / style ≡ các mặc định đó; đặt `lang="en"` cho bề mặt EN). Bilingual: component resolve chuỗi từ `lang` (`lang="en"` trên mọi container → tiếng Anh đầy đủ; không đặt → fallback tiếng Việt trước).
96
96
 
97
97
  ## Nâng cấp
98
98
 
@@ -1,6 +1,6 @@
1
1
  # Báo cáo contrast — cặp elemental (APCA)
2
2
 
3
- Tạo 2026-07-27 · quét tại pin VERSION 1.1.1.
3
+ Tạo · quét tại VERSION 1.3.8.
4
4
 
5
5
  **Doctrine của sweep này:** chữ ngồi trên `-bright` hoặc `-tint`, không bao giờ trên mid-tone `-accent`. Accent chỉ cho bar, border, progress, fill không chữ. (Rule trong `tokens/elements.css` và conventions.)
6
6
 
@@ -90,6 +90,84 @@ Pack được generate từ `tokens/element-seeds.json` (thang soft / middle / d
90
90
 
91
91
  **Failures: 0.** All text pairings pass Lc ≥ 60.
92
92
 
93
+ ## Element-pack link contrast (WCAG AA · body text)
94
+
95
+ `--cs-color-link` is scored at **4.5:1** (regular-weight body). `--cs-color-text-accent` remains on the APCA Lc ≥ 60 bold-label floor above.
96
+ | Scope | Pairing | fg / bg | Ratio | Verdict |
97
+ |---|---|---|---|---|
98
+ | tho · sand | link on white (body) | #7B6D4A / #FFFFFF | 5.09:1 | ✓ |
99
+ | tho · sand | link on element page (body) | #7B6D4A / #FFFDF8 | 5.00:1 | ✓ |
100
+ | tho · sand | link on tint (body) | #7B6D4A / #FBF7EA | 4.75:1 | ✓ |
101
+ | tho · sand | link on raised cream (body) | #7B6D4A / #FBF4E9 | 4.66:1 | ✓ |
102
+ | tho · sand | link on dark panel (body) | #F6EBCC / #221710 | 14.75:1 | ✓ |
103
+ | tho · clay | link on white (body) | #201300 / #FFFFFF | 18.19:1 | ✓ |
104
+ | tho · clay | link on element page (body) | #201300 / #FFFDF8 | 17.89:1 | ✓ |
105
+ | tho · clay | link on tint (body) | #201300 / #FFF6E7 | 16.97:1 | ✓ |
106
+ | tho · clay | link on raised cream (body) | #201300 / #FBF4E9 | 16.65:1 | ✓ |
107
+ | tho · clay | link on dark panel (body) | #E4CDA8 / #221710 | 11.35:1 | ✓ |
108
+ | hoa | link on white (body) | #A15E43 / #FFFFFF | 5.00:1 | ✓ |
109
+ | hoa | link on element page (body) | #A15E43 / #FFFDF8 | 4.92:1 | ✓ |
110
+ | hoa | link on tint (body) | #A15E43 / #FFF4F0 | 4.63:1 | ✓ |
111
+ | hoa | link on raised cream (body) | #A15E43 / #FBF4E9 | 4.58:1 | ✓ |
112
+ | hoa | link on dark panel (body) | #FED3C3 / #221710 | 12.79:1 | ✓ |
113
+ | hoa · plasma | link on white (body) | #846B64 / #FFFFFF | 4.92:1 | ✓ |
114
+ | hoa · plasma | link on element page (body) | #846B64 / #FFFDF8 | 4.84:1 | ✓ |
115
+ | hoa · plasma | link on tint (body) | #846B64 / #FEF4F2 | 4.55:1 | ✓ |
116
+ | hoa · plasma | link on raised cream (body) | #846B64 / #FBF4E9 | 4.50:1 | ✓ |
117
+ | hoa · plasma | link on dark panel (body) | #FCE6DF / #221710 | 14.63:1 | ✓ |
118
+ | hoa · lava | link on white (body) | #280D00 / #FFFFFF | 18.30:1 | ✓ |
119
+ | hoa · lava | link on element page (body) | #280D00 / #FFFDF8 | 18.00:1 | ✓ |
120
+ | hoa · lava | link on tint (body) | #280D00 / #FFF4EF | 16.94:1 | ✓ |
121
+ | hoa · lava | link on raised cream (body) | #280D00 / #FBF4E9 | 16.74:1 | ✓ |
122
+ | hoa · lava | link on dark panel (body) | #F8C4AA / #221710 | 11.24:1 | ✓ |
123
+ | thuy | link on white (body) | #027B8B / #FFFFFF | 4.99:1 | ✓ |
124
+ | thuy | link on element page (body) | #027B8B / #FFFDF8 | 4.91:1 | ✓ |
125
+ | thuy | link on tint (body) | #027B8B / #E7FBFF | 4.67:1 | ✓ |
126
+ | thuy | link on raised cream (body) | #027B8B / #FBF4E9 | 4.57:1 | ✓ |
127
+ | thuy | link on dark panel (body) | #99EEFF / #221710 | 13.38:1 | ✓ |
128
+ | thuy · mist | link on white (body) | #48787E / #FFFFFF | 4.93:1 | ✓ |
129
+ | thuy · mist | link on element page (body) | #48787E / #FFFDF8 | 4.85:1 | ✓ |
130
+ | thuy · mist | link on tint (body) | #48787E / #EBFAFC | 4.60:1 | ✓ |
131
+ | thuy · mist | link on raised cream (body) | #48787E / #FBF4E9 | 4.51:1 | ✓ |
132
+ | thuy · mist | link on dark panel (body) | #CDF4F9 / #221710 | 14.94:1 | ✓ |
133
+ | thuy · ocean | link on white (body) | #011A21 / #FFFFFF | 17.93:1 | ✓ |
134
+ | thuy · ocean | link on element page (body) | #011A21 / #FFFDF8 | 17.64:1 | ✓ |
135
+ | thuy · ocean | link on tint (body) | #011A21 / #EAFAFF | 16.75:1 | ✓ |
136
+ | thuy · ocean | link on raised cream (body) | #011A21 / #FBF4E9 | 16.41:1 | ✓ |
137
+ | thuy · ocean | link on dark panel (body) | #ABD6E3 / #221710 | 11.23:1 | ✓ |
138
+ | moc | link on white (body) | #517B13 / #FFFFFF | 5.01:1 | ✓ |
139
+ | moc | link on element page (body) | #517B13 / #FFFDF8 | 4.93:1 | ✓ |
140
+ | moc | link on tint (body) | #517B13 / #EFFCE3 | 4.70:1 | ✓ |
141
+ | moc | link on raised cream (body) | #517B13 / #FBF4E9 | 4.58:1 | ✓ |
142
+ | moc | link on dark panel (body) | #C5ED9C / #221710 | 13.33:1 | ✓ |
143
+ | moc · bamboo | link on white (body) | #687451 / #FFFFFF | 4.99:1 | ✓ |
144
+ | moc · bamboo | link on element page (body) | #687451 / #FFFDF8 | 4.91:1 | ✓ |
145
+ | moc · bamboo | link on tint (body) | #687451 / #F4F9ED | 4.67:1 | ✓ |
146
+ | moc · bamboo | link on raised cream (body) | #687451 / #FBF4E9 | 4.57:1 | ✓ |
147
+ | moc · bamboo | link on dark panel (body) | #E5F1D2 / #221710 | 14.89:1 | ✓ |
148
+ | moc · forest | link on white (body) | #051D00 / #FFFFFF | 17.76:1 | ✓ |
149
+ | moc · forest | link on element page (body) | #051D00 / #FFFDF8 | 17.47:1 | ✓ |
150
+ | moc · forest | link on tint (body) | #051D00 / #ECFDE7 | 16.72:1 | ✓ |
151
+ | moc · forest | link on raised cream (body) | #051D00 / #FBF4E9 | 16.26:1 | ✓ |
152
+ | moc · forest | link on dark panel (body) | #B0D8A3 / #221710 | 11.02:1 | ✓ |
153
+ | kim | link on white (body) | #966700 / #FFFFFF | 4.96:1 | ✓ |
154
+ | kim | link on element page (body) | #966700 / #FFFDF8 | 4.88:1 | ✓ |
155
+ | kim | link on tint (body) | #966700 / #FFF5E7 | 4.59:1 | ✓ |
156
+ | kim | link on raised cream (body) | #966700 / #FBF4E9 | 4.54:1 | ✓ |
157
+ | kim | link on dark panel (body) | #FDD89F / #221710 | 12.94:1 | ✓ |
158
+ | kim · titanium | link on white (body) | #826D54 / #FFFFFF | 4.92:1 | ✓ |
159
+ | kim · titanium | link on element page (body) | #826D54 / #FFFDF8 | 4.84:1 | ✓ |
160
+ | kim · titanium | link on tint (body) | #826D54 / #FDF5ED | 4.56:1 | ✓ |
161
+ | kim · titanium | link on raised cream (body) | #826D54 / #FBF4E9 | 4.51:1 | ✓ |
162
+ | kim · titanium | link on dark panel (body) | #FBE8D2 / #221710 | 14.67:1 | ✓ |
163
+ | kim · steel | link on white (body) | #1F1400 / #FFFFFF | 18.13:1 | ✓ |
164
+ | kim · steel | link on element page (body) | #1F1400 / #FFFDF8 | 17.83:1 | ✓ |
165
+ | kim · steel | link on tint (body) | #1F1400 / #FEF6E7 | 16.88:1 | ✓ |
166
+ | kim · steel | link on raised cream (body) | #1F1400 / #FBF4E9 | 16.59:1 | ✓ |
167
+ | kim · steel | link on dark panel (body) | #DFCBA5 / #221710 | 11.04:1 | ✓ |
168
+
169
+ **Link failures: 0.**
170
+
93
171
  ## Dark pack APCA floors (gate twin)
94
172
 
95
173
  Targets match `_audit/apca-dark-preview.html`: bright ≥ 75 · accent ≥ 60 · on/strong ≥ 75 · ink-on-tint ≥ 75.
@@ -16,11 +16,11 @@ Mọi bề mặt render resolve bốn scope trực giao — không bao giờ mã
16
16
  | **Language** | `lang` / Language tweak | copy EN · VI | registry component + props template |
17
17
  | **Style** | `data-cs-style` (tuỳ chọn; vắng ≡ `liquid-glass`) | pack vật liệu bề mặt | `tokens/styles.css` |
18
18
 
19
- Pack Style duy nhất hiện nay là **liquid-glass**. Bất biến dưới mọi trục: anchors Umber/Ochre, voice, họ chữ, màu semantic status, vòng focus Ochre 3px, sàn APCA, target ≥44px.
19
+ Pack Style duy nhất hiện nay là **liquid-glass**. Bất biến dưới mọi trục: anchors Umber/Ochre, voice, họ chữ, màu semantic status, chỉ báo focus ghép (contour text-primary + halo ochre), sàn APCA, target ≥44px.
20
20
 
21
21
  ## Ngữ pháp đặt tên
22
22
 
23
- - Tokens: `--cs-<layer>-<role>[-<state>]` (`--cs-color-text-muted`). Layer role element: `--cs-accent-*` (hợp đồng 9-token — xem header `tokens/elements.css`).
23
+ - Tokens: `--cs-<layer>-<role>[-<state>]` (`--cs-color-text-muted`). Layer role element: `--cs-accent-*` (hợp đồng 10-token gồm `--cs-accent-on-strong` — xem header `tokens/elements.css`).
24
24
 
25
25
  - Classes: `.cs-<block>[-<part>][--modifier]`. Data attributes: `data-theme`, `data-cs-element`, `data-cs-variant`, `data-cs-style`.
26
26
 
@@ -36,7 +36,7 @@ Pack Style duy nhất hiện nay là **liquid-glass**. Bất biến dưới mọ
36
36
 
37
37
  **Template mới** — `templates/<slug>/<Slug>.dc.html` với `<!-- @template name description -->` đầu tiên; `ds-base.js` giữ single-entry (`styles.css` thôi — rebind một dòng đã validate end-to-end Th7 2026 từ path sâu hơn; scaffold test đã gỡ) và publish alias độc lập-id `window.CyberSkillDS` lúc bundle load, nên mount DS component qua `component-from-global-scope="CyberSkillDS.…"` — **không bao giờ** hậu tố `CyberSkillDesignSystem_<projectId>` hardcode (đổi khi re-import; enforce bởi `_audit/namespace-portability.html`); chỉ inline styles; 1–3 tweaks (không plain copy/color — in-place editing phủ những cái đó); printable docs thêm khối print `@page` + `.cs-desk`/`.cs-sheet` và **không** mang dark tweak (print chỉ light).
38
38
 
39
- **Element variant mới** — ở trong envelope oklch của element; đặt 9 token hợp đồng `--cs-accent-*` cộng `--cs-color-text-accent`/`--cs-color-link` (APCA-check cả hai); aurora wash kế thừa từ element qua `.cs-aurora-wash` (chỉ override class rule nếu variant thật sự cần ảnh riêng); đối tác gradient (`grad-b`) phải theo Tương sinh.
39
+ **Element variant mới** — ở trong envelope oklch của element; đặt 10 token hợp đồng `--cs-accent-*` (gồm `--cs-accent-on-strong` ≥4.5:1 trên `-strong`) cộng `--cs-color-text-accent`/`--cs-color-link` (APCA-check cả hai); aurora wash kế thừa từ element qua `.cs-aurora-wash` (chỉ override class rule nếu variant thật sự cần ảnh riêng); đối tác gradient (`grad-b`) phải theo Tương sinh.
40
40
 
41
41
  **Ship check** — chạy compiler check đến sạch, rồi chạy `_audit/docs-consistency.html` **và** `_audit/namespace-portability.html` (cả hai phải xanh). Pin theo `VERSION` (auto-bump trên `main`).
42
42
 
@@ -52,7 +52,7 @@ Pack Style duy nhất hiện nay là **liquid-glass**. Bất biến dưới mọ
52
52
 
53
53
  - **Mặt chữ display là một token role, không phải mặc định thứ hai (Th7 2026).** `--cs-font-family-display` ship Space Grotesk (variable 300–700, có subset tiếng Việt) self-host cạnh Be Vietnam Pro và JetBrains Mono, để sản phẩm có mặt chữ tiêu đề — `cyberskill.world` / Lumi — tiêu thụ một role của DS thay vì tự nuôi `@font-face` ngoại lệ riêng. **Không thứ gì trong DS trỏ vào nó**: `--cs-heading-family` vẫn resolve về `--cs-font-family-ui`, mọi template và UI kit không đổi, và một scope chọn dùng qua `.cs-display-face` (`base/typography.css`) — chỉ chuyển `--cs-heading-family`, body copy vẫn ở mặt chữ UI. Space Grotesk không có 800, nên heading weight extrabold clamp về 700 trong family đó (CSS không nhảy sang Be Vietnam Pro vì thiếu weight). Thêm một họ chữ nghĩa là thêm face vào `tokens/fonts.css` **và** file vào `fonts/` **và** entry vào `fonts[]`/`brandFonts[]` của `_ds_manifest.json` — `_audit/font-face-integrity.html` fail nếu một trong ba lệch, và fail nếu family nào thiếu subset tiếng Việt. Điều này không đổi mapping sản phẩm → element nào; `docs/products.md` vẫn khóa.
54
54
 
55
- - Biến thể ngôn ngữ render **tách hoàn toàn**: đổi Language tweak cho artifact một ngôn ngữ đầy đủ — không dịch dở trong heading, label, helper text, footer, hoặc body. Phần bilingual-by-design duy nhất là lockup tên + slogan CyberSkill (`Hiện Thực Hoá Ý Chí · Turn Your Will Into Real`, dạng chuẩn — không bao giờ một nửa lẻ) và khối identity pháp lý A4 (tên pháp lý công ty + khẩu hiệu nhà nước). Mọi thứ khác đổi theo tweak (Th7 2026).
55
+ - Biến thể ngôn ngữ render **tách hoàn toàn**: đổi Language tweak cho artifact một ngôn ngữ đầy đủ — không dịch dở trong heading, label, helper text, footer, hoặc body. Phần bilingual-by-design duy nhất là lockup tên + slogan CyberSkill (`Hiện Thực Hoá Ý Chí · Turn Your Will Into Real`, dạng chuẩn — không bao giờ một nửa lẻ) và khối identity pháp lý A4 (tên pháp lý công ty + khẩu hiệu nhà nước). Mọi thứ khác đổi theo tweak — gồm skip link và eyebrow loại tài liệu (FIND-125). Chuỗi catalog `<title>` / `@template name` giữ tiếng Anh cho chỉ mục template, không phải chrome chế độ ngôn ngữ; `_audit/template-lang-parity.html` che chúng khi quét lexicon rò EN (Th7 2026; follow-up FIND-125).
56
56
 
57
57
  - **Namespace được resolve, không hardcode** — compiler đặt tên bundle global `window.CyberSkillDesignSystem_<projectId>`, và hậu tố 6-hex dẫn từ project id, nên đổi khi hệ thống import vào project khác. Templates đọc alias ổn định `window.CyberSkillDS` mà `ds-base.js` publish lúc bundle load; cards, trang UI-kit, email standalone, và audit harness resolve theo prefix (`window[Object.keys(window).find(k=>/^CyberSkillDesignSystem_[0-9a-f]{6}$/.test(k))]`). `_ds_bundle.js`/`_ds_manifest.json` và snapshot đóng băng `_audit/exports/` hợp pháp mang hậu tố thật (chúng *định nghĩa* nó). Enforce bởi `_audit/namespace-portability.html`, quét mọi source index bởi manifest — giữ re-import **zero-touch** (Th7 2026).
58
58
 
@@ -70,9 +70,22 @@ Mọi template export; đường đúng phụ thuộc archetype. Mọi đường
70
70
 
71
71
  **Hành vi font lúc export.** Font brand self-host (`Be Vietnam Pro` UI, `Space Grotesk` display opt-in, mono). PPTX giữ **tên** font brand nhưng không embed file — trên máy không có chúng PowerPoint thay fallback (text Unicode VN được giữ; shape có thể reflow nhẹ). Để hand-off tự chứa đầy đủ, hoặc bảo người nhận cài font, hoặc export với swap web-safe (`fontSwaps:[{from:'Be Vietnam Pro',to:'Arial'}]` — Arial phủ tiếng Việt). Standalone HTML **embed** font (`@font-face` inline từ `styles.css`), nên luôn trung thành offline (Th7 2026).
72
72
 
73
+ ## Trạng thái dữ liệu bất đồng bộ (FIND-020 — pattern mỏng)
74
+
75
+ Bề mặt collection sở hữu một fetch phải phân biệt **loading · error · empty · idle-có-dữ-liệu**. Không render thông báo trống trong lúc request đang chạy.
76
+
77
+ | Trạng thái | Ghép | Ghi chú |
78
+ |---|---|---|
79
+ | **loading** | `Skeleton` (hàng bảng / dòng) + chuỗi status lịch sự | `aria-busy` trên vùng; giữ header cột khi có thể |
80
+ | **error** | `Result status="error"` (hoặc `errorState` tùy chỉnh) | `role="alert"`; host cung cấp thử lại |
81
+ | **empty** | `emptyState` / `EmptyState` | Chỉ sau phản hồi idle thành công với zero rows |
82
+ | **idle** | Các hàng bình thường | Mặc định — bỏ `state` cho specimen tĩnh |
83
+
84
+ **API tham chiếu:** `DataTable` nhận `state?: "idle" \| "loading" \| "error"` cùng `loadingState` / `errorState` / `loadingRows` tùy chọn. Các component collection khác (`DataGrid`, `TreeTable`, …) nên theo cùng triad khi có async ownership — không invent API loading song song từng bề mặt. Tác vụ dài không dạng bảng vẫn dùng `Spinner` (`role="status"`) hoặc `ProgressBar` với `aria-label` thật; ghép `Skeleton` trang trí (`aria-hidden`) với chuỗi status live.
85
+
73
86
  ## Accessibility (base/a11y.css + component ARIA)
74
87
 
75
- - **Focus rings có hệ thống** — vòng ochre global `:where(:focus-visible)` trong `base/reset.css`, cộng vòng ochre 3px theo component (`components/forms/navigation/controls`). Không bao giờ đặt `outline: none` trên state `:focus-visible` mà không thay vòng (regression menu-item, đã sửa Th7 2026). Soft glow 5px trong `interaction.css` chồng lên vòng 3px, không thay thế.
88
+ - **Focus rings có hệ thống** — global `:where(:focus-visible)` trong `base/reset.css` là chỉ báo **ghép**: contour 2px `text-primary` (≥3:1 trên light và dark) cộng halo ochre 5px. Control wrapper (`SearchField`, `NumberField`, `PromptInput`, `Terminal`, `CommandPalette`) lặp lại qua `:focus-within`. Không bao giờ đặt `outline: none` trên control focusable mà không có vòng thay thế/wrapper.
76
89
 
77
90
  - **Touch targets** — `base/a11y.css` đảm bảo 44px trên `@media (pointer: coarse)` (tabs, pagination, menu items, stepper, checkbox/radio; segmented 40px trong track đã pad). Mật độ chuột desktop được giữ. `pointer: coarse` dùng có chủ đích thay breakpoint chiều rộng — touch laptop ở 1280px vẫn cần 44px. Button mang khối coarse riêng trong `components.css`.
78
91
 
@@ -53,7 +53,7 @@ Giữ plan Figma hiện tại. Variables REST API chỉ Enterprise — job ghi *
53
53
  **Trạng thái: LAUNCH `@cyberskill/design@1.1.0`; pin theo `VERSION` (auto-bump trên main từ Th8 2026); CI publish dùng npm Trusted Publishing; token bị cấm** (Th7 2026; LAUNCH 2026-07-27)
54
54
 
55
55
  - `package.json` là `private: false`; tên package **`@cyberskill/design`** (`publishConfig.access: public`); `repository.url` khớp repo GitHub này cho provenance.
56
- - Workflow `.github/workflows/npm-publish.yml`: `id-token: write` + runner GitHub-hosted; **không** đặt `NPM_TOKEN` / `NODE_AUTH_TOKEN` trên bước publish (OIDC). Trusted Publisher trên npm phải liệt kê filename workflow **`npm-publish.yml`** cho `cyberskill-official/design-system`. Soft-skip khi auth / 403 / 404 / EOTP / conflict phiên bản.
56
+ - Workflow `.github/workflows/npm-publish.yml`: `id-token: write` + runner GitHub-hosted; **không** đặt `NPM_TOKEN` / `NODE_AUTH_TOKEN` trên bước publish (OIDC). Trusted Publisher trên npm phải liệt kê filename workflow **`npm-publish.yml`** cho `cyberskill-official/design` (FIND-088). Soft-skip chỉ no-op mong đợi (`already_published` / EPUBLISHCONFLICT, và `missing_secrets` thật sự ngoài GHA). Trên GHA tag / `workflow_dispatch`, **ENEEDAUTH** / **404** / **402** / **403** / **EOTP** fail job; publish thành công được khóa bởi `npm view @cyberskill/design@VERSION` **và** `dist-tags.latest === VERSION` (FIND-020 / FIND-094).
57
57
  - **Publishing access** trên npmjs: **Require two-factor authentication and disallow tokens** (Trusted Publishing OIDC vẫn hoạt động; token publish classic / granular bị từ chối). Secret `NPM_TOKEN` dài hạn đã thu hồi.
58
58
  - License giữ **UNLICENSED**; phiên bản theo root `VERSION`. Dùng đã duyệt ghi tại **`docs/consumer-grant.md`** (sản phẩm portfolio CyberSkill). Cài từ registry một mình không phải license công khai.
59
59
 
@@ -100,7 +100,7 @@ Theo dõi vận hành — không phải marketing sản phẩm, không phải ba
100
100
 
101
101
  ~~4. Đường consumer registry đầu tiên~~ — **xong** (Th7 2026): `examples/npm-hello/` cài `@cyberskill/design` cho identity **Lumi** đã khóa; ghi trong `docs/consuming.md` (+ VI) và `docs/release-notes.md`. Nay target **1.1.1** post-LAUNCH.
102
102
 
103
- 5. **Drift native + element-pack trên PR là fail-closed** (Th7 2026) — `regenerate-tokens` chạy read-only trên pull request và exit 1 thay vì push; chỉ `regenerate-tokens-push` (main / schedule / thủ công) giữ `contents: write`. Khi PR fail, chạy `npm run tokens:elements && node _audit/ci/generate-native-tokens.mjs` rồi commit packs + natives. Xem `docs/ci-cd.md`.
103
+ 5. **Drift native + element-pack trên PR là fail-closed** (Th7 2026; FIND-021 re-gate Th8 2026) — `regenerate-tokens` chạy read-only trên pull request và exit 1 thay vì push; chỉ `regenerate-tokens-push` (main / schedule / thủ công) giữ `contents: write`. Khi PR fail, chạy `npm run tokens:elements && node _audit/ci/generate-native-tokens.mjs` rồi commit packs + natives. Auto-commit từ `regenerate-tokens-push` **không** được dùng `[skip ci]` để tip chạy lại bảng gate (TASK-IMP-008); freshness fail-closed trên PR vẫn là đường chính cho contributor. Thực tế required-check branch protection là NV-18.1 (ngoài repo). Xem `docs/ci-cd.md`.
104
104
 
105
105
  6. **Shadcn gap / Wave 2** (Th7 2026) — CyberSkill đã phủ ~53/62 primitive shadcn dưới dạng CS-native (forms / overlays / nav / data). Đã ship: pilot `ScrollArea`, `Collapsible`, `AspectRatio`; Wave 2 `AlertDialog` (vs Popconfirm), `Item` row primitive, `NativeSelect`. Phần còn lại của shadcn là non-port có chủ đích (block, Tailwind, Radix CLI). **Không** port những thứ đó — map pattern sang `templates/` + `ui_kits/` hiện có.
106
106
 
@@ -112,6 +112,43 @@ Theo dõi vận hành — không phải marketing sản phẩm, không phải ba
112
112
 
113
113
  Storybook `FullMatrix` bắt buộc khi public primary có ≥1 trong {size enums, variant enums, state keys} (sàn contract ≥28 story). Schema sidecar đã đủ cho template content-hole; template chỉ-axis bỏ qua theo thiết kế. Xem `docs/quality-gates.md` và `docs/storybook.md`.
114
114
 
115
+ ## 11. Giấy tờ audit — lưu trữ, không xóa
116
+
117
+ **Lựa chọn owner: archive-don't-delete** (Th8 2026; TASK-IMP-001 / assessment FIND-001)
118
+
119
+ - Artifact audit UX / AT / chất lượng đã đóng MUST còn tái dựng được từ clone mới. Ưu tiên `_audit/archive/` và/hoặc `docs/audits/` thay vì xóa im lặng.
120
+ - Commit `8646dbf` đã xóa plan UX audit 8/8, AT matrix, README archive, và `TASK-UX-AUDIT-20260808` “không để lại bản ghi vĩnh viễn.” Nội dung được khôi phục tại `docs/audits/ux-audit-2026-08-08/` (kèm pointer `_audit/archive/ux-audit-2026-08-08/`). Các quyết định D1–D6 lịch sử trong plan đã khôi phục **không** ủy quyền xóa audit trail trong tương lai.
121
+ - Chỉnh status-feed / ledger để che công việc audit đã làm nên tránh; nếu commit phải ledger-exempt, ghi exemption và đường dẫn archive trong cùng change.
122
+ - Claim cần provenance (ví dụ “AT complete”, “lawyer-validated”) đòi bản ghi có ngày trong cây hoặc mục quyết định tường minh — không chỉ attestation miệng.
123
+
124
+ ## 12. Kế hoạch tiến hóa 12 tháng — trạng thái adoption
125
+
126
+ **Lựa chọn owner: partially superseded (gần); giữ plan làm tham chiếu dài hạn** (Th8 2026; TASK-IMP-001 / assessment FIND-002)
127
+
128
+ Nguồn: `docs/plans/cyberskill-design-system-audit-and-12-month-evolution-plan.md` (ngày audit 2026-07-28). PLAN `task-author` trước đó đã APPROVED (manifest `docs/tasks/.workflow/task-author.design-system-evolution.manifest.json`) nhưng hầu hết artefact chưa land vào `docs/tasks/`.
129
+
130
+ | Tầm | Trạng thái | Ghi chú |
131
+ |-----|------------|---------|
132
+ | Assessment Phases 0–2 (tập con FIND-001…025) | **Adopted** — hàng đợi improvement CyberOS đang chạy | `TASK-IMP-001`…`TASK-IMP-008` trong `docs/tasks/improvement/`; index `docs/tasks/BACKLOG.md` |
133
+ | Materialize đủ phase 0–6 của plan 12 tháng | **Deferred** | Không tự bung artefact PENDING trong manifest cho tới khi operator triage với tip `1.2.1` / hậu assessment |
134
+ | Việc đã ship ngoài backlog (vd. commit remediation UX 8/8) | **Superseded** với tư cách task sống | Bằng chứng ở `docs/audits/ux-audit-2026-08-08/` + git history; không mở lại trùng trừ khi có finding mới |
135
+
136
+ Phases 3–5 của **roadmap assessment** (RSC `"use client"`, APG Rating/Tree/Toolbar, CSP, SBOM, …) vẫn là follow-on hoãn — chưa author trong tranche này trừ khi operator yêu cầu.
137
+
138
+ ## 13. Ranh giới Product vs CyberOS
139
+
140
+ **Lựa chọn owner: hàng rào tường minh** (Th8 2026; assessment area 28 / TASK-IMP-020)
141
+
142
+ | Lớp | Ship tới consumer? | Nằm ở đâu | Mục đích |
143
+ |-------|---------------------|-------------|---------|
144
+ | **Product design system** | Có — `@cyberskill/design`, cây portable (`styles.css`, `tokens/`, `components/`, `templates/`, gates `_audit/`, host Storybook) | root repo (không-`.cyberos`) | Token, component, template, kit, quality gate xác định |
145
+ | **CyberOS orchestration** | Không (gitignored / agent kit cài được) | `.cyberos/`, `docs/tasks/`, BRAIN dưới `.cyberos/memory/` | Vòng đời task, cổng HITL, agent entry, workflow ship-tasks |
146
+
147
+ Quy tắc:
148
+ - Tiêu chí chấp nhận product không được đòi `.cyberos/` có mặt cho consumer hoặc cho `npm` publish.
149
+ - Agent vận hành repo này theo `.cyberos/AGENT-ENTRY.md`; người ship DS theo `docs/release-runbook.md`.
150
+ - Không chuyển token/component product vào `.cyberos/`; không publish orchestration CyberOS như một phần của gói npm.
151
+
115
152
  ## Cách đổi một quyết định
116
153
 
117
154
  Sửa dòng **Lựa chọn owner** tại đây. Người triển khai nối lại CI và docs liên quan trong cùng một change.
package/docs/vi/deploy.md CHANGED
@@ -48,11 +48,15 @@ Nếu host dưới **domain khác**, đổi absolute URL trong các meta tag cho
48
48
 
49
49
  **Có Storybook tại `/`:** chạy `npm run build:site` trên host hoặc CI, rồi serve `.vercel-static/` làm webroot. Cấu hình cùng redirect như `vercel.json` (`/dashboard`, `/dashboard/`, `/dashboard.html`, `/dashboard/:path*`, `/playground`, `/playground/`, `/playground/:path*` → `/`) nếu host hỗ trợ.
50
50
 
51
- Ví dụ Nginx (`.vercel-static` làm `root`):
51
+ Ví dụ Nginx (`.vercel-static` làm `root`) — **cùng security headers với `vercel.json`** (FIND-019):
52
+ ```nginx
52
53
  server {
53
54
  server_name design.cyberskill.world;
54
55
  root /var/www/design-system/.vercel-static;
55
56
  index index.html;
57
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self'; worker-src 'self' blob:; base-uri 'self'; form-action 'self'" always;
58
+ add_header X-Content-Type-Options "nosniff" always;
59
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
56
60
  location = /dashboard { return 301 /; }
57
61
  location = /dashboard/ { return 301 /; }
58
62
  location = /dashboard.html { return 301 /; }
@@ -60,8 +64,11 @@ server {
60
64
  location = /playground { return 301 /; }
61
65
  location /playground/ { return 301 /; }
62
66
  }
67
+ ```
63
68
  Path portable vẫn là file thật (`guidelines/atomic-view.html`, `_audit/run.html`, `styles.css`, v.v.). Storybook sở hữu `/` và `/index.html`.
64
69
 
70
+ **Netlify / Cloudflare Pages / tương tự:** file `_headers` ở root (và `public/_headers`) mirror cùng ba header cho host đọc `_headers` kiểu Netlify. Giữ khớp `vercel.json`.
71
+
65
72
  ## Checklist sau deploy
66
73
  1. Mở `/` — xác nhận Storybook load (không 404, directory listing, hay shell dashboard cũ).
67
74
  2. Mở `/_audit/run.html` — để board chạy xong, xác nhận mọi fast gate pass trên bản *đã deploy* (CDN cache, MIME type, hoặc file thiếu có thể lộ ở đây dù check local sạch).
@@ -86,12 +93,16 @@ Sau mỗi deploy production:
86
93
 
87
94
  ## Ghi chú Content-Security-Policy
88
95
 
89
- Static hosting có thể ship CSP chặt. Allowance điển hình cho hệ thống này:
90
- - `script-src 'self' https://unpkg.com` nếu load React từ CDN cho Atomic View
91
- - `font-src 'self'`
96
+ CSP chuẩn nằm trong `vercel.json` và được mirror ở `_headers` + snippet nginx ở trên (FIND-015 / FIND-019). Policy hiện tại:
97
+
98
+ `default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self'; worker-src 'self' blob:; base-uri 'self'; form-action 'self'`
92
99
 
93
- - `img-src 'self' data:`
94
- Ưu tiên self-host React cạnh `_ds_bundle.js` cho cài air-gapped.
100
+ Lý do:
101
+ - **Đã bỏ `unsafe-eval`** — React vendored local; Storybook static không cần `eval` toàn site.
102
+ - **Đã bỏ CDN script/connect** (`unpkg.com`, `cdnjs.cloudflare.com`) — local-first React / `_ds_bundle.js`.
103
+ - **Giữ `unsafe-inline`** — shell Storybook vẫn emit inline script/style; migrate nonce/hash là follow-on.
104
+ - Ưu tiên self-host React cạnh `_ds_bundle.js` cho cài air-gapped.
105
+ - Header thật trên production đã xác nhận 2026-08-09 qua `curl -sSI https://design.cyberskill.world/` (OQ-003 đóng — xem `docs/audits/nv-rendered-inrepo-2026-08-09.md`).
95
106
 
96
107
  ## OG đa domain
97
108