Skip to content
Draft
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
12 changes: 11 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@

## 检查清单

- [ ] `npm run verify` 通过
- [ ] 已按改动范围选择最小充分的测试层级
- [ ] `npm run test:fast` 通过(如涉及逻辑或代码)
- [ ] 相关 UI/E2E 测试通过(如涉及页面、导航、离线或 PWA)
- [ ] 数据库/Functions/集成测试通过(如涉及对应边界)
- [ ] `npm run test:full` 通过(合并前、发布前或高风险改动)
- [ ] 未提交 `.env`、`.vercel`、secret key 或个人数据
- [ ] 数据库变更附带迁移文件
- [ ] 已检查本次变更涉及的 README、公开 docs、CHANGELOG、发布说明及隐私/安全文档,并已同步实现差异
Expand All @@ -22,3 +26,9 @@
- [ ] 如包含外部贡献或第三方代码,已阅读 [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md),并在 PR 中说明来源与授权状态
- [ ] 已运行 `npm run public:check`,并复查暂存区没有内部、敏感或不必要文件
- [ ] 如果这是发布准备变更,已确认 `release-gate.config.json`、版本文件和发布说明同步

## 验证记录

- 选定层级:
- 命令与结果:
- 未运行或被环境阻塞的检查及原因:
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ jobs:
run: |
status="$(npx supabase status --output env 2>/dev/null)"
api_url="$(printf '%s\n' "$status" | sed -n 's/^API_URL=//p' | tr -d '"')"
db_url="$(printf '%s\n' "$status" | sed -n 's/^DB_URL=//p' | tr -d '"')"
publishable_key="$(printf '%s\n' "$status" | sed -n 's/^ANON_KEY=//p' | tr -d '"')"
test -n "$api_url"
test -n "$db_url"
test -n "$publishable_key"
echo "VITE_SUPABASE_URL=$api_url" >> "$GITHUB_ENV"
echo "SHADOW_MATE_TEST_DB_URL=$db_url" >> "$GITHUB_ENV"
echo "VITE_SUPABASE_PUBLISHABLE_KEY=$publishable_key" >> "$GITHUB_ENV"
echo "E2E_REAL_SUPABASE=1" >> "$GITHUB_ENV"
- name: Run database tests
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ dist/
.DS_Store
Thumbs.db

# Agent runtime harness (local-only, must not enter public repo)
.agent_context/
.claude/
.multica/
/attachments/

test-results/
playwright-report/
coverage/
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [1.4.0-rc] - 2026-08-16

### Added
- 行为积分体系:自定义积分项(加分/扣分)、快捷撤销(10 秒内)、历史受控扣分与按日补记;积分日历独立统计,不计入成长模块数量。
- 期初积分:切换到新账本时旧积分只读保留,家长可为每个孩子确认一次期初积分,重复确认会被拒绝。
- 奖励与兑换:家长创建奖励并设置所需积分,可设为当前目标;孩子攒够后兑换,联网确认后生效,家长兑现后标记“已兑现”。
- 学习包模块启停:家长可按孩子启用或隐藏整个学习包及其中每个模块;成长日历按已启用模块数显示 `已完成/n`。
- 程序化界面音效:完成行动、获得积分、再试一次、扣除积分、奖励兑现五类音效,可逐事件开关、调音量、试听并恢复默认(仅存本机)。
- 数据库迁移提案:积分、奖励、兑换、期初积分与成长漏斗聚合(待 Shadow Portal 控制面审批执行)。

### Changed
- 公开资料同步:README 标语与“现有能力”仅列已实现并验证的功能;页面 meta 与 PWA 安装描述统一文案;使用指南与应用内指南同步积分、奖励与兑换说明。
- 修正应用内指南离线语音包体积描述(115MB → 63.5MB `en_US-ljspeech-medium`)。

### Fixed
- 移除引用未导入 `CHECKIN_GROUPS` 的孤儿 `CHECKIN_MODULES` 常量(W2 重构后已无使用),修复整页渲染 `ReferenceError`。
- E2E 同步合并后的导航结构(学习页 → 模块入口)与指南语音包体积断言。

### Tests
- 发布候选全量验证通过:`npm run verify`(公开/安全/静态检查、构建、构建产物、覆盖率)、数据库 pgTAP 241/241、Edge Function 隔离守卫、E2E 66 通过 1 按环境跳过、`release:check` 通过(生产检查因无 `RELEASE_URL` 跳过)。

## [1.3.8] - 2026-08-15

