Skip to content

Commit 800fb90

Browse files
committed
docs: archive expanded community tabs spec
1 parent 4cd0d00 commit 800fb90

17 files changed

Lines changed: 869 additions & 55 deletions

File tree

‎docs/biz/business-rules.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ This document records durable behavior that affects users or data consistency. R
1212

1313
## Visibility And Governance
1414

15-
- Public topic queries exclude deleted content, internal `dev`/`test` tabs, and content from blocked authors.
15+
- Public topic queries exclude deleted content, the non-public `dev` tab, and content from blocked authors.
1616
- Blocking hides a user's public content. Muting prevents new topics and replies. UI and bulk actions must not conflate these states.
1717
- Dangerous administration actions require explicit confirmation, authorization, self-action protection where applicable, and audit records.
18-
- `share`, `ask`, and `job` are author-selectable tabs; `good` is a display filter; `dev` and `test` are internal.
18+
- Author-selectable tabs are `share`, `ask`, `tech`, `ai`, `ideas`, `career`, `life`, `event`, and the role-restricted `job`. `good` is a display filter, `dev` is for API/client development, and `test` is retired.
19+
- The home order is `all / share / ask / tech / ai / ideas / career / life / event / job / dev / good`; `dev` is only visible to administrators, while `all` and visible `good` remain at the two ends.
20+
- Public `job` topics participate in the `all` feed while retaining recruiter authorization, structured `job_meta`, and the jobs zone.
1921

2022
## Replies, Collections, And Messages
2123

@@ -27,6 +29,8 @@ This document records durable behavior that affects users or data consistency. R
2729
## Publishing Controls
2830

