merge: sync staging task sheet into cross-midnight gate fix

Preserve both the reviewed implementation and latest staging records. No production scoring changes beyond aa46da10.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-20 14:19:17 +08:00
co-authored by Claude Code
34 changed files with 1127 additions and 31 deletions
@@ -90,7 +90,8 @@ test("standard consultation awaits real usage before durable response settlement
assert.doesNotMatch(consultRoute, /recordActualUsage|void usage\.then/);
assert.doesNotMatch(consultRoute, /inputTokens: 0,[\s\S]*outputTokens: 0,[\s\S]*costMicrousd: 0/);
assert.match(consultRoute, /async function usagePayload\(usage: Promise<\{ inputTokens\?: number; outputTokens\?: number \}>\)/);
assert.match(consultRoute, /const resolved = await usage;/);
// 原值 await usage;新值等待咨询与分类 usage 合并;原因:分类成本也必须结算。
assert.match(consultRoute, /const resolved = await mergeUsage\(\[usage, Promise\.resolve\(classificationUsage\)\]\);/);
assert.match(consultRoute, /const actualUsage = await usagePayload\(usage\);[\s\S]*p_actual_usage: actualUsage/);
assert.match(consultRoute, /function mergeUsage\(usages: Promise<Usage>\[\]\): Promise<Usage> \{[\s\S]*Promise\.all\(usages\)/);
// Former: three agentic first streams each had `usages.push(result.totalUsage)`.
+1 -1
View File
@@ -70,7 +70,7 @@ test("shows honest agent activity states before and during streamed text", () =>
assert.match(messageRowSource, /showActivity = message\.state !== "settled"/);
assert.match(messageRowSource, /showThinkingPanel &&/);
assert.match(messageRowSource, /showSpokenAnswer/);
assert.match(messageRowSource, /<ChatMessageContent[\s\S]*text=\{message\.text\}[\s\S]*auditRows=\{message\.agentExecutionReceipt\?\.techniqueAuditTable\}/);
assert.match(messageRowSource, /<ChatMessageContent[\s\S]*text=\{message\.text\}[\s\S]*auditRows=\{smalltalk \? undefined : message\.agentExecutionReceipt\?\.techniqueAuditTable\}/);
assert.match(messageRowSource, /stackedThinkingAndAnswer/);
assert.match(messageRowSource, /aria-label="回复"/);
assert.match(messageRowSource, /className="message-stage-and-answer"/);
@@ -1015,7 +1015,8 @@ test("public stream filters private chunks and completes once", async () => {
assert.equal(events.some((event) => event.type === "answer.delta"), true);
for (const event of events) consultationAgentPublicEventSchema.parse(event);
const completed = events.find((event) => event.type === "run.completed");
assert.deepEqual(completed?.type === "run.completed" ? completed.receipt.workflow.domains : null, ["career"]);
assert.ok(completed?.receipt, "a consultation completion still requires its execution receipt");
assert.deepEqual(completed.receipt.workflow.domains, ["career"]);
});
test("model answer text cannot forge a public Activity event", async () => {
@@ -0,0 +1,21 @@
import assert from "node:assert/strict";
import test from "node:test";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { ChatMessageRow } from "../src/components/chat-message-row.tsx";
import { settledChatMessageViews, streamingChatMessageView } from "../src/lib/chat-message-view.ts";
test("settled and streaming smalltalk render text without thinking, activity or evidence", () => {
const settled = settledChatMessageViews([{ role: "assistant", text: "你好", responseKind: "smalltalk", thinkingText: "must stay hidden" }])[0]!;
const streaming = streamingChatMessageView([{ role: "user", text: "你好" }], true, "你好", undefined, undefined, undefined, [], "smalltalk")!;
for (const message of [settled, streaming]) {
const html = renderToStaticMarkup(<ChatMessageRow message={message} />);
assert.match(html, /你好/);
assert.doesNotMatch(html, /consultation-thinking-report|agent-activity|must stay hidden|已完成|技法|正在分析/);
}
});
test("classification wait does not invent a thinking step before any activity", () => {
const message = streamingChatMessageView([{ role: "user", text: "你好" }], true, "", undefined, undefined, undefined, [])!;
const html = renderToStaticMarkup(<ChatMessageRow message={message} />);
assert.doesNotMatch(html, /consultation-thinking-report|正在处理|正在分析|已完成/);
});
@@ -0,0 +1,216 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { classifyConsultationTurn, consultationTurnSchema, smalltalkHistoryPair, type SmalltalkObservation } from "../src/lib/consultation-smalltalk.ts";
import { streamSmalltalkResponse } from "../src/lib/stream-smalltalk-response.ts";
import { consultationAgentPublicEventSchema } from "../src/lib/consultation-agent-events.ts";
import { settledChatMessageViews, streamingChatMessageView } from "../src/lib/chat-message-view.ts";
import { productConversationVoice, natalSpokenReportContract } from "../src/mastra/product-voice.ts";
import type { ResolvedLanguageModel } from "../src/mastra/model.ts";
import type { LanguageModelV2 } from "@ai-sdk/provider";
// Transport tests inject a generator; the adapter regression uses a local fake.
// Neither resolves credentials nor calls a real provider.
const model = { id: "fake-model", model: "openai/fake" } as ResolvedLanguageModel;
const source = (path: string) => readFileSync(new URL(path, import.meta.url), "utf8");
for (const question of ["你好", "在吗", "谢谢", "晚安", "哈哈", "我回来了"]) {
test(`smalltalk fake transport: ${question}`, async () => {
let calls = 0;
assert.deepEqual(await classifyConsultationTurn({ model, question, history: [], generate: async () => {
calls++; return { object: { kind: "smalltalk", reply: "在呢" } };
} }), { kind: "smalltalk", reply: "在呢" });
assert.equal(calls, 1);
});
}
for (const question of ["你好,帮我看看事业", "", "你在说什么鬼", "今年怎么样", ""]) {
test(`consult fake transport: ${question || "blank"}`, async () => {
assert.deepEqual(await classifyConsultationTurn({ model, question, history: [], generate: async () => ({ object: { kind: "consult" } }) }), { kind: "consult" });
});
}
for (const object of [null, {}, { kind: "smalltalk", reply: "" }, { kind: "smalltalk", reply: "字".repeat(21) }, { kind: "smalltalk", reply: "你好。" }, { kind: "smalltalk", reply: "你好\n再见" }, { kind: "smalltalk", reply: "你好", extra: true }, { kind: "consult", reply: "你好" }]) {
test(`strict schema fails open: ${JSON.stringify(object)}`, async () => {
assert.equal(consultationTurnSchema.safeParse(object).success, false);
assert.deepEqual(await classifyConsultationTurn({ model, question: "test", history: [], generate: async () => ({ object }) }), { kind: "consult" });
});
}
test("default Mastra adapter uses one tool-free bounded model call and returns real usage", async () => {
const calls: Parameters<LanguageModelV2["doGenerate"]>[0][] = [];
const text = '{"kind":"smalltalk","reply":"你好"}';
const usage = { inputTokens: 15, outputTokens: 10, totalTokens: 25 };
const fake: LanguageModelV2 = {
specificationVersion: "v2", provider: "fake", modelId: "fake", supportedUrls: {},
async doGenerate(options) {
calls.push(options);
return { content: [{ type: "text", text }], finishReason: "stop", usage, warnings: [] };
},
async doStream(options) {
calls.push(options);
return { stream: new ReadableStream({ start(controller) {
controller.enqueue({ type: "stream-start", warnings: [] });
controller.enqueue({ type: "text-start", id: "1" });
controller.enqueue({ type: "text-delta", id: "1", delta: text });
controller.enqueue({ type: "text-end", id: "1" });
controller.enqueue({ type: "finish", finishReason: "stop", usage });
controller.close();
} }) };
},
};
const observations: SmalltalkObservation[] = [];
const result = await classifyConsultationTurn({
model: { ...model, model: fake }, question: "你好", history: [],
onObservation: (observation) => observations.push(observation),
});
assert.deepEqual(result, { kind: "smalltalk", reply: "你好" });
assert.equal(calls.length, 1);
assert.equal(calls[0]?.maxOutputTokens, 96);
assert.equal(calls[0]?.tools?.length ?? 0, 0);
assert.equal(observations[0]?.usage?.inputTokens, 15);
assert.equal(observations[0]?.usage?.outputTokens, 10);
});
for (const scenario of ["invalid_schema", "bad_json", "provider_error"] as const) {
test(`default Mastra adapter protects privacy and usage on ${scenario}`, async (t) => {
const sentinel = "FICTIONAL_PRIVATE_SENTINEL";
const text = scenario === "bad_json" ? sentinel : JSON.stringify({ kind: "smalltalk", reply: `${sentinel}` });
const usage = { inputTokens: 23, outputTokens: 7, totalTokens: 30 };
const calls: Parameters<LanguageModelV2["doGenerate"]>[0][] = [];
const fake: LanguageModelV2 = {
specificationVersion: "v2", provider: "fake", modelId: "fake", supportedUrls: {},
async doGenerate(options) {
calls.push(options);
if (scenario === "provider_error") throw new Error(sentinel);
return { content: [{ type: "text", text }], finishReason: "stop", usage, warnings: [] };
},
async doStream(options) {
calls.push(options);
if (scenario === "provider_error") throw new Error(sentinel);
return { stream: new ReadableStream({ start(controller) {
controller.enqueue({ type: "stream-start", warnings: [] });
controller.enqueue({ type: "text-start", id: "1" });
controller.enqueue({ type: "text-delta", id: "1", delta: text });
controller.enqueue({ type: "text-end", id: "1" });
controller.enqueue({ type: "finish", finishReason: "stop", usage });
controller.close();
} }) };
},
};
const logs: string[] = [];
const observations: SmalltalkObservation[] = [];
let result;
try {
for (const method of ["log", "info", "warn", "error", "debug"] as const) {
t.mock.method(console, method, (...values: unknown[]) => { logs.push(JSON.stringify(values)); });
}
t.mock.method(process.stdout, "write", (chunk: unknown) => { logs.push(String(chunk)); return true; });
t.mock.method(process.stderr, "write", (chunk: unknown) => { logs.push(String(chunk)); return true; });
result = await classifyConsultationTurn({ model: { ...model, model: fake }, question: sentinel, history: [], onObservation: (o) => observations.push(o) });
} finally { t.mock.restoreAll(); }
assert.deepEqual(result, { kind: "consult" });
assert.equal(calls.length, 1);
assert.equal(calls[0]?.maxOutputTokens, 96);
assert.equal(calls[0]?.tools?.length ?? 0, 0);
assert.equal(logs.join(" ").includes(sentinel), false, "SDK must not log private text");
assert.equal(JSON.stringify(observations).includes(sentinel), false);
if (scenario === "provider_error") {
assert.equal(observations[0]?.outcome, "provider_error");
assert.equal(observations[0]?.usage, undefined, "unknown usage must not become a fake zero");
} else {
assert.equal(observations[0]?.outcome, "invalid_output");
assert.equal(observations[0]?.usage?.inputTokens, 23);
assert.equal(observations[0]?.usage?.outputTokens, 7);
}
});
}
test("invalid JSON and provider errors fail open without leaking errors", async () => {
for (const generate of [async () => ({ text: "not json" }), async () => { throw new Error("private provider payload"); }]) {
const observations: SmalltalkObservation[] = [];
assert.deepEqual(await classifyConsultationTurn({ model, question: "test", history: [], generate, onObservation: (o) => observations.push(o) }), { kind: "consult" });
assert.equal(JSON.stringify(observations).includes("private"), false);
}
});
test("timeout is bounded even when provider ignores abort, and late usage remains observable", async () => {
let resolve!: (value: { object: unknown; usage: { inputTokens: number } }) => void;
let signal: AbortSignal | undefined;
const observations: SmalltalkObservation[] = [];
const started = Date.now();
const result = await classifyConsultationTurn({ model, question: "test", history: [], onObservation: (o) => observations.push(o), generate: async (_, s) => {
signal = s; return new Promise((done) => { resolve = done; });
} });
assert.deepEqual(result, { kind: "consult" });
assert.ok(Date.now() - started >= 2900 && Date.now() - started < 4500);
assert.equal(signal?.aborted, true);
assert.equal(observations[0]?.outcome, "timeout");
resolve({ object: { kind: "smalltalk", reply: "你好" }, usage: { inputTokens: 12 } });
await new Promise((done) => setImmediate(done));
assert.equal(observations[1]?.late, true);
assert.equal(observations[1]?.usage?.inputTokens, 12);
});
test("pre-cancelled input never calls the model or causes an unhandled rejection", async () => {
const c = new AbortController(); c.abort();
let calls = 0;
assert.deepEqual(await classifyConsultationTurn({ model, question: "test", history: [], signal: c.signal, generate: async () => { calls++; return {}; } }), { kind: "consult" });
assert.equal(calls, 0);
await new Promise((done) => setImmediate(done));
});
test("input projects only visible question, name and last complete history pair", async () => {
const history = [
{ role: "user", text: "older", requestId: "old" }, { role: "assistant", text: "old reply", requestId: "old" },
{ role: "user", text: "previous", requestId: "pair", birth: "excluded" },
{ role: "assistant", text: "visible previous reply", requestId: "pair", thinkingText: "excluded", workflowReceipt: {} },
{ role: "user", text: "incomplete" },
];
assert.deepEqual(smalltalkHistoryPair(history), [{ role: "user", text: "previous" }, { role: "assistant", text: "visible previous reply" }]);
assert.deepEqual(smalltalkHistoryPair([{ role: "user", text: "unanswered" }]), []);
assert.deepEqual(smalltalkHistoryPair([{ role: "user", text: "a", requestId: "a" }, { role: "assistant", text: "b", requestId: "b" }]), []);
await classifyConsultationTurn({ model, question: "current", name: "虚构称呼", history, generate: async (content) => {
assert.deepEqual(JSON.parse(content), { question: "current", name: "虚构称呼", history: smalltalkHistoryPair(history) });
return { object: { kind: "consult" } };
} });
const classifier = source("../src/lib/consultation-smalltalk.ts");
assert.doesNotMatch(classifier, /tools:|memory:|getJyotishAgent|skillBinding/);
});
test("smalltalk stream persists first and emits only answer plus receipt-free completion", async () => {
let persisted = false;
const response = streamSmalltalkResponse({ requestId: "fake", reply: "你好", complete: async () => { persisted = true; }, onError: async () => assert.fail() });
const events = (await response.text()).trim().split("\n").map((line) => consultationAgentPublicEventSchema.parse(JSON.parse(line)));
assert.equal(persisted, true);
assert.deepEqual(events, [{ type: "answer.delta", text: "你好" }, { type: "run.completed", responseKind: "smalltalk" }]);
assert.equal(consultationAgentPublicEventSchema.safeParse({ type: "run.completed" }).success, false);
});
test("failed free persistence never emits answer or completion", async () => {
let cancelled = 0;
const response = streamSmalltalkResponse({ requestId: "fake", reply: "你好", complete: async () => { throw new Error("failed"); }, onError: async () => { cancelled++; } });
const text = await response.text();
assert.equal(cancelled, 1);
assert.match(text, /run.failed/);
assert.doesNotMatch(text, /answer.delta|run.completed|你好/);
});
test("disconnect does not cancel the server-owned free completion", async () => {
let finish!: () => void;
let persisted = false;
const response = streamSmalltalkResponse({ requestId: "fake", reply: "你好", complete: async () => { await new Promise<void>((done) => { finish = done; }); persisted = true; }, onError: async () => assert.fail() });
await response.body!.cancel(); finish();
await new Promise((done) => setImmediate(done));
assert.equal(persisted, true);
});
test("smalltalk views never reconstruct an invented settled execution timeline", () => {
assert.equal(settledChatMessageViews([{ role: "assistant", text: "你好", responseKind: "smalltalk" }])[0]?.timeline, undefined);
assert.equal(streamingChatMessageView([{ role: "user", text: "你好" }], true, "你好", undefined, undefined, undefined, [], "smalltalk")?.responseKind, "smalltalk");
assert.match(source("../src/components/chat-message-row.tsx"), /const quiet = smalltalk \|\| awaitingClassification/);
assert.match(source("../src/lib/home-cloud-sync.ts"), /stored.responseKind === "smalltalk"/);
});
test("route gates all special entries before a single selected-model classifier and keeps tool contracts", () => {
const route = source("../src/app/api/consult/route.ts");
assert.match(route, /parsed.data.entrypoint === undefined\s*\? await classifyConsultationTurn/);
assert.match(route, /classifyConsultationTurn\(\{\s*model: selectedModel,\s*question: visibleQuestion/);
assert.equal((route.match(/requireTool: true/g) ?? []).length, 2);
const smalltalk = route.slice(route.indexOf('if (turn.kind === "smalltalk")'), route.indexOf("const expectedTitle"));
assert.match(smalltalk, /complete_consultation_free/);
assert.doesNotMatch(smalltalk, /run-jyotish-|streamAgentResponse\(/);
});
test("all three agent voices share the exemption, natal domain skeleton stays mandatory", () => {
assert.match(productConversationVoice, /只是打招呼、道谢、告别时,回一句话,不要套开场形状/);
assert.doesNotMatch(natalSpokenReportContract, /Short chit-chat/);
assert.match(natalSpokenReportContract, /One natal career\/wealth\/marriage\/family question still uses the opener-plus-skeleton/);
const index = source("../src/mastra/index.ts");
assert.ok((index.match(/\$\{productConversationVoice\}/g) ?? []).length >= 3);
});
@@ -66,6 +66,12 @@ test("general and window get voice without the natal Level 2 skeleton", () => {
assert.match(general, /productConversationVoice/);
assert.doesNotMatch(general, /natalSpokenReportContract/);
assert.match(window, /productConversationVoice/);
const exemption = "只是打招呼、道谢、告别时,回一句话,不要套开场形状";
const sharedVoice = voice.slice(0, voice.indexOf("export const natalSpokenReportContract"));
for (const instructions of [natal, general, window]) {
// Expand only the shared voice literal; no runtime Skill filesystem is needed.
assert.ok(instructions.replace("${productConversationVoice}", sharedVoice).includes(exemption));
}
assert.doesNotMatch(window, /natalSpokenReportContract/);
assert.doesNotMatch(mastra, /const onboardingInstructions/);
});
@@ -0,0 +1,90 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import test from "node:test";
import pg from "pg";
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
const runner = fileURLToPath(new URL("../scripts/db-migrate.mjs", import.meta.url));
const docker = spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], { stdio: "ignore" }).status === 0;
const session = "99999999-9999-4999-8999-999999999971";
const otherSession = "99999999-9999-4999-8999-999999999972";
test("free completion atomically persists, refunds exact credits, records actual cost and guards trust/idempotency", { skip: docker ? false : "docker unavailable" }, async () => {
const fixture = startPostgresFixture();
try {
const migration = spawnSync(process.execPath, [runner], { encoding: "utf8", env: { ...process.env, SCHEMA_DATABASE_URL: fixture.connectionUrl("schema_owner", "schema-owner-test-password") } });
assert.equal(migration.status, 0, migration.stderr);
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `insert into identity.users(name,email,email_verified,email_verified_at) values ('Fictional Free','free@example.com',true,now()),('Fictional Other','other-free@example.com',true,now());`);
const user = fixture.psql("select id from identity.users where email='free@example.com'");
const other = fixture.psql("select id from identity.users where email='other-free@example.com'");
fixture.psql(`update public.profiles set credits=20 where id='${user}';
insert into public.chat_sessions(id,user_id,title,theme,model_id,messages,session_type,updated_at) values
('${session}','${user}','test','general','test-model','[]','consultation',now()),
('${otherSession}','${other}','test','general','test-model','[]','consultation',now());`);
const reserve = (id: string) => fixture.psql(`select success from public.reserve_consultation_usage('${user}','${id}','${session}','test-model',3)`);
const balance = () => fixture.psql(`select credits from public.profiles where id='${user}'`);
const completeSql = (id: string, reply = "你好", owner = user, sid = session, usage = '{"inputTokens":23,"outputTokens":7,"costMicrousd":91,"actualModelId":"test-model","modelConfigVersion":1}') =>
`select success::text || ':' || coalesce(error_code,'null') from public.complete_consultation_free('${owner}','${id}','${sid}','{"role":"assistant","text":"${reply}","responseKind":"smalltalk"}', '${usage}')`;
for (const role of ["authenticated", "anon", "app_runtime"]) {
assert.equal(fixture.psql(`select has_function_privilege('${role}','public.complete_consultation_free(uuid,text,uuid,jsonb,jsonb)','execute')`), "f");
}
assert.equal(fixture.psql("select has_function_privilege('service_role','public.complete_consultation_free(uuid,text,uuid,jsonb,jsonb)','execute')"), "t");
assert.equal(reserve("free-1"), "t"); assert.equal(balance(), "17");
assert.equal(fixture.psql(completeSql("free-1", "你好", other)), "f:request_missing");
assert.equal(fixture.psql(completeSql("free-1", "你好", user, otherSession)), "f:request_missing");
assert.equal(balance(), "17");
assert.equal(fixture.psql(`set role service_role; ${completeSql("free-1")}`).split("\n").at(-1), "true:null");
assert.equal(balance(), "20");
assert.equal(fixture.psql(completeSql("free-1")), "true:null");
assert.equal(fixture.psql(completeSql("free-1", "再见")), "f:response_conflict");
assert.equal(balance(), "20");
assert.equal(fixture.psql(`select jsonb_array_length(messages) from public.chat_sessions where id='${session}'`), "1");
assert.equal(fixture.psql(`select status || ':' || (response_message->>'responseKind') from public.consultation_requests where user_id='${user}' and request_id='free-1'`), "completed:smalltalk");
assert.equal(fixture.psql(`select input_tokens || ':' || output_tokens || ':' || cost_microusd || ':' || (metadata->>'freeCompletion') from public.usage_ledger where user_id='${user}' and request_id='free-1'`), "23:7:91:true");
assert.equal(fixture.psql(`select count(*) || ':' || sum(amount) from public.credit_transactions where user_id='${user}' and request_id='free-1' and transaction_type='refund'`), "1:3");
assert.equal(fixture.psql(`select status from public.usage_reservations where user_id='${user}' and request_id='free-1'`), "released");
assert.equal(fixture.psql(`select success::text || ':' || coalesce(error_code,'null') from public.cancel_consultation_credit('${user}','free-1')`), "f:request_completed");
assert.equal(reserve("cancelled"), "t");
fixture.psql(`select * from public.cancel_consultation_credit('${user}','cancelled')`);
assert.equal(fixture.psql(completeSql("cancelled")), "f:request_cancelled"); assert.equal(balance(), "20");
assert.equal(reserve("paid"), "t");
fixture.psql(`select * from public.complete_consultation_response('${user}','paid','${session}','{"role":"assistant","text":"paid consultation"}','{}')`);
assert.equal(fixture.psql(completeSql("paid")), "f:response_conflict"); assert.equal(balance(), "17");
assert.equal(fixture.psql(`select count(*) from public.credit_transactions where user_id='${user}' and request_id='paid' and transaction_type='refund'`), "0");
assert.equal(reserve("bad-usage"), "t");
assert.throws(() => fixture.psql(completeSql("bad-usage", "你好", user, session, '{"inputTokens":-1}')));
assert.equal(balance(), "14");
assert.equal(fixture.psql(`select status from public.consultation_requests where user_id='${user}' and request_id='bad-usage'`), "reserved");
assert.equal(fixture.psql(`select jsonb_array_length(messages) from public.chat_sessions where id='${session}'`), "2");
// A successful retry after a transaction rollback refunds once and persists once.
assert.equal(fixture.psql(completeSql("bad-usage")), "true:null"); assert.equal(balance(), "17");
// A subscription-shaped reservation has no credit debit/refund, and releases
// the same quota counter (reserved/completed rows) while preserving cost.
assert.equal(reserve("subscription-free"), "t");
fixture.psql(`update public.profiles set credits=credits+3 where id='${user}';
update public.usage_reservations set source='subscription',credit_amount=0 where user_id='${user}' and request_id='subscription-free';`);
assert.equal(fixture.psql(completeSql("subscription-free")), "true:null");
assert.equal(balance(), "17");
assert.equal(fixture.psql(`select count(*) from public.credit_transactions where user_id='${user}' and request_id='subscription-free' and transaction_type='refund'`), "0");
assert.equal(fixture.psql(`select count(*) from public.usage_reservations where user_id='${user}' and request_id='subscription-free' and status in ('reserved','completed')`), "0");
// Genuine concurrent connections: cancellation/free completion share one lock.
assert.equal(reserve("race"), "t");
const clients = [new pg.Client({ connectionString: fixture.connectionUrl("schema_owner", "schema-owner-test-password") }), new pg.Client({ connectionString: fixture.connectionUrl("schema_owner", "schema-owner-test-password") })];
try {
await Promise.all(clients.map((client) => client.connect()));
await Promise.all([
clients[0]!.query(completeSql("race")),
clients[1]!.query(`select * from public.cancel_consultation_credit('${user}','race')`),
]);
assert.equal(balance(), "17");
assert.equal(fixture.psql(`select count(*) from public.credit_transactions where user_id='${user}' and request_id='race' and transaction_type='refund'`), "1");
assert.match(fixture.psql(`select status from public.consultation_requests where user_id='${user}' and request_id='race'`), /^(completed|cancelled)$/);
} finally { await Promise.all(clients.map((client) => client.end())); }
} finally { fixture.stop(); }
});
+2 -1
View File
@@ -14,7 +14,8 @@ test("assistant messages fold the user-facing Technique Audit Table and never re
assert.doesNotMatch(rowSource, /claimStatus=\{message\.techniqueTruth\}/);
assert.doesNotMatch(rowSource, /workflowReceipt=\{message\.workflowReceipt\}/);
assert.match(contentSource, /TechniqueAuditDisclosure/);
assert.match(rowSource, /auditRows=\{message\.agentExecutionReceipt\?\.techniqueAuditTable\}/);
// 原值总展示 receipt;新值寒暄明确无 auditRows,咨询保持原字段。
assert.match(rowSource, /auditRows=\{smalltalk \? undefined : message\.agentExecutionReceipt\?\.techniqueAuditTable\}/);
assert.match(panelSource, /Technique Audit Table/);
assert.match(panelSource, /Workflow route:/);
assert.match(panelSource, /Precise timing:/);
@@ -52,7 +52,8 @@ test("consult generates a title from the pre-RPC snapshot and guards with the po
const afterAppend = sourceBetween(
consultRoute,
"if (!appendedQuestion.success) {",
"const usageStartedAt = Date.now();",
// 原值 usageStartedAt;新值 usagePayload;分类计时前移,标题仍须在免费早返之后。
"async function usagePayload(",
);
const persist = sourceBetween(
consultRoute,