### Changed
Expand Down
22 changes: 18 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,31 @@
npm.cmd ci
git config core.hooksPath .githooks
git config user.email "YOUR_GITHUB_NOREPLY_ADDRESS"
npm.cmd run verify
npm.cmd run test:fast
```

Get the noreply address from GitHub **Settings → Emails**. Do not use a personal or work mailbox in public commit metadata.

Maintainers may create an ignored `.security-local-denylist` file with one private term per line. The security check scans tracked and untracked candidate files without publishing the denylist itself.

## 测试范围与分层

先写清本次改动的范围、明确不做什么、验收条件和受影响边界,再按风险选择最小充分的验证层级。开发循环不要求每次小改动都运行全量测试;合并、发布和高风险边界仍必须经过完整门禁。

| 层级 | 适用场景 | 命令 |
| --- | --- | --- |
| 静态 | 文档、文案、低风险 CSS 或静态检查 | `npm run check` |
| 快速 | 纯逻辑、数据模型、控制器 | `npm run test:fast` |
| 页面 | 导航、设置、PWA、离线和可见交互 | `npm run test:ui`,或运行指定 E2E 文件 |
| 集成 | Supabase schema/RLS、Functions、认证、同步、导出/删除 | `npm run test:db`、`npm run test:functions`,以及受影响的 E2E |
| 完整 | 合并前、发布前、依赖/公开资源或高风险边界 | `npm run test:full` |

`test:fast` 当前包含全部 unit test 和 `check`,它是比浏览器/数据库测试更快的项目级入口,但不是 changed-only 测试。`verify` 负责公开范围、安全检查、静态检查、构建和覆盖率,不包含数据库、Functions 或 E2E;`test:full` 只在合并、发布或高风险边界运行。PR 必须记录实际选择的层级、命令、结果,以及未运行或被环境阻塞的检查。

## Required checks

- Use a branch and pull request; do not push directly to `main`.
- Run `npm run verify` before pushing.
- Before pushing, run the smallest sufficient layer for the changed surface; source/build/public-resource changes require `npm run verify`, with database, Functions and E2E layers added when affected. Run `npm run test:full` before merge or release.
- For a release tag, run `npm run build` followed by `npm run release:check`; the tag workflow repeats this against the final archive.
- Commit `package-lock.json` and pin dependency versions.
- Add explicit PostgreSQL grants and RLS policies in the same migration.
Expand All @@ -37,7 +51,7 @@ Maintainers may create an ignored `.security-local-denylist` file with one priva
- 检查本次 `git diff` 涉及的用户行为、数据模型、迁移、配置、测试命令、覆盖率、版本号和发布状态。
- 按影响范围同步 `README.md`、公开 `docs/`、`CHANGELOG.md`、`RELEASE_NOTES.md`、隐私/安全文档和 PR 说明;内部计划、法律记录和发布闸门不得放入公开目录。
- 代码、测试、迁移、配置和对应文档必须作为同一项工作提交并推送,禁止明知文档过期而先提交代码、之后再补文档。
- 提交前运行 `npm run public:check`、`git diff --cached --check` 和 `npm run verify`,并逐项复查 `git diff --cached --name-status` 与 `git diff --cached`,确认没有把内部、敏感或不必要文件加入提交。
- 提交前运行 `npm run public:check`、`git diff --cached --check` 和与改动范围匹配的测试层,并逐项复查 `git diff --cached --name-status` 与 `git diff --cached`,确认没有把内部、敏感或不必要文件加入提交;合并前或发布前补齐 `npm run test:full`
- 推送前重新核对远端仓库可见性、目标分支、PR base/head 和 PR 描述;任何不确定的文件先移出暂存区,不要“先提交再解释”。

## Release 闸门
Expand All @@ -59,7 +73,7 @@ Release 必须在 Tag 上执行,不把普通 PR 当作发布验收:

## GitHub 协作流程

- 本地先运行 `npm.cmd run verify`;涉及端到端流程时,再运行 `npm.cmd run test:e2e`。
- 本地先运行 `npm.cmd run test:fast`;涉及页面交互时,再运行 `npm.cmd run test:ui`;涉及数据库、认证或同步时,补充对应集成测试。合并前按风险矩阵运行 `npm.cmd run test:full`。
- 使用分支提交并推送,保持现有 SSH 远程仓库配置;不需要为每次 PR 重复配置 GitHub CLI。
- 分支推送后,优先使用已连接的 GitHub 插件创建、查看、Review 和合并 PR,避免通过浏览器重复填写表单。
- PR 作者不能批准自己的 PR;需要独立 Review 时邀请其他协作者,管理员按分支保护规则完成合并。
Expand Down
52 changes: 40 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
</p>

<p align="center">
<strong>把每天的学习,变成看得见的成长。</strong><br>
面向家庭的儿童学习打卡 PWA:学习、记录、同步,一处完成
<strong>陪伴有方法,成长有动力。</strong><br>
面向家庭的学习记录与成长反馈 PWA:多孩子、学习打卡、行为积分、离线使用与云端同步
</p>

<p align="center">
Expand All @@ -29,9 +29,11 @@

## 它能做什么

- **四个学习模块**:语文、数学、英语、绘本;每个模块内部的任务可以独立打卡和取消。
- **成长记录**:近 30 天按学习模块统计完成情况,用 `已完成/4` 直接说明当天进度。
- **积分日历**:行为积分单独记录,与学习模块分开,支持按日期查看和补记。
- **学习打卡**:语文、数学、英语、绘本四个学习模块;可按孩子启用或隐藏模块,每个模块内部的任务可以独立打卡和取消。
- **成长记录**:近 30 天按学习模块统计完成情况,用 `已完成/n` 直接说明当天进度(`n` 为孩子已启用的模块数)。
- **行为积分**:自定义积分项,加分、扣分、快捷撤销和补记;旧积分只读保留,家长可为孩子确认一次期初积分。
- **奖励与兑换**:设置奖励目标和分值,孩子攒够积分后可兑换;奖励在家长确认后标记为已兑现。
- **界面音效**:完成行动、获得积分、再试一次、扣除积分、奖励兑现五类程序化音效,可逐事件开关、试听、调音量并恢复默认(仅保存在当前设备)。
- **家庭空间**:一个家长管理多个学习者,切换孩子后加载对应的学习记录。
- **共享账号登录**:支持邮箱验证码和邮箱密码;可设置、修改或找回适用于 Shadow 系列产品的共享密码。
- **防重复操作**:提交、同步、删除和打卡等操作会拦截快速连点,避免重复创建或重复变更。
Expand Down Expand Up @@ -82,20 +84,38 @@ npm.cmd run dev
```powershell
npm.cmd run check
npm.cmd run build
npm.cmd run test:unit
npm.cmd run test:e2e
npm.cmd run test:fast
npm.cmd run test:ui
```

需要本地数据库测试时,先启动 Docker Desktop:

```powershell
supabase start
npm.cmd run test:db
supabase db lint --local --schema public --level warning --fail-on error
npm run supabase:local:start
npm run test:db
```

`supabase:local:start` 会转到同级 `shadow-size/merchant-admin`,启动共享本地 Supabase,并按 Shadow Portal 控制面的 SHA-256 校验结果加载 Shadow Mate 的 `learning_*` 业务 schema。控制面历史快照只会应用到 `127.0.0.1:54322`,不会复制到生产迁移目录,也不会连接生产数据库。

如果要验收登录、找回密码或其他 Edge Function,再开一个终端运行:

```powershell
npm run supabase:local:functions:serve
```

该命令会准备共享函数覆盖层并以前台方式运行本地函数服务;关闭该终端就会停止函数服务。

如果要对共享本地数据库执行 lint,请在 merchant-admin 目录运行:

```powershell
cd ../shadow-size/merchant-admin
npx supabase db lint --local --schema public --level warning --fail-on error
```

`test:coverage` 覆盖核心纯函数、学习状态机和防重复操作锁,语句、分支、函数和行覆盖率门槛均为 80%。`test:e2e` 覆盖离线导航、打卡、积分、日历、家庭空间、重复点击保护、邮箱验证码/密码登录、找回密码、数据生命周期和云端冲突限次重试;真实 Supabase E2E 需要额外配置环境变量。

日常开发按改动范围选择最小充分的检查:页面改动运行目标 UI 测试,数据库/认证/同步改动补充对应集成测试;合并或发布前运行 `npm.cmd run test:full`。`test:fast` 是静态检查加全部 unit test,不是 changed-only 测试。

## 工作方式

```text
Expand Down Expand Up @@ -124,7 +144,7 @@ src/learning-state.js 学习状态机与四个模块的打卡分组
src/cloud.js 验证码/密码登录、家庭空间、同步、导出与删除
src/action-lock.js 全局快速连点拦截与异步操作单次执行锁
src/icons.js Lucide 图标渲染与图标 hydration
supabase/migrations/ 家庭数据、RLS、生命周期和删除权限
supabase/migrations/ Shadow Mate 的 schema 提案与隔离 CI 测试副本
supabase/functions/ 账号级服务端删除
tests/unit/ 纯函数与学习状态机测试
tests/e2e/ 离线、云端和数据生命周期测试
Expand All @@ -134,7 +154,15 @@ tests/e2e/ 离线、云端和数据生命周期测试

