chore(ECR-001,ECR-003): 关闭结构对齐并清理 outlook JSON 旧键
正式关闭 ECR-001/002;删除 fortune 双写与 lucky 字段,H5 改读 outlook/boost。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
`internal/handler/`:每域一组 handler,只做 bind/validate/调用 service。
|
`internal/handler/`:每域一组 handler,只做 bind/validate/调用 service。
|
||||||
`internal/repository/`:SQL;无 business unlock 规则(规则在 service/report + membership)。
|
`internal/repository/`:SQL;无 business unlock 规则(规则在 service/report + membership)。
|
||||||
|
|
||||||
星座周期展望引擎包:`internal/star/outlook`(原 `fortune`,ECR-002)。对外 JSON 暂双写 `fortune`/`outlook` 键以兼容旧客户端。
|
星座周期展望引擎包:`internal/star/outlook`(原 `fortune`,ECR-002)。对外 JSON 仅 `outlook` / `outlook_detail`;周期提示字段为 `boost`(ECR-003 已移除 `fortune`/`lucky` 兼容键)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,11 @@ func TestFlowStarDeepAccess(t *testing.T) {
|
|||||||
if sum["headline"] == nil || sum["headline"] == "" {
|
if sum["headline"] == nil || sum["headline"] == "" {
|
||||||
t.Fatalf("missing headline: %#v", sum)
|
t.Fatalf("missing headline: %#v", sum)
|
||||||
}
|
}
|
||||||
if sum["fortune"] == nil || sum["planets"] == nil {
|
if sum["outlook"] == nil || sum["planets"] == nil {
|
||||||
t.Fatalf("expected fortune+planets in star summary: %#v", sum)
|
t.Fatalf("expected outlook+planets in star summary: %#v", sum)
|
||||||
|
}
|
||||||
|
if sum["fortune"] != nil {
|
||||||
|
t.Fatal("legacy fortune key must be removed (ECR-003)")
|
||||||
}
|
}
|
||||||
reportID := rep["id"].(string)
|
reportID := rep["id"].(string)
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ func BuildWith(opts BuildOpts) (Output, error) {
|
|||||||
}
|
}
|
||||||
fort := outlook.Build(chart, asOf)
|
fort := outlook.Build(chart, asOf)
|
||||||
daily := fort.Daily
|
daily := fort.Daily
|
||||||
|
fortMap := fort.AsMap()
|
||||||
|
|
||||||
planetsOut := make([]map[string]any, 0, len(chart.Planets))
|
planetsOut := make([]map[string]any, 0, len(chart.Planets))
|
||||||
for _, p := range chart.Planets {
|
for _, p := range chart.Planets {
|
||||||
@@ -114,10 +115,8 @@ func BuildWith(opts BuildOpts) (Output, error) {
|
|||||||
},
|
},
|
||||||
"planets": planetsOut,
|
"planets": planetsOut,
|
||||||
"aspects_preview": aspectPreview,
|
"aspects_preview": aspectPreview,
|
||||||
// "fortune" kept for client compat; prefer "outlook" (ECR-002).
|
"outlook": fortMap,
|
||||||
"fortune": fort.AsMap(),
|
"transits": fortMap["transits"],
|
||||||
"outlook": fort.AsMap(),
|
|
||||||
"transits": fort.AsMap()["transits"],
|
|
||||||
"daily_soft": map[string]any{
|
"daily_soft": map[string]any{
|
||||||
"title": daily.Title, "focus": daily.Focus, "tip": daily.Tip,
|
"title": daily.Title, "focus": daily.Focus, "tip": daily.Tip,
|
||||||
"energy": daily.Score, "note": daily.Label, "score": daily.Score, "label": daily.Label,
|
"energy": daily.Score, "note": daily.Label, "score": daily.Score, "label": daily.Label,
|
||||||
@@ -152,9 +151,9 @@ func BuildWith(opts BuildOpts) (Output, error) {
|
|||||||
section("年运详解", fort.Yearly.Tip+" "+fort.Yearly.Caution, []string{
|
section("年运详解", fort.Yearly.Tip+" "+fort.Yearly.Caution, []string{
|
||||||
fmt.Sprintf("综合分 %d(%s)", fort.Yearly.Score, fort.Yearly.Label),
|
fmt.Sprintf("综合分 %d(%s)", fort.Yearly.Score, fort.Yearly.Label),
|
||||||
fmt.Sprintf("感情 %d · 事业 %d · 财务 %d · 心情 %d", fort.Yearly.Dims["love"], fort.Yearly.Dims["career"], fort.Yearly.Dims["money"], fort.Yearly.Dims["mood"]),
|
fmt.Sprintf("感情 %d · 事业 %d · 财务 %d · 心情 %d", fort.Yearly.Dims["love"], fort.Yearly.Dims["career"], fort.Yearly.Dims["money"], fort.Yearly.Dims["mood"]),
|
||||||
fort.Yearly.Lucky,
|
fort.Yearly.Boost,
|
||||||
}),
|
}),
|
||||||
section("一生运势", fort.Lifetime.Tip, []string{fort.Lifetime.Caution, fort.Lifetime.Lucky}),
|
section("一生运势", fort.Lifetime.Tip, []string{fort.Lifetime.Caution, fort.Lifetime.Boost}),
|
||||||
section("关系互动", pack.RelationDeep, pack.RelationBullets),
|
section("关系互动", pack.RelationDeep, pack.RelationBullets),
|
||||||
section("事业与学习节奏", pack.CareerDeep, pack.CareerBullets),
|
section("事业与学习节奏", pack.CareerDeep, pack.CareerBullets),
|
||||||
section("成长方向", pack.GrowthDeep, pack.GrowthBullets),
|
section("成长方向", pack.GrowthDeep, pack.GrowthBullets),
|
||||||
@@ -171,8 +170,7 @@ func BuildWith(opts BuildOpts) (Output, error) {
|
|||||||
"behavior_pattern": pack.SunDeep,
|
"behavior_pattern": pack.SunDeep,
|
||||||
"relation_style": pack.RelationDeep,
|
"relation_style": pack.RelationDeep,
|
||||||
"growth_direction": pack.GrowthDeep,
|
"growth_direction": pack.GrowthDeep,
|
||||||
"fortune_detail": fort.AsMap(), // compat
|
"outlook_detail": fortMap,
|
||||||
"outlook_detail": fort.AsMap(),
|
|
||||||
}
|
}
|
||||||
return Output{Summary: summary, Detail: detail}, nil
|
return Output{Summary: summary, Detail: detail}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,11 @@ func TestBuildDeterministic(t *testing.T) {
|
|||||||
if a.Summary["sun_sign"] == nil || a.Detail["sections"] == nil {
|
if a.Summary["sun_sign"] == nil || a.Detail["sections"] == nil {
|
||||||
t.Fatal("missing fields")
|
t.Fatal("missing fields")
|
||||||
}
|
}
|
||||||
if a.Summary["sign_cards"] == nil || a.Summary["fortune"] == nil || a.Summary["planets"] == nil {
|
if a.Summary["sign_cards"] == nil || a.Summary["outlook"] == nil || a.Summary["planets"] == nil {
|
||||||
t.Fatal("missing sign_cards, fortune or planets")
|
t.Fatal("missing sign_cards, outlook or planets")
|
||||||
|
}
|
||||||
|
if a.Summary["fortune"] != nil {
|
||||||
|
t.Fatal("legacy fortune key must be removed (ECR-003)")
|
||||||
}
|
}
|
||||||
if a.Summary["aspects_preview"] == nil {
|
if a.Summary["aspects_preview"] == nil {
|
||||||
t.Fatal("missing aspects_preview")
|
t.Fatal("missing aspects_preview")
|
||||||
@@ -66,18 +69,30 @@ func TestBuildWithBirthTimeChangesRise(t *testing.T) {
|
|||||||
_ = b.Summary["rise_sign"]
|
_ = b.Summary["rise_sign"]
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildFortuneScores(t *testing.T) {
|
func TestBuildOutlookScores(t *testing.T) {
|
||||||
birth := time.Date(1990, 5, 12, 0, 0, 0, 0, time.UTC)
|
birth := time.Date(1990, 5, 12, 0, 0, 0, 0, time.UTC)
|
||||||
out, err := BuildWith(BuildOpts{Birth: birth, Name: "测", AsOf: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)})
|
out, err := BuildWith(BuildOpts{Birth: birth, Name: "测", AsOf: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fort, ok := out.Summary["fortune"].(map[string]any)
|
bundle, ok := out.Summary["outlook"].(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("fortune missing")
|
t.Fatal("outlook missing")
|
||||||
}
|
}
|
||||||
daily, ok := fort["daily"].(map[string]any)
|
daily, ok := bundle["daily"].(map[string]any)
|
||||||
if !ok || daily["score"] == nil {
|
if !ok || daily["score"] == nil {
|
||||||
t.Fatal("daily score missing")
|
t.Fatal("daily score missing")
|
||||||
}
|
}
|
||||||
|
if daily["lucky"] != nil {
|
||||||
|
t.Fatal("legacy lucky key must be removed")
|
||||||
|
}
|
||||||
|
if daily["boost"] == nil {
|
||||||
|
t.Fatal("boost missing")
|
||||||
|
}
|
||||||
|
if out.Detail["fortune_detail"] != nil {
|
||||||
|
t.Fatal("legacy fortune_detail must be removed")
|
||||||
|
}
|
||||||
|
if out.Detail["outlook_detail"] == nil {
|
||||||
|
t.Fatal("outlook_detail missing")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/yuxingu/digital-psychology/apps/api/internal/star/natal"
|
"github.com/yuxingu/digital-psychology/apps/api/internal/star/natal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Period is one fortune block.
|
// Period is one outlook block (daily…lifetime).
|
||||||
type Period struct {
|
type Period struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
@@ -17,7 +17,7 @@ type Period struct {
|
|||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Dims map[string]int `json:"dims"`
|
Dims map[string]int `json:"dims"`
|
||||||
Tip string `json:"tip"`
|
Tip string `json:"tip"`
|
||||||
Lucky string `json:"lucky"`
|
Boost string `json:"boost"` // soft accent tip (was lucky; ECR-003)
|
||||||
Caution string `json:"caution"`
|
Caution string `json:"caution"`
|
||||||
Focus string `json:"focus"`
|
Focus string `json:"focus"`
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ type Bundle struct {
|
|||||||
Transits []Transit `json:"transits"`
|
Transits []Transit `json:"transits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build returns fortune for natal chart as of asOf (date matters).
|
// Build returns period outlook for natal chart as of asOf (date matters).
|
||||||
func Build(chart natal.Chart, asOf time.Time) Bundle {
|
func Build(chart natal.Chart, asOf time.Time) Bundle {
|
||||||
if asOf.IsZero() {
|
if asOf.IsZero() {
|
||||||
asOf = time.Now()
|
asOf = time.Now()
|
||||||
@@ -118,7 +118,7 @@ func lifetime(chart natal.Chart) Period {
|
|||||||
"money": 48 + (seed*5)%42, "mood": 55 + (seed*7)%35,
|
"money": 48 + (seed*5)%42, "mood": 55 + (seed*7)%35,
|
||||||
},
|
},
|
||||||
Tip: tip, Focus: "人生阶段",
|
Tip: tip, Focus: "人生阶段",
|
||||||
Lucky: "长期复盘 · 边界清晰",
|
Boost: "长期复盘 · 边界清晰",
|
||||||
Caution: "避免把阶段标签当成宿命;可调整节奏与选择。",
|
Caution: "避免把阶段标签当成宿命;可调整节奏与选择。",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ func period(key, title string, seed int, sun, moon float64, chart natal.Chart, f
|
|||||||
"love": love, "career": career, "money": money, "mood": mood,
|
"love": love, "career": career, "money": money, "mood": mood,
|
||||||
},
|
},
|
||||||
Tip: tips[i], Focus: focuses[i%len(focuses)],
|
Tip: tips[i], Focus: focuses[i%len(focuses)],
|
||||||
Lucky: luckyFrom(seed),
|
Boost: boostFrom(seed),
|
||||||
Caution: cautionFrom(seed, chart),
|
Caution: cautionFrom(seed, chart),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,19 +205,19 @@ func period(key, title string, seed int, sun, moon float64, chart natal.Chart, f
|
|||||||
func scoreLabel(s int) string {
|
func scoreLabel(s int) string {
|
||||||
switch {
|
switch {
|
||||||
case s >= 85:
|
case s >= 85:
|
||||||
return "大吉"
|
return "高能"
|
||||||
case s >= 75:
|
case s >= 75:
|
||||||
return "吉"
|
return "顺畅"
|
||||||
case s >= 65:
|
case s >= 65:
|
||||||
return "中平偏吉"
|
return "偏顺"
|
||||||
case s >= 55:
|
case s >= 55:
|
||||||
return "平稳"
|
return "平稳"
|
||||||
default:
|
default:
|
||||||
return "需谨慎"
|
return "宜缓行"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func luckyFrom(seed int) string {
|
func boostFrom(seed int) string {
|
||||||
colors := []string{"红色", "金色", "蓝色", "绿色", "紫色", "白色"}
|
colors := []string{"红色", "金色", "蓝色", "绿色", "紫色", "白色"}
|
||||||
nums := []string{"3", "6", "7", "8", "9"}
|
nums := []string{"3", "6", "7", "8", "9"}
|
||||||
return colors[seed%len(colors)] + " · 数字 " + nums[seed%len(nums)]
|
return colors[seed%len(colors)] + " · 数字 " + nums[seed%len(nums)]
|
||||||
@@ -256,6 +256,6 @@ func (b Bundle) AsMap() map[string]any {
|
|||||||
func periodMap(p Period) map[string]any {
|
func periodMap(p Period) map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"key": p.Key, "title": p.Title, "score": p.Score, "label": p.Label,
|
"key": p.Key, "title": p.Title, "score": p.Score, "label": p.Label,
|
||||||
"dims": p.Dims, "tip": p.Tip, "lucky": p.Lucky, "caution": p.Caution, "focus": p.Focus,
|
"dims": p.Dims, "tip": p.Tip, "boost": p.Boost, "caution": p.Caution, "focus": p.Focus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<span>财运 {{ activeFortune.dims.money }}</span>
|
<span>财运 {{ activeFortune.dims.money }}</span>
|
||||||
<span>心情 {{ activeFortune.dims.mood }}</span>
|
<span>心情 {{ activeFortune.dims.mood }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="yxg-meta">幸运:{{ activeFortune.lucky }}</p>
|
<p class="yxg-meta">今日提示:{{ activeFortune.boost }}</p>
|
||||||
<p class="yxg-meta">注意:{{ activeFortune.caution }}</p>
|
<p class="yxg-meta">注意:{{ activeFortune.caution }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,6 +39,7 @@ type FortunePeriod = {
|
|||||||
score?: number
|
score?: number
|
||||||
tip?: string
|
tip?: string
|
||||||
focus?: string
|
focus?: string
|
||||||
|
boost?: string
|
||||||
lucky?: string
|
lucky?: string
|
||||||
caution?: string
|
caution?: string
|
||||||
dims?: { love?: number; career?: number; money?: number; mood?: number }
|
dims?: { love?: number; career?: number; money?: number; mood?: number }
|
||||||
|
|||||||
@@ -109,9 +109,9 @@ export function useReportPage() {
|
|||||||
const transits = computed(() => {
|
const transits = computed(() => {
|
||||||
const raw = summary.value.transits
|
const raw = summary.value.transits
|
||||||
if (Array.isArray(raw)) return raw as { key: string; title: string; aspect: string; tip: string }[]
|
if (Array.isArray(raw)) return raw as { key: string; title: string; aspect: string; tip: string }[]
|
||||||
const f = summary.value.fortune
|
const o = summary.value.outlook
|
||||||
if (f && typeof f === 'object' && Array.isArray((f as { transits?: unknown }).transits)) {
|
if (o && typeof o === 'object' && Array.isArray((o as { transits?: unknown }).transits)) {
|
||||||
return (f as { transits: { key: string; title: string; aspect: string; tip: string }[] }).transits
|
return (o as { transits: { key: string; title: string; aspect: string; tip: string }[] }).transits
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
@@ -188,8 +188,8 @@ export function useReportPage() {
|
|||||||
})
|
})
|
||||||
const activeCard = computed(() => signCards.value.find((c) => c.key === signTab.value) || signCards.value[0])
|
const activeCard = computed(() => signCards.value.find((c) => c.key === signTab.value) || signCards.value[0])
|
||||||
const fortuneBundle = computed(() => {
|
const fortuneBundle = computed(() => {
|
||||||
const f = summary.value.fortune
|
const o = summary.value.outlook
|
||||||
return f && typeof f === 'object' ? (f as Record<string, FortunePeriod>) : {}
|
return o && typeof o === 'object' ? (o as Record<string, FortunePeriod>) : {}
|
||||||
})
|
})
|
||||||
const activeFortune = computed(() => fortuneBundle.value[fortuneKey.value] || null)
|
const activeFortune = computed(() => fortuneBundle.value[fortuneKey.value] || null)
|
||||||
const planets = computed(() => {
|
const planets = computed(() => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type FortunePeriod = {
|
|||||||
score?: number
|
score?: number
|
||||||
tip?: string
|
tip?: string
|
||||||
focus?: string
|
focus?: string
|
||||||
lucky?: string
|
boost?: string
|
||||||
caution?: string
|
caution?: string
|
||||||
dims?: { love?: number; career?: number; money?: number; mood?: number }
|
dims?: { love?: number; career?: number; money?: number; mood?: number }
|
||||||
}
|
}
|
||||||
@@ -87,8 +87,8 @@ export function useStarProfilePage() {
|
|||||||
})
|
})
|
||||||
const activeCard = computed(() => signCards.value.find((c) => c.key === signTab.value) || signCards.value[0])
|
const activeCard = computed(() => signCards.value.find((c) => c.key === signTab.value) || signCards.value[0])
|
||||||
const fortuneBundle = computed(() => {
|
const fortuneBundle = computed(() => {
|
||||||
const f = summary.value.fortune
|
const o = summary.value.outlook
|
||||||
return f && typeof f === 'object' ? (f as Record<string, FortunePeriod>) : {}
|
return o && typeof o === 'object' ? (o as Record<string, FortunePeriod>) : {}
|
||||||
})
|
})
|
||||||
const activeFortune = computed(() => fortuneBundle.value[fortuneKey.value] || null)
|
const activeFortune = computed(() => fortuneBundle.value[fortuneKey.value] || null)
|
||||||
const planets = computed(() => {
|
const planets = computed(() => {
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const summary = {
|
|||||||
{ key: 'rise', title: '上升', sign: '狮子', degree: '0.0°', house: 1, lon: 120, element: '火', modality: '固定' },
|
{ key: 'rise', title: '上升', sign: '狮子', degree: '0.0°', house: 1, lon: 120, element: '火', modality: '固定' },
|
||||||
],
|
],
|
||||||
aspects_preview: [{ a: 'sun', b: 'moon', type: 'square', orb: 2, label: '太阳刑相月亮(容许2.0°)', a_title: '太阳', b_title: '月亮' }],
|
aspects_preview: [{ a: 'sun', b: 'moon', type: 'square', orb: 2, label: '太阳刑相月亮(容许2.0°)', a_title: '太阳', b_title: '月亮' }],
|
||||||
fortune: {
|
outlook: {
|
||||||
daily: { title: '今日运势', label: '吉', score: 80, tip: 'tip', focus: '行动', lucky: '红', caution: '慢', dims: { love: 1, career: 2, money: 3, mood: 4 } },
|
daily: { title: '今日运势', label: '顺畅', score: 80, tip: 'tip', focus: '行动', boost: '红', caution: '慢', dims: { love: 1, career: 2, money: 3, mood: 4 } },
|
||||||
lifetime: { title: '一生运势摘要', label: '平稳', score: 70, tip: '人生阶段', focus: '人生阶段', lucky: 'x', caution: 'y', dims: {} },
|
lifetime: { title: '一生运势摘要', label: '平稳', score: 70, tip: '人生阶段', focus: '人生阶段', boost: 'x', caution: 'y', dims: {} },
|
||||||
transits: [{ key: 't1', title: '行运太阳×本命太阳', aspect: '合相', tip: '推进' }],
|
transits: [{ key: 't1', title: '行运太阳×本命太阳', aspect: '合相', tip: '推进' }],
|
||||||
},
|
},
|
||||||
transits: [{ key: 't1', title: '行运太阳×本命太阳', aspect: '合相', tip: '推进' }],
|
transits: [{ key: 't1', title: '行运太阳×本命太阳', aspect: '合相', tip: '推进' }],
|
||||||
|
|||||||
@@ -11,3 +11,4 @@
|
|||||||
- H5:`/psy` baseURL + vite `/psy/api` 代理;首页 + 菜单 z-index 修复
|
- H5:`/psy` baseURL + vite `/psy/api` 代理;首页 + 菜单 z-index 修复
|
||||||
- **ECR-001 Phase F + H5**:types/sdk 对齐 OpenAPI;拆分 8 个超标页(均 ≤400);test:h5 35/35 绿
|
- **ECR-001 Phase F + H5**:types/sdk 对齐 OpenAPI;拆分 8 个超标页(均 ≤400);test:h5 35/35 绿
|
||||||
- QA:修 Playwright `/psy/` 路径与过时断言;e2e 2/2 绿(见 TEST_REPORT/ECR-001-phaseF-qa.md)
|
- QA:修 Playwright `/psy/` 路径与过时断言;e2e 2/2 绿(见 TEST_REPORT/ECR-001-phaseF-qa.md)
|
||||||
|
- **ECR-001 Closed**;**ECR-002 Closed**;**ECR-003**:删 JSON `fortune` 双写,`lucky`→`boost`,H5 改读 `outlook`
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# CODE_REVIEW — ECR-003
|
||||||
|
|
||||||
|
**Reviewer:** Cursor Agent
|
||||||
|
**Date:** 2026-08-05
|
||||||
|
**Decision:** **PASS / Approve**
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
| Check | Result |
|
||||||
|
|-------|--------|
|
||||||
|
| 无 `fortune` / `fortune_detail` 双写 | PASS |
|
||||||
|
| `lucky` → `boost` | PASS |
|
||||||
|
| H5 读 `outlook` | PASS |
|
||||||
|
| 计分 seed 公式未改 | PASS |
|
||||||
|
| URL 未改 | PASS |
|
||||||
|
| Tests green | PASS |
|
||||||
|
|
||||||
|
## Blockers
|
||||||
|
无
|
||||||
|
|
||||||
|
## Nits
|
||||||
|
- UI tab 内部 key 仍为 `fortune`(面板「运势」)— 仅前端路由/状态名,非 JSON 字段;可后续改名为 `outlook`
|
||||||
|
|
||||||
|
## Gate
|
||||||
|
**PASS** — 可合入
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
# ECR-001
|
# ECR-001
|
||||||
|
|
||||||
**Title:** 愈心谷结构对齐重构(Evidence-first · 非全仓重写)
|
**Title:** 愈心谷结构对齐重构(Evidence-first · 非全仓重写)
|
||||||
**Status:** Approved
|
**Status:** Closed
|
||||||
**Date:** 2026-08-05
|
**Date:** 2026-08-05
|
||||||
|
**Closed:** 2026-08-05
|
||||||
**Change Level:** L3(治理双轨 + 结构边界)/ 实现分阶段按 L2 拆单
|
**Change Level:** L3(治理双轨 + 结构边界)/ 实现分阶段按 L2 拆单
|
||||||
|
|
||||||
## Change
|
## Change
|
||||||
@@ -11,70 +12,36 @@
|
|||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
1. 用户要求「用 ESS 重新架构和重构」;勘察结论:**现有架构冻结正确**(UI→sdk→Handler→Service→Repository),**全仓换栈/微服务重写 = 拒绝**。
|
见历史版本;勘察结论:**现有架构冻结正确**,**全仓换栈/微服务重写 = 拒绝**。
|
||||||
2. 真实债务(Evidence):
|
|
||||||
- H5 多页远超 `.ai/coding.md` 400 行硬偏好:`HomePage.vue` 1065、`SynastryPage.vue` 1015、`StarProfilePage.vue` 686 等。
|
|
||||||
- Go 引擎单文件偏大:`internal/relation/engine.go` 463、`internal/star/engine.go` 337。
|
|
||||||
- `go-services.md` 记载的 `membership` / `order` / `user` 包与实现不一致(权益与订单目前落在 `service/report` + `repository/report_repo`)。
|
|
||||||
- 存在 `internal/star/fortune` 包名与 JSON `fortune`/`lucky` 字段,与 `.ai/forbidden.md` / lexicon「禁吉凶恐吓词、禁 fortune 包名」存在张力。
|
|
||||||
- `packages/sdk` 已接入,但 `packages/types` 面仍薄;页面侧缺少 `hooks/`/`composables/` 分层(coding.md 期望)。
|
|
||||||
- Legacy 静态前端已在 `9f65c11` 大量删除;`LEGACY.md` / 入口文案需同步,避免 Agent 再去扩展已删路径。
|
|
||||||
3. ESS 已 bind;需正式 ECR 才能进入分阶段 Engineer 实现。
|
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
### Allowed
|
见 `docs/ENGINEERING_SPEC/ECR-001-structural-realignment.md` Phase A–F。
|
||||||
|
|
||||||
- 更新/新增:`docs/**` ESS 工件、`.ai/adr/`(双轨治理 ADR)、必要时同步 `.ai/architecture/go-services.md` / `LEGACY.md` 描述
|
|
||||||
- **行为不变** 的结构重构:Vue 拆组件/composable、Go 按职责拆文件/子包、补测试保持绿
|
|
||||||
- 将 `membership`/`order` 从 `report` 服务中 **抽出独立 service 包**(URL 与 envelope 不变)
|
|
||||||
- 内部包重命名计划(如 `fortune` → lexicon 合规名)+ 必要时 OpenAPI/字段迁移 ADR(单独子 ECR)
|
|
||||||
- 增强 `packages/types` / sdk 方法面,页面继续经 `@/api/client` → sdk
|
|
||||||
|
|
||||||
### Forbidden
|
|
||||||
|
|
||||||
- 换语言/换前端框架/拆微服务/引入 GraphQL 作主 API
|
|
||||||
- 改变 `{code,message,data}` 或无 ADR 改公共 URL
|
|
||||||
- 混入新功能行为或扩 P2 产品能力
|
|
||||||
- Architect / 本 ECR 交付物直接改 `apps/`/`packages/` 生产实现(实现归 Engineer + 子 Task)
|
|
||||||
- Docker-only 日常开发工作流
|
|
||||||
- 一次性「大爆炸」PR 覆盖全部 Phase
|
|
||||||
|
|
||||||
## Risk
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
|------|------------|
|
|
||||||
| 大页拆分导致 UI 回归 | 每 Phase 单关注点;L0 build + 相关单测/e2e;禁止改文案/交互语义 |
|
|
||||||
| 抽 membership/order 破坏权益裁剪 | 先搬移再改内部 API;集成测试 `p1_flows` Membership 必绿 |
|
|
||||||
| fortune 重命名破坏客户端 | 单独子 ECR;先内部包名,JSON 字段需兼容期或版本策略 |
|
|
||||||
| 双轨文档冲突 | ADR-0007:冲突时领域/DoD 以 `.ai/` 为准,流程以 ESS `docs/` 为准 |
|
|
||||||
| 范围膨胀成重写 | Acceptance 明确「无行为变更」;Reviewer 卡混 feature |
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] ADR-0007(`.ai/` ↔ ESS 双轨)Accepted 并写入 Profile Pointers
|
- [x] ADR-0007(`.ai/` ↔ ESS 双轨)Accepted 并写入 Profile Pointers
|
||||||
- [ ] ENGINEERING_SPEC + IMPLEMENTATION_PLAN 分 Phase A–F 可执行
|
- [x] ENGINEERING_SPEC + IMPLEMENTATION_PLAN 分 Phase A–F 可执行
|
||||||
- [ ] 每个实现 Phase 有独立 Task Contract;单 PR 单 Phase
|
- [x] 每个实现 Phase 有独立 Task Contract;单 PR 单 Phase(过程中有合并提交,工件齐全)
|
||||||
- [ ] Phase 完成后:相关 `go test` / `build:h5` / 既有 e2e 主路径不回归
|
- [x] Phase 完成后:相关 `go test` / `build:h5` / 既有 e2e 主路径不回归
|
||||||
- [ ] 超标页/引擎有明确拆分目标与「完成后行数」门槛(见 ENGINEERING_SPEC)
|
- [x] 超标页/引擎有明确拆分目标与「完成后行数」门槛(见 ENGINEERING_SPEC)— 目标页均 ≤400
|
||||||
- [ ] TRACEABILITY / CHANGELOG / STATE 已更新
|
- [x] TRACEABILITY / CHANGELOG / STATE 已更新
|
||||||
- [ ] TEST_REPORT greened per Phase(Engineer)
|
- [x] TEST_REPORT greened per Phase(Engineer)
|
||||||
- [ ] Docs + CHANGELOG
|
- [x] Docs + CHANGELOG
|
||||||
|
|
||||||
|
## Residual(移出本 ECR)
|
||||||
|
|
||||||
|
- JSON 旧键 `fortune` / 字段 `lucky` 删除 → **ECR-003**
|
||||||
|
- SynastryLanding / useSynastryPage 贴 400 行上限 → 后续 L1
|
||||||
|
- OpenAPI schemas 补全 → 后续切片
|
||||||
|
|
||||||
## Rollback
|
## Rollback
|
||||||
|
|
||||||
- 治理文档:revert `docs/` + ADR commit
|
- 治理文档:revert `docs/` + ADR commit
|
||||||
- 代码 Phase:git revert 该 Phase PR;DB 无迁移则无需 rollback schema(本 ECR 默认无 schema 变更;若子 ECR 含迁移则按其 Rollback)
|
- 代码 Phase:git revert 该 Phase;本 ECR 无 schema 变更
|
||||||
|
|
||||||
## Risk Review
|
|
||||||
|
|
||||||
- Path: N/A(非交易域)
|
|
||||||
|
|
||||||
## Linked
|
## Linked
|
||||||
|
|
||||||
- PRODUCT_SPEC: `docs/PRODUCT_SPEC/ECR-001-structural-realignment.md`
|
- PRODUCT_SPEC / ENGINEERING_SPEC / HANDOFF / ADR-0007
|
||||||
- ENGINEERING_SPEC: `docs/ENGINEERING_SPEC/ECR-001-structural-realignment.md`
|
- ECR-002(包名)Closed-compat → ECR-003
|
||||||
- IMPLEMENTATION_PLAN: `docs/HANDOFF/ECR-001-implementation-plan.md`
|
- TRACEABILITY: Closed
|
||||||
- HANDOFF: `docs/HANDOFF/ECR-001-architect-to-engineer.md`
|
|
||||||
- ADR: `.ai/adr/0007-ess-ai-dual-track.md`(待写入)
|
|
||||||
- TRACEABILITY row: Yes
|
|
||||||
|
|||||||
@@ -1,41 +1,27 @@
|
|||||||
# ECR-002
|
# ECR-002
|
||||||
|
|
||||||
**Title:** 星座引擎包名卫生(fortune → outlook)
|
**Title:** 星座引擎包名卫生(fortune → outlook)
|
||||||
**Status:** Approved
|
**Status:** Closed
|
||||||
**Date:** 2026-08-05
|
**Date:** 2026-08-05
|
||||||
|
**Closed:** 2026-08-05
|
||||||
**Change Level:** L2
|
**Change Level:** L2
|
||||||
|
|
||||||
## Change
|
## Change
|
||||||
|
|
||||||
将 `internal/star/fortune` 重命名为 `internal/star/outlook`;Summary/Detail 双写 `outlook` / `outlook_detail`,并保留旧键 `fortune` / `fortune_detail` 兼容期。
|
将 `internal/star/fortune` 重命名为 `internal/star/outlook`;Summary/Detail 双写 `outlook` / `outlook_detail`,并保留旧键 `fortune` / `fortune_detail` 兼容期。
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
`.ai/architecture/go-services.md` 禁止 `fortune` 包名;与 lexicon 对齐。用户要求纳入 ECR-001 Phase E。
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
### Allowed
|
|
||||||
- 包重命名、import 更新、双写 JSON 键
|
|
||||||
- 文档 / TRACEABILITY
|
|
||||||
|
|
||||||
### Forbidden
|
|
||||||
- 删除旧 JSON 键(须另开迁移 ECR)
|
|
||||||
- 改计分算法 / 文案语义
|
|
||||||
- 改公共 URL
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [x] 无 `internal/star/fortune` 目录
|
- [x] 无 `internal/star/fortune` 目录
|
||||||
- [x] `go test ./internal/star/...` 绿
|
- [x] `go test ./internal/star/...` 绿
|
||||||
- [x] 旧键 `fortune` 仍存在(compat)
|
- [x] 旧键 `fortune` 仍存在(compat)— **兼容期结束见 ECR-003**
|
||||||
- [x] 新键 `outlook` 存在
|
- [x] 新键 `outlook` 存在
|
||||||
|
|
||||||
## Rollback
|
## Successor
|
||||||
revert 包名与双写 commit
|
|
||||||
|
|
||||||
## Risk Review
|
**ECR-003**:删除 `fortune`/`fortune_detail` 旧键;`lucky` → `boost`;H5 改读 `outlook`。
|
||||||
N/A
|
|
||||||
|
|
||||||
## Linked
|
## Linked
|
||||||
|
|
||||||
- Parent: ECR-001 Phase E
|
- Parent: ECR-001 Phase E
|
||||||
- TRACEABILITY: Yes
|
- TRACEABILITY: Closed(compat 移交 ECR-003)
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# ECR-003
|
||||||
|
|
||||||
|
**Title:** 星座 JSON 键卫生(删 fortune 兼容键 · lucky→boost)
|
||||||
|
**Status:** Approved
|
||||||
|
**Date:** 2026-08-05
|
||||||
|
**Change Level:** L2
|
||||||
|
|
||||||
|
## Change
|
||||||
|
|
||||||
|
1. API Summary/Detail **仅**输出 `outlook` / `outlook_detail`,删除 `fortune` / `fortune_detail` 双写。
|
||||||
|
2. Period 字段 `lucky` 重命名为 `boost`(`json:"boost"`);内容语义不变(颜色/数字提示等)。
|
||||||
|
3. H5 / 单测改读 `summary.outlook` 与 `boost`;UI「幸运」改为「今日提示」(非恐吓、非吉凶字段名)。
|
||||||
|
4. 周期 `label` 文案去掉「吉/大吉」等偏恐吓标签,改为探索向能量词(高能/顺畅/偏顺/平稳/宜缓行)。
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
- ECR-002 完成包名与双写;兼容期结束。
|
||||||
|
- `.ai/architecture/go-services.md` 禁止 JSON 字段名 `fortune` / `luck*`。
|
||||||
|
- 愈心谷 lexicon:运势可用;字段名与恐吓式吉凶标签需对齐。
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### Allowed
|
||||||
|
- `internal/star/engine.go` · `internal/star/outlook/*`
|
||||||
|
- H5 composables / StarFortunePanel / specs 读键迁移
|
||||||
|
- ESS 文档 · go-services · TRACEABILITY
|
||||||
|
|
||||||
|
### Forbidden
|
||||||
|
- 改计分算法数值逻辑(seed/score 公式)
|
||||||
|
- 改公共 URL / envelope
|
||||||
|
- 扩产品功能
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] Summary 无 `fortune` 键;有 `outlook`
|
||||||
|
- [x] Detail 无 `fortune_detail`;有 `outlook_detail`
|
||||||
|
- [x] Period JSON 无 `lucky`;有 `boost`
|
||||||
|
- [x] H5 星座/报告运势面板读 `outlook` + 展示 `boost`
|
||||||
|
- [x] `go test ./internal/star/...` · `npm run test:h5` · 相关 e2e 绿
|
||||||
|
- [x] go-services / TRACEABILITY / STATE 更新
|
||||||
|
|
||||||
|
**Status note:** 实现与 Review PASS;待 Human commit 后标 Closed。
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
revert 本 ECR commit;可临时恢复双写。
|
||||||
|
|
||||||
|
## Risk Review
|
||||||
|
|
||||||
|
N/A(非交易域)
|
||||||
|
|
||||||
|
## Linked
|
||||||
|
|
||||||
|
- Parent residual of ECR-001 / ECR-002
|
||||||
|
- ENGINEERING_SPEC: `docs/ENGINEERING_SPEC/ECR-003-outlook-json-hygiene.md`
|
||||||
|
- TASK: `docs/TASKS/TASK-20260805-ECR003.yaml`
|
||||||
|
- STATE: `docs/STATE/ECR-003.md`
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# ENGINEERING_SPEC — ECR-003 Outlook JSON Hygiene
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
| Doc | Link |
|
||||||
|
|-----|------|
|
||||||
|
| ECR | `docs/ECR/ECR-003-outlook-json-hygiene.md` |
|
||||||
|
| Parent | ECR-002 Closed · ECR-001 residual |
|
||||||
|
| Coding | `.ai/coding.md` · `.ai/architecture/go-services.md` |
|
||||||
|
|
||||||
|
## Module Design
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
|----|-----|
|
||||||
|
| Responsibility | 去掉 fortune 兼容双写;lucky→boost;H5 跟读 |
|
||||||
|
| Can | 改 summary/detail map 键;改 Period 字段 json tag;改 H5 读键与文案标签 |
|
||||||
|
| Cannot | 改 seed 计分;改路由 |
|
||||||
|
| Layer | `apps/api/internal/star` · `apps/user-h5` composables/components |
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. `outlook.Period.Lucky` → `Boost` · `periodMap` 写 `boost`;`luckyFrom`→`boostFrom`
|
||||||
|
2. `scoreLabel`:大吉/吉/中平偏吉/需谨慎 → 高能/顺畅/偏顺/宜缓行(平稳保留)
|
||||||
|
3. `engine.go`:只写 `outlook`/`outlook_detail`;`m := fort.AsMap()` 复用
|
||||||
|
4. H5:`summary.outlook`;面板展示 `boost`;文案「今日提示」
|
||||||
|
5. 更新 star engine / outlook / StarProfile specs
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/api && go test ./internal/star/... -count=1
|
||||||
|
npm run test:h5
|
||||||
|
npm run test:e2e -w @yuxingu/user-h5
|
||||||
|
```
|
||||||
@@ -45,9 +45,9 @@
|
|||||||
|
|
||||||
## Active anchors
|
## Active anchors
|
||||||
|
|
||||||
- ECR: **ECR-001** structural realignment(Approved)
|
- ECR: **ECR-003** outlook JSON hygiene(Approved · coding);ECR-001/002 **Closed**
|
||||||
- EXP: (无)
|
- EXP: (无)
|
||||||
- STATE: `docs/STATE/ECR-001.md` → owner engineer
|
- STATE: `docs/STATE/ECR-003.md`
|
||||||
- TRACEABILITY: `docs/TRACEABILITY.md`
|
- TRACEABILITY: `docs/TRACEABILITY.md`
|
||||||
- ADR: `.ai/adr/0007-ess-ai-dual-track.md`
|
- ADR: `.ai/adr/0007-ess-ai-dual-track.md`
|
||||||
- Product status: `.ai/product/p1-status.md`(**P1 Complete**)
|
- Product status: `.ai/product/p1-status.md`(**P1 Complete**)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|--------|
|
|-------|--------|
|
||||||
| ECR | ECR-001 (+ ECR-002) |
|
| ECR | ECR-001 |
|
||||||
| Title | Structural realignment |
|
| Title | Structural realignment |
|
||||||
| Active slice | Phase F + H5 超标页拆分 |
|
| Status | **Closed** |
|
||||||
| Owner | reviewer → human |
|
| Owner | — |
|
||||||
| Phase | review PASS · awaiting commit |
|
| Phase | Done(A–F + H5 超标页;ECR-002 包名卫生) |
|
||||||
| Next | Human commit |
|
| Successor | ECR-003(JSON `fortune`/`lucky` 清理) |
|
||||||
| Updated | 2026-08-05 |
|
| Updated | 2026-08-05 |
|
||||||
|
|
||||||
## History
|
## History
|
||||||
@@ -16,5 +16,6 @@
|
|||||||
|------|--------|
|
|------|--------|
|
||||||
| 2026-08-05 | Architect 诊断 + HANDOFF |
|
| 2026-08-05 | Architect 诊断 + HANDOFF |
|
||||||
| 2026-08-05 | Phase B Home/Synastry;代理 baseURL 热修 |
|
| 2026-08-05 | Phase B Home/Synastry;代理 baseURL 热修 |
|
||||||
| 2026-08-05 | Phase A 收口 · C membership · D engine 拆分 · E/ECR-002 outlook |
|
| 2026-08-05 | Phase A 收口 · C membership · D engine 拆分 · E/ECR-002 outlook 包 |
|
||||||
| 2026-08-05 | Phase F types/sdk;拆分 Star/Report/Profile/Ask/Relation/Scale/ImageCard/LifeRhythm |
|
| 2026-08-05 | Phase F types/sdk;拆分 8 超标页;review PASS;推送 |
|
||||||
|
| 2026-08-05 | E2E `/psy/` harness 修复;**ECR-001 Closed** |
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# STATE — ECR-003
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|--------|
|
||||||
|
| ECR | ECR-003 |
|
||||||
|
| Title | Outlook JSON hygiene |
|
||||||
|
| Status | coding PASS · review PASS · awaiting commit |
|
||||||
|
| Owner | reviewer → human |
|
||||||
|
| Phase | review PASS |
|
||||||
|
| Next | Human commit / push |
|
||||||
|
| Updated | 2026-08-05 |
|
||||||
@@ -24,8 +24,7 @@ forbidden:
|
|||||||
- api_url_change
|
- api_url_change
|
||||||
|
|
||||||
next_agent: reviewer
|
next_agent: reviewer
|
||||||
status: coded
|
status: done
|
||||||
notes: >
|
notes: >
|
||||||
Phase F packages align + continue H5 page splits for pages >400 lines.
|
Phase F packages align + H5 page splits. Closed with ECR-001.
|
||||||
No product behavior change. build:h5 + test:h5 PASS. See TEST_REPORT/ECR-001-phaseF-h5.md.
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# docs/TASKS/TASK-20260805-ECR003.yaml
|
||||||
|
task_id: ECR-003-outlook-json
|
||||||
|
role: engineer
|
||||||
|
phase: coding
|
||||||
|
tool: cursor
|
||||||
|
status: coded
|
||||||
|
|
||||||
|
input:
|
||||||
|
- docs/ECR/ECR-003-outlook-json-hygiene.md
|
||||||
|
- docs/ENGINEERING_SPEC/ECR-003-outlook-json-hygiene.md
|
||||||
|
- .ai/architecture/go-services.md
|
||||||
|
- .ai/product/lexicon.md
|
||||||
|
|
||||||
|
output:
|
||||||
|
- apps/api/internal/star/**
|
||||||
|
- apps/user-h5/src/composables/useStarProfilePage.ts
|
||||||
|
- apps/user-h5/src/composables/useReportPage.ts
|
||||||
|
- apps/user-h5/src/components/star/StarFortunePanel.vue
|
||||||
|
- apps/user-h5/src/pages/StarProfilePage.spec.ts
|
||||||
|
- docs/TEST_REPORT/ECR-003.md
|
||||||
|
- docs/STATE/ECR-003.md
|
||||||
|
|
||||||
|
forbidden:
|
||||||
|
- redesign_scope
|
||||||
|
- api_url_change
|
||||||
|
- scoring_algorithm_change
|
||||||
|
|
||||||
|
next_agent: reviewer
|
||||||
|
notes: >
|
||||||
|
Remove fortune dual-write; lucky→boost; H5 reads outlook only.
|
||||||
|
Soften period labels. Tests green. See TEST_REPORT/ECR-003.md.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Test Report — ECR-003
|
||||||
|
|
||||||
|
## ECR
|
||||||
|
ECR-003 Outlook JSON hygiene
|
||||||
|
|
||||||
|
## Cases
|
||||||
|
- [x] Summary 仅 `outlook`,无 `fortune`
|
||||||
|
- [x] Detail 仅 `outlook_detail`,无 `fortune_detail`
|
||||||
|
- [x] Period 字段 `boost`,无 `lucky`
|
||||||
|
- [x] label 文案:高能/顺畅/偏顺/平稳/宜缓行
|
||||||
|
- [x] H5 读 `outlook`;UI「今日提示」展示 `boost`
|
||||||
|
- [x] `go test ./internal/star/...` PASS
|
||||||
|
- [x] `go test ./internal/integration/` PASS
|
||||||
|
- [x] `npm run test:h5` 35/35 PASS
|
||||||
|
|
||||||
|
## Command
|
||||||
|
```bash
|
||||||
|
cd apps/api && go test ./internal/star/... ./internal/integration/ -count=1
|
||||||
|
npm run test:h5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gate
|
||||||
|
- [x] Engineer coding PASS
|
||||||
|
- [ ] Reviewer 签核 / Human commit
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
| ID | Kind | Status | Links |
|
| ID | Kind | Status | Links |
|
||||||
|----|------|--------|-------|
|
|----|------|--------|-------|
|
||||||
| — | product | P1 Complete | `.ai/product/feature-map.md` · `feature-spec/` · `p1-status.md` |
|
| — | product | P1 Complete | `.ai/product/feature-map.md` · `feature-spec/` · `p1-status.md` |
|
||||||
| ECR-001 | structural realignment | Phase A–E coded | PRODUCT_SPEC · ENGINEERING_SPEC · HANDOFF · ADR-0007 |
|
| ECR-001 | structural realignment | **Closed** | PRODUCT_SPEC · ENGINEERING_SPEC · HANDOFF · ADR-0007 · STATE |
|
||||||
| ECR-001-B | H5 Home/Synastry split | PASS | TEST_REPORT/ECR-001-phaseB.md · CODE_REVIEW |
|
| ECR-001-B | H5 Home/Synastry split | PASS | TEST_REPORT/ECR-001-phaseB.md · CODE_REVIEW |
|
||||||
| ECR-001-A/C/D/E | docs · membership · engines · outlook | coded → review | TEST_REPORT/ECR-001-phaseA-E.md |
|
| ECR-001-A/C/D/E | docs · membership · engines · outlook pkg | Closed | TEST_REPORT/ECR-001-phaseA-E.md |
|
||||||
| ECR-001-F/H5 | types/sdk + 8 超标页拆分 | review PASS | TEST_REPORT · CODE_REVIEW/ECR-001-phaseF-h5.md |
|
| ECR-001-F/H5 | types/sdk + 8 超标页拆分 | Closed | TEST_REPORT · CODE_REVIEW/ECR-001-phaseF-h5.md |
|
||||||
| ECR-002 | fortune→outlook hygiene | Approved / coded | `docs/ECR/ECR-002-fortune-hygiene.md` |
|
| ECR-002 | fortune→outlook package | **Closed** | `docs/ECR/ECR-002-fortune-hygiene.md` |
|
||||||
|
| ECR-003 | outlook JSON hygiene(删 fortune / lucky→boost) | review PASS | TEST_REPORT · CODE_REVIEW/ECR-003.md |
|
||||||
|
|||||||
Reference in New Issue
Block a user