2931
- Topic creation validates title, tab, content, account state, rate limit, moderation rules, and Turnstile when enabled.
32+
- The publishing UI presents mandatory community rules before the selected tab guidance. It prohibits illegal or abusive content, spam, unrelated advertising, exposed secrets or personal data, unattributed copying, and undisclosed commercial relationships.
33+
- Event topics use normal topic content for now and must state the time, place or online channel, organizer, registration information, and commercial relationship; there is no `event_meta` contract yet.
3034
- New-user publishing limits are configurable through site settings.
3135
- The public `/rss` feed contains recent public topics and excludes internal, deleted, and blocked-author content.
3236

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-08-09
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
## Context
2+
3+
当前 `tabs` 表负责首页按钮的 label、visible、sort order 和 scope,但有效 topic key 仍分散在共享 Zod enum、Web 发布/编辑表单、标签映射、API 可见性集合和 seed 中。首页将 `all` 作为合成项,其余项按数据库排序;`good` 虽存于注册表,却由 API 转换为精选条件。`job` 是带 `job_meta` 的 topic,但 `all` 查询当前排除它。`dev/test` 被当作 admin scope,其中测试数据库快照显示 `test` topic 精确值和规范化值均为 0。
4+
5+
本变更保留 `share/ask` 及全部历史 topic,不解决现存空 tab 数据。新增社区主题仍沿用单一 `topics.tab`,避免本次同时引入标签或多维分类模型。
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
11+
- 增量加入 `tech/ai/ideas/career/life/event`,并让 API、DB、Web 与文档使用同一有效集合。
12+
- 固定 `all` 最左、`dev` 位于公开项之后、`good` 最右,同时允许管理员调整中间公开项顺序。
13+
- 首页和发帖页按当前 Tab 呈现明确、可访问且响应式的说明与发布规范。
14+
- 保留 `job` 权限与 `job_meta`,同时将招聘作为常规首页 Tab 纳入 `all`。
15+
- 在确认零引用后退役 `test`,不丢失 topic。
16+
17+
**Non-Goals:**
18+
19+
- 不迁移 `share/ask` 或空 tab topic,不引入 topic tags/type/board 多维模型。
20+
- 不实现 `event_meta`、活动状态、活动专区或交易能力。
21+
- 不改变招聘角色授予、`job_meta` 字段或招聘专区筛选能力。
22+
- 不修改 Base UI/shadcn primitives。
23+
24+
## Decisions
25+
26+
### 使用共享有效 key 集合,展示元数据留在 Web
27+
28+
`packages/shared` SHALL 提供小写单词 key 的只读集合和创建/编辑 enum,API 与 Web 表单从该集合派生约束。Tab label 仍来自数据库,以保留后台改名能力;Web 维护按 key 索引的说明和发布提示,并以数据库 label 作为 Card 标题。测试 SHALL 断言 seed、共享 key、Web presentation map 和后台筛选不存在遗漏。
29+
30+
替代方案是让 API 完全信任 `tabs` 表。该方案会把运行时配置变成写入合约,并使 `good/dev` 等特殊语义无法只靠数据表达,因此不采用。
31+
32+
### 保留单一 tab 语义并采用增量集合
33+
34+
有效注册顺序为:
35+
36+
| 顺序 | key | label | scope |
37+
| ------------ | -------- | ----- | ------ |
38+
| 合成左端 | `all` | 全部 | public |
39+
| 10 | `share` | 分享 | public |
40+
| 20 | `ask` | 问答 | public |
41+
| 30 | `tech` | 技术 | public |
42+
| 40 | `ai` | AI | public |
43+
| 50 | `ideas` | 创意 | public |
44+
| 60 | `career` | 职场 | public |
45+
| 70 | `life` | 生活 | public |
46+
| 80 | `event` | 活动 | public |
47+
| 90 | `job` | 招聘 | public |
48+
| 固定倒数第二 | `dev` | 开发 | admin |
49+
| 固定右端 | `good` | 精华 | public |
50+
51+
`all` 不写入数据库。首页组合 SHALL 使用 `[all] + sortedVisiblePublicWithoutGood + [dev when allowed] + [good when visible]`,从而不依赖可编辑 sort order 保证端点位置。隐藏 `good` 时不渲染它,但其他项顺序不变。
52+
53+
替代方案是只给 `good/dev` 设置很大的 sort order。管理员仍可制造顺序冲突,不能满足“始终最右”的约束,因此不采用。
54+
55+
### 招聘是常规 Topic,结构化能力是附加层
56+
57+
`job` SHALL 进入 `all` feed,并与其他公开 topic 共享回复、收藏、置顶和精选能力。创建权限、普通 topic 与 job 的转换限制、`job_meta` 事务及招聘专区保持不变。`event` 本次只保存普通 topic 字段,时间、地点、组织方和报名方式由发布规范要求写入正文。
58+
59+
替代方案是继续从 `all` 排除招聘。该行为与“招聘是常规 Tab,只多结构化信息”的产品定义冲突,因此废弃。
60+
61+
### 首页首卡按 Tab 互斥选择
62+
63+
```mermaid
64+
flowchart TD
65+
T[当前首页 tab] --> C{all 或 good?}
66+
C -->|是| Coop[社区合作 Card]
67+
C -->|否| D{dev?}
68+
D -->|是| Dev[开发使用 Card]
69+
D -->|否| Info[当前 Tab 说明 Card]
70+
Coop --> Rest[最新回复 / 积分榜 / 无人回复 / 生态资源]
71+
Dev --> Rest
72+
Info --> Rest
73+
```
74+
75+
`all/good` 继续显示现有社区合作 Card;其余有效 Tab 不显示社区合作。桌面端说明位于右侧首位;移动端必须在 topic feed 前提供来自同一元数据的紧凑说明,完整 sidebar 模块仍可按现有 feed archetype 重排。
76+
77+
替代方案是在所有页面叠加说明 Card。该方案增加侧边栏长度且与用户确认的互斥规则冲突,因此不采用。
78+
79+
### 发帖页保留硬性规范并追加动态内容
80+
81+
发帖页右侧固定为:发布规范 Card、当前 Tab 说明 Card、`job_meta` 信息(仅 `job`)。发布规范使用 MUST 语气概括禁止违法、攻击、垃圾、无关广告、敏感信息泄漏、冒充原创及未披露商业关系等规则,并链接唯一权威业务规则。`JobMetaForm` 不得替换前两项。
82+
83+
移动端 SHALL 在提交按钮之前展示发布规范和当前 Tab 的紧凑说明;切换 Select 后说明可感知地更新,并通过现有 Field label/description 关系保持键盘和辅助技术可用。招聘无权限状态继续由 Web 禁用和 API 403 双重约束。
84+
85+
替代方案是保留一张混合“发布建议”。软性写作建议无法表达硬性治理规则,也无法清晰解释当前 Tab,故拆分为两个职责稳定的 Card。
86+
87+
## Database Change Audit
88+
89+
- 变更类型:只改 `tabs` 行和 seed,不改 PostgreSQL schema、索引、约束或 `topics` 行。
90+
- migration SHALL 幂等插入六个新公开 key,更新基线 label/scope/sort order,但不得覆盖现有 `visible` 运营值。
91+
- 删除 `test` 前 SHALL 在 migration 的同一事务中检查精确 `tab='test'` 及 `lower(btrim(tab))='test'`;任一计数非零即抛错并回滚。
92+
- migration SHALL 删除 `tabs.key='test'`;历史已执行 migration 不修改,新 migration 表达退役。
93+
- seed SHALL 不再插入 `test`,且继续保留已有行的 visible 值。
94+
- 本次不处理测试数据库快照中的空 tab topic,也不打印 topic 内容或用户数据。
95+
96+
## Risks / Trade-offs
97+
98+
- [新增 Tab 内容稀疏] → 保留后台 visible 开关,运营可隐藏低活跃项而不改变 API 合法性。
99+
- [招聘进入 all 增加商业信息密度] → 继续 recruiter 权限、结构化字段和治理规则,并监控列表占比。
100+
- [多处 key 映射漂移] → 共享合法集合并增加 registry/presentation/admin filter parity tests。
101+
- [旧客户端不认识新增返回值] → 旧 key 不变;新增值只出现在对应新 topic,OpenAPI 明确扩展 enum。
102+
- [删除 test 时快照与生产发生偏差] → 发布 migration 现场再次断言零引用,失败时停止部署而非自动重分类。
103+
- [移动端说明出现在列表或按钮之后] → 对首页和 compose 分别增加顺序验收测试及 375px 人工检查。
104+
- [Tab 含义存在交叉] → Card 提供短边界说明;本次接受单一主分类,不引入多标签模型。
105+
106+
## Migration Plan
107+
108+
1. 在测试服务器通过受控数据库连接只读统计 `topics.tab`,确认规范化 `test` 为 0,并记录聚合结果但不输出连接信息、凭据、用户内容或其他环境秘密;如需 SSH,仍须先打印并确认 `.cnode-ops.md` 中的 alias。
109+
2. 先发布兼容新增 key 的共享/API/Web 代码及 reviewed migration;migration 插入新 tabs、调整顺序并安全删除 `test`。
110+
3. 生成 OpenAPI,验证创建、编辑、首页查询、sidebar、招聘权限和 admin filters。
111+
4. 测试环境应用 migration 后检查普通用户与管理员 Tab 顺序、`job` 在 `all` 中可见、移动端说明位置及各 Card 互斥关系。
112+
113+
回滚应用版本时,新 tab topic 可能已存在,旧 API 无法编辑其 enum。因此数据库 migration 不做自动 down migration;回滚前须先停止新增 key 写入,并使用经审核的数据处置方案或恢复兼容代码。`test` 注册行可在确认需要时重新插入,但不得绕过 topic 零引用检查假设。
114+
115+
## Open Questions
116+
117+
- 活动达到何种数量和数据质量后启动独立 `event_meta` OpenSpec change。
118+
- 是否为新增社区 Tab 设置发帖账号年龄或积分门槛;本次沿用普通发帖治理。
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## Why
2+
3+
CNode 仍以 Chinese Node.js 社区为核心,但经过多年沉淀,成员讨论已经延伸到 AI、创意、职场、生活和社区活动。现有公开 Tab 主要只有 `share`、`ask` 和招聘内容,无法稳定承载这些主题,也缺少随 Tab 变化的范围说明和发帖约束提示。
4+
5+
## What Changes
6+
7+
- 保留 `share`、`ask`、`job`、`dev`、`good` 的既有 key、数据和语义,不迁移历史话题;新增 `tech`、`ai`、`ideas`、`career`、`life`、`event` 六个公开 Tab。
8+
- 将首页顺序固定为 `all / share / ask / tech / ai / ideas / career / life / event / job / dev / good`;普通用户不显示 `dev`,`all` 固定最左,`good` 固定最右。
9+
- 删除测试数据库快照中没有关联 topic 的 `test` Tab;迁移执行前再次断言不存在 `tab='test'` 的 topic,避免静默丢失内容。
10+
- 让 `job` 与 `event` 作为常规 Tab 参与首页和 `all` 列表;`job` 继续保留发布权限、`job_meta` 和招聘专区能力。
11+
- 首页侧边栏在 `all`、`good` 显示现有“社区合作” Card;其他 Tab 显示对应说明,`dev` 显示“开发使用” Card。
12+
- 发帖页右侧按“发布规范 Card、当前 Tab 说明 Card、`job_meta` 信息(仅 `job`)”的固定顺序呈现;活动本次只使用普通 topic 正文。
13+
- 更新创建、编辑、列表、管理筛选和 OpenAPI 合约,使新增 key 在 API 与 Web 中保持一致。
14+
- 更新本地 `.cnode-ops.md` 使用约定:任何 SSH 操作在连接前必须打印并确认所用 alias,不输出真实主机地址或凭据。
15+
- **BREAKING**:`test` 不再是有效或可配置 Tab;依赖该 key 的内部调用方需要停止使用。
16+
17+
## Capabilities
18+
19+
### New Capabilities
20+
21+
- `community-topic-tabs`: 定义社区 Tab 集合、顺序、显示名称、首页说明和发帖页 Card 组合规则。
22+
23+
### Modified Capabilities
24+
25+
- `admin-tab-management`: 调整注册表默认项、固定端点顺序约束并退役 `test`。
26+
- `api-contract`: 扩展合法 topic tab,并调整 `all`、`job`、`dev` 与已退役 `test` 的查询行为。
27+
- `home-sidebar-information`: 根据当前 Tab 在“社区合作”和板块说明 Card 之间切换。
28+
- `content-lifecycle`: 将非公开 Tab 规则从 `dev/test` 收敛为 `dev`,并安全处理已退役 `test`。
29+
- `jobs-structured-fields`: 保持招聘结构化信息,同时要求招聘发帖页先显示发布规范和招聘说明。
30+
31+
## Impact
32+
33+
- 范围内:`packages/shared` topic 合约,`packages/db` tabs 数据迁移与 seed,`apps/api` 话题/侧边栏查询,`apps/web` 首页、发布/编辑页、话题标签和后台 Tab/话题筛选,本地 `.cnode-ops.md` SSH 安全约定,以及对应测试和 OpenAPI 输出。
34+
- 范围外:活动结构化字段、`event_meta`、活动状态/专区、交易 Tab、技术标签系统、历史 `share/ask` 或空 Tab 话题重分类。
35+
- 受影响系统:PostgreSQL tabs 配置、Topic API、React Router SSR 首页和 compose archetype、后台 data-list 页面。
36+
- 高风险类别:错误删除历史 `test` 数据、公开可见性回归、旧客户端枚举兼容、招聘内容进入 `all` 后的信息密度、移动端 Tab 横向导航和发帖规范可见性。
37+
- Web 设计:沿用现有 Base UI Tabs、Card、Field 和 Select primitives;首页保持 feed archetype,发帖页保持 compose archetype。桌面侧边栏和移动端提交前提示必须共享文案来源、保持键盘可达且不依赖颜色传达规则。
38+
- 适用 Skills:`cnode-web-design` 约束首页与发帖布局;`cnode-docs` 约束业务规则和生成 API 资产更新。
39+
40+
## Non-goals
41+
42+
- 不把 Deno、Bun、Framework 或 Runtime 建成独立一级 Tab。
43+
- 不删除或迁移 `share`、`ask` 及其历史 topic。
44+
- 不在本次实现活动结构化数据;未来另行评估 `event_meta`。
45+
- 不上线交易能力、支付、担保或交易纠纷处理。
46+
- 不重做 Tabs、Card、Select 等设计系统 primitives。
47+
48+
## Documentation Impact
49+
50+
- 更新 `docs/biz/business-rules.md` 中有效 Tab、公开可见性、招聘和发帖规范;如社区定位需要长期说明,更新其现有业务文档 owner,不新增索引页。
51+
- `docs/arch/` 仅在 topic 分类数据模型产生新架构约束时更新;本次不改 `docs/deployment/`、根治理文件或 app README。
52+
- 通过生成命令更新 `apps/web/public/openapi.json`,不得手工编辑。
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: tabs 注册表
4+
5+
系统 SHALL 使用 `tabs` 表存储首页 tab 按钮的 key、标签、可见性、排序和访问范围。`tabs.key` SHALL 与代码支持的有效 `topics.tab` 字段值对应并保持 UNIQUE。tab 的存在性 SHALL 由代码/migration/bootstrap 控制,管理后台 SHALL NOT 支持运行时新增或删除 tab。
6+
7+
#### Scenario: tabs 表字段
8+
9+
- **WHEN** 系统 schema 包含 `tabs` 表
10+
- **THEN** 表包含字段:`id` (serial PK)、`key` (text, UNIQUE)、`label` (text)、`visible` (bool, default true)、`sort_order` (int, default 0)、`scope` (text, default `public`)、`create_at` (timestamp)、`update_at` (timestamp)
11+
- **AND** `scope` 支持 `public` 和 `admin`
12+
13+
#### Scenario: migration/bootstrap 默认值
14+
15+
- **WHEN** 系统通过 reviewed migration 或受控 bootstrap 初始化当前 tabs
16+
- **THEN** 注册 `share/ask/tech/ai/ideas/career/life/event/job/dev/good`
17+
- **AND** 除 `dev` 外均为 `scope='public'`,`dev` 为 `scope='admin'`
18+
- **AND** 不注册 `test`
19+
- **AND** 初始化保持幂等,重复执行不得重置已有 visible 运营值或 topic 数据
20+
21+
#### Scenario: 退役 test 前检查数据
22+
23+
- **WHEN** migration 准备删除 `tabs.key='test'`
24+
- **THEN** migration MUST 断言不存在精确或忽略大小写与首尾空格后等于 `test` 的 topic tab
25+
- **AND** 发现任一关联 topic 时 MUST 回滚并停止迁移
26+
27+
### Requirement: 首页 tab 按钮从 DB 加载
28+
29+
首页 `_index.tsx` SHALL 从 root loader data 读取 tabs 配置并按 `visible` 和 `scope` 过滤。页面 SHALL 固定组合 `all`、中间可配置项、允许时的 `dev` 和最右侧 `good`,而不是仅依赖 sort order 确定端点位置。
30+
31+
#### Scenario: 首页公开 Tab 按配置渲染
32+
33+
- **WHEN** 匿名或普通登录用户访问首页
34+
- **THEN** 页面只展示 `visible=true` 且 `scope='public'` 的注册项
35+
- **AND** 中间项按 sort order 升序排列
36+
- **AND** `all` 固定最左、可见 `good` 固定最右
37+
38+
#### Scenario: 管理员看到 dev
39+
40+
- **WHEN** 管理员访问首页且 `dev.visible=true`
41+
- **THEN** 页面在全部普通公开项之后展示 `dev`
42+
- **AND** 在 `dev` 之后只显示可见的 `good`
43+
44+
#### Scenario: 普通用户不可见 dev
45+
46+
- **WHEN** 非管理员访问首页
47+
- **THEN** 页面 MUST NOT 展示 `dev`
48+
- **AND** 页面 MUST NOT 展示已退役的 `test`
49+
50+
#### Scenario: Tab 隐藏后直接访问
51+
52+
- **WHEN** 管理员将某个 public tab 的 visible 设为 false
53+
- **THEN** 首页不展示该 tab 按钮
54+
- **AND** 直接访问对应 `?tab=` 仍按 API 合法 key、权限和公开规则处理
55+
56+
### Requirement: 管理后台 Tab 管理页面
57+
58+
`admin/tabs.tsx` SHALL 展示当前所有注册 tabs,支持编辑标签、切换可见性、调整允许调整的中间排序,并展示 scope。页面 SHALL 将 `dev` 标记为管理员可见,不得展示已退役的 `test`,也不得允许通过配置破坏 `all/dev/good` 的端点顺序。
59+
60+
#### Scenario: 访问 Tab 管理页
61+
62+
- **WHEN** 管理员访问 `/admin/tabs`
63+
- **THEN** 页面展示当前注册 tabs 的 key、标签、scope、可见性、排序和保存操作
64+
- **AND** `dev` 标记为管理员可见
65+
- **AND** 不显示 `test`
66+
67+
#### Scenario: 切换 Tab 可见性
68+
69+
- **WHEN** 管理员更新某个 public tab 的 visible
70+
- **THEN** 首页按钮可见性随配置变化
71+
- **AND** API 对合法 key 的处理不因按钮隐藏而改变
72+
73+
#### Scenario: dev 不可改为 public
74+
75+
- **WHEN** 管理员请求将 `dev.scope` 改为 `public`
76+
- **THEN** API MUST 拒绝或忽略该字段
77+
- **AND** `dev` 保持管理员可见范围
78+
79+
#### Scenario: 固定项不可被重排越界
80+
81+
- **WHEN** 管理员编辑 `dev` 或 `good` 的 sort order
82+
- **THEN** 首页仍将 `dev` 放在普通项之后
83+
- **AND** 首页仍将可见 `good` 放在最右侧

0 commit comments

Comments
 (0)