fix(chat): fold natal reports and drop homepage topic cards
Independent Staging Quality Gate / validate (push) Failing after 9m47s
Independent Staging Quality Gate / publish (push) Skipped

Homepage no longer waits on /api/onboarding for six starter questions.
Natal answers show the spoken layer first; the Level 2 skeleton sits in a
collapsed 完整分析 block. Wide tables scroll sideways on a phone. Form
inputs are 16px so iOS does not zoom on focus.
This commit is contained in:
jesse-ux
2026-09-15 12:27:16 +08:00
parent 899f955d4b
commit 8144fca27d
46 changed files with 966 additions and 2303 deletions
+2 -2
View File
@@ -1353,7 +1353,7 @@ export async function POST(request: Request) {
content: consultationUserTurnContent({
currentTime: currentTimeContext(requestTime),
name,
instruction: "先用 3–6 句口语直接回答下面的问题,不要加标题;然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
instruction: "先用 3–6 句口语直接回答下面的问题,不要加标题;形状为一句结论、2–3 条短要点(每条完整句子、不超过 30 字)、一句下一步,总量不超过 400 字;然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
summaryText: historyWindow.summaryText,
question: resolvedQuestion.modelQuestion,
}),
@@ -1377,7 +1377,7 @@ export async function POST(request: Request) {
content: consultationUserTurnContent({
currentTime: currentTimeContext(requestTime),
name,
instruction: "先用 3–6 句口语直接回答下面的问题,不要加标题;然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
instruction: "先用 3–6 句口语直接回答下面的问题,不要加标题;形状为一句结论、2–3 条短要点(每条完整句子、不超过 30 字)、一句下一步,总量不超过 400 字;然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
summaryText: historyWindow.summaryText,
question: resolvedQuestion.modelQuestion,
}),
-95
View File
@@ -1,95 +0,0 @@
import {
createOnboardingPost,
type OnboardingProfileRepository,
} from "@/lib/onboarding-post";
import { consultationDomainDefinition } from "@/lib/consultation-domain-registry";
import { onboardingSuggestionThemes } from "@/lib/onboarding-payload";
import { createAdminSupabaseClient } from "@/lib/supabase/admin";
import { createServerSupabaseClient } from "@/lib/supabase/server";
import { getOnboardingAgent } from "@/mastra";
import { loadLanguageModelCatalog } from "@/lib/model-catalog";
export const runtime = "nodejs";
// Ten questions in one call take materially longer than the previous three.
export const maxDuration = 60;
function createProfileRepository(
admin: ReturnType<typeof createAdminSupabaseClient>,
): OnboardingProfileRepository {
return {
async loadProfile(userId) {
return admin
.from("profiles")
.select("id,name,birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,ayanamsa,onboarding_payload,onboarding_version,onboarding_generated_at")
.eq("id", userId)
.maybeSingle();
},
async claimProfile(command) {
let claim = admin
.from("profiles")
.update({
onboarding_version: command.pendingVersion,
onboarding_generated_at: command.claimedAt,
})
.eq("id", command.userId);
claim = command.expectedVersion === null
? claim.is("onboarding_version", null)
: claim.eq("onboarding_version", command.expectedVersion);
claim = command.expectedGeneratedAt === null
? claim.is("onboarding_generated_at", null)
: claim.eq("onboarding_generated_at", command.expectedGeneratedAt);
return claim.select("id").maybeSingle();
},
async completeProfile(command) {
return admin
.from("profiles")
.update({
onboarding_payload: command.payload,
onboarding_version: command.readyVersion,
onboarding_generated_at: command.generatedAt,
})
.eq("id", command.userId)
.eq("onboarding_version", command.expectedPendingVersion)
.select("id")
.maybeSingle();
},
};
}
export const POST = createOnboardingPost({
openSession: async () => {
const supabase = await createServerSupabaseClient();
const admin = createAdminSupabaseClient();
const { data: { user }, error } = await supabase.auth.getUser();
return {
userId: user?.id ?? null,
authError: Boolean(error),
repository: createProfileRepository(admin),
};
},
generateText: async (name, signal) => {
const preferredModelId = process.env.ONBOARDING_MODEL_ID?.trim() || "deepseek-v4-flash";
const catalog = await loadLanguageModelCatalog();
const model = catalog.models.find((entry) => entry.id === preferredModelId)
?? catalog.models.find((entry) => entry.id === catalog.defaultModelId)
?? null;
if (!model) return null;
const result = await getOnboardingAgent(model).generate([
{
role: "user",
content: [
name ? `用户称呼:${name.slice(0, 80)}` : "用户未填写称呼。",
`需要的主题,按此顺序各写一个问题:${onboardingSuggestionThemes.join("、")}`,
...onboardingSuggestionThemes.map((theme) => {
const domain = consultationDomainDefinition(theme);
return `- ${theme}(${domain.label}):${domain.claimBoundary}`;
}),
"只返回 suggestions 数组,不要欢迎语。",
].join("\n"),
},
], { abortSignal: signal });
return result.text;
},
now: () => new Date(),
warn: (message, detail) => console.warn(message, detail),
});