当前部署配置位于 `src/config.js`,浏览器端只使用 publishable key。真正的数据隔离由 Supabase RLS、家庭成员关系和产品 ID 共同完成;绝不能把 secret key 或 `service_role` key 放进仓库。

数据库迁移位于 `supabase/migrations/`,包括:
### 共享 Supabase 与迁移边界

影伴接入共享 Supabase 后,日常本地验收必须通过 `npm run supabase:local:start`,由同级 `shadow-size/merchant-admin` 启动共享本地实例,并加载经 Shadow Portal 控制面校验的 Shadow Mate `learning_*` schema。需要验收 Auth 或 Edge Functions 时,再在第二个终端运行 `npm run supabase:local:functions:serve`。

仓库中的 `supabase/migrations/` 仍用于保存与代码同步的迁移提案和隔离 CI 测试副本,不是共享生产库的唯一发布目录。共享生产迁移的 canonical 文件、审批、发布和台账由 `shadow-portal/supabase/control-plane` 管理;不要在本仓库直接执行生产 `db push`、`migration repair` 或 linked SQL。

`supabase/config.toml` 的独立端口和迁移配置仅供 CI/隔离测试使用。不要在影伴仓库根目录直接运行裸 `supabase start` 来代替共享本地启动。

数据库迁移提案包括:

- 项目登记和共享多租户兼容性
- 家庭、成员、学习者和学习状态表
Expand Down
29 changes: 29 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# Release Notes

## v1.4.0-rc - 2026-08-16(发布候选,未正式发布)

Growth Loop MVP 发布候选。功能已实现,等待全量验证与迁移审批通过后发布;发布清单与回滚方案见本分支工作产物。

- **行为积分**:自定义积分项(加分/扣分)、快捷撤销(10 秒内)、历史受控扣分与按日补记;积分日历独立统计,不计入成长模块数量。
- **期初积分**:切换到新账本时旧积分只读保留,家长可为每个孩子确认一次期初积分,生成独立期初流水;重复确认会被拒绝。
- **奖励与兑换**:家长创建奖励并设置所需积分,可设为当前目标;孩子攒够后兑换,联网确认后生效,家长兑现后标记“已兑现”。
- **学习包模块启停**:家长可在“学习”页按孩子启用或隐藏整个学习包及其中每个模块;成长日历按已启用模块数显示 `已完成/n`。
- **程序化界面音效**:完成行动、获得积分、再试一次、扣除积分、奖励兑现五类音效,可在设置页逐事件开关、调音量、试听并恢复默认(仅存本机)。
- **公开资料同步**:README 标语与“现有能力”仅列已实现并验证的功能;页面 meta 与 PWA 安装描述统一为“面向家庭的学习记录与成长反馈 PWA,支持多孩子、学习打卡、行为积分、离线使用与云端同步。”;使用指南与应用内指南同步积分、奖励与兑换说明。
- **数据库提案**:新增积分、奖励、兑换、期初积分与成长漏斗聚合等迁移提案(经 Shadow Portal 控制面审批后执行,本仓库不直接执行生产迁移)。

### 验证结果

- 本分支为 `feat/growth-loop-release-candidate`,基于 Growth Loop 集成分支合并 W2/W4/W5 的 PR head 构建;正式发布以全量验证与生产迁移执行结果为准。

### 已知缺口

- 首页 Slogan 欢迎卡片(计划 4.5/10.3 的 E2E 要求)尚未实现,暂无对应 E2E 用例;不影响已上线功能,正式发布前需评估补上或明确延后。

### 部署清单(待执行,本候选未发布)

1. 全量验证通过后,将本分支变更合并到 `main`,由 Vercel 部署前端。
2. 生产迁移(积分/奖励/兑换/期初积分/漏斗聚合提案)经 Shadow Portal 控制面审批后串行执行;本仓库不直接执行生产迁移,执行顺序与回滚方案见迁移 runbook。
3. 生产域名验证 `/`、`/manifest.json` 与隐私页响应头(CSP/HSTS/X-Frame-Options,无 `unsafe-inline`)。
4. 发布后观察核心指标,异常按 runbook 回滚。

---

## v1.3.8 - 2026-08-15

- 将无 GMS Android 的离线英语语音模型改为从 `voice.shadow.wang` CDN 分发(`en_US-ljspeech-medium`,约 63.5MB):首次点击“听发音”时下载并缓存到浏览器,之后可离线合成,不上传录音。
Expand Down
Loading
Loading