fix: normalize ambience loudness for v0.2.5

This commit is contained in:
oz
2026-10-14 12:12:13 +00:00
parent 812540c743
commit 91aff66445
390 changed files with 97709 additions and 257 deletions
+167 -9
View File
@@ -4,12 +4,23 @@ import { readFileSync, readdirSync } from "node:fs";
import { expect, it } from "vitest";
import { ENVIRONMENTS } from "../src/content";
// 과거 ASR/보존 검사는 해당 버전의 정확한 바이트를 읽는다. 새 음원의 인수로 재사용하지 않는다.
const readHistoricalBytes = (path: string) =>
readFileSync(
path.startsWith("public/ambience/")
? path.replace(
"public/ambience/",
".agent/evidence-v0.2.5/baseline/ambience/",
)
: path,
);
// 생성기 자기 보고 대신 배포 바이트의 형식·파형·권리 추적 정보를 독립 측정한다.
it("여덟 현장 녹음은 고유 PCM16 WAV·짧은 fade·완전한 provenance/notice를 가진다", () => {
const manifest = JSON.parse(
readFileSync("public/ambience/manifest.json", "utf8"),
);
expect(manifest.version).toBe("0.2.3");
expect(manifest.version).toBe("0.2.5");
expect(
manifest.files.map((f: { environmentId: string }) => f.environmentId),
).toEqual(ENVIRONMENTS.map((e) => e.id));
@@ -177,13 +188,16 @@ it("선택 구간은 승인 계획의 불변 provenance이며 manifest와 고지
}
});
it("최종 ASR 증거는 정확한 WAV와 연결되며 청취·법적 인수로 주장하지 않는다", () => {
it("v0.2.3 최종 ASR 증거는 정확한 WAV와 연결되며 청취·법적 인수로 주장하지 않는다", () => {
const directory = ".agent/evidence-v0.2.3/privacy-screening";
const screening = JSON.parse(
readFileSync(`${directory}/final-asr.json`, "utf8"),
);
const manifest = JSON.parse(
readFileSync("public/ambience/manifest.json", "utf8"),
readFileSync(
".agent/evidence-v0.2.5/baseline/ambience/manifest.json",
"utf8",
),
);
expect(screening.tool).toMatchObject({
name: "faster-whisper",
@@ -212,7 +226,9 @@ it("최종 ASR 증거는 정확한 WAV와 연결되며 청취·법적 인수로
});
expect(screening.method.manifestSha256).toBe(
createHash("sha256")
.update(readFileSync("public/ambience/manifest.json"))
.update(
readFileSync(".agent/evidence-v0.2.5/baseline/ambience/manifest.json"),
)
.digest("hex"),
);
expect(screening.limitations).toMatchObject({
@@ -261,7 +277,7 @@ it("최종 ASR 증거는 정확한 WAV와 연결되며 청취·법적 인수로
).toBe(record.sha256);
});
it("101개 음성·다섯 현장음 및 이전 evidence 바이트를 보존한다", () => {
it("v0.2.3 역사 snapshot은 당시 101개 음성·다섯 현장음 보존을 증명한다", () => {
const baseline = JSON.parse(
readFileSync(
".agent/evidence-v0.2.3/privacy-screening/baseline.json",
@@ -273,7 +289,7 @@ it("101개 음성·다섯 현장음 및 이전 evidence 바이트를 보존한
changedAmbience = 0;
for (const [path, before] of Object.entries(baseline.wavSha256)) {
const actual = createHash("sha256")
.update(readFileSync(path))
.update(readHistoricalBytes(path))
.digest("hex");
if (/public\/ambience\/(mall|market|cafe)\.wav$/.test(path)) {
expect(actual).not.toBe(before);
@@ -288,19 +304,19 @@ it("101개 음성·다섯 현장음 및 이전 evidence 바이트를 보존한
for (const [path, before] of Object.entries(baseline.priorAgentFiles)) {
if (baseline.mutableAgentFiles.includes(path)) continue;
expect(
createHash("sha256").update(readFileSync(path)).digest("hex"),
createHash("sha256").update(readHistoricalBytes(path)).digest("hex"),
path,
).toBe(before);
}
});
it("재시도는 market/cafe만 바꾸고 mall 및 실패·no-VAD 증거를 바이트 그대로 보존한다", () => {
it("v0.2.3 재시도는 market/cafe만 바꾸고 mall 및 실패·no-VAD 증거를 바이트 그대로 보존한다", () => {
const directory = ".agent/evidence-v0.2.3/privacy-screening";
const baseline = JSON.parse(
readFileSync(`${directory}/retry-market24-cafe0/baseline.json`, "utf8"),
);
const hash = (path: string) =>
createHash("sha256").update(readFileSync(path)).digest("hex");
createHash("sha256").update(readHistoricalBytes(path)).digest("hex");
const prior = `${directory}/prior-attempt-market60-cafe180`;
expect(hash(`${prior}/final-asr.json`)).toBe(
"01c8a92fab9f8df7a5f778c5f7e478ce96bcedc33e11a1055bd29154a08b706b",
@@ -352,3 +368,145 @@ it("재시도는 market/cafe만 바꾸고 mall 및 실패·no-VAD 증거를 바
]);
expect([voices, preservedAmbience]).toEqual([101, 6]);
});
// 이번 릴리스는 8 배경을 모두 바꾸되 원본·선택 구간·권리와 101 voice/660 역사 파일을 보존한다.
it("v0.2.5는 이전 증거·음성 바이트와 모든 출처 필드를 그대로 보존한다", () => {
const baseline = JSON.parse(
readFileSync(".agent/evidence-v0.2.5/baseline.json", "utf8"),
);
for (const [path, expected] of Object.entries({
...baseline.immutable,
...baseline.voiceWavs,
}))
expect(
createHash("sha256").update(readFileSync(path)).digest("hex"),
path,
).toBe(expected);
const prior = JSON.parse(
readFileSync(
".agent/evidence-v0.2.5/baseline/ambience/manifest.json",
"utf8",
),
);
const current = JSON.parse(
readFileSync("public/ambience/manifest.json", "utf8"),
);
for (const [index, before] of prior.files.entries()) {
const after = current.files[index];
for (const key of [
"environmentId",
"pageUrl",
"directUrl",
"creator",
"license",
"licenseUrl",
"attribution",
"expectedSourceSha256",
"sourceBytes",
"sourceSha256",
"directMediaDurationSeconds",
"sourceVerification",
"selectedSegmentStartSeconds",
"outputLicense",
])
expect(after[key], key).toEqual(before[key]);
expect(after.sha256).not.toBe(before.sha256);
}
});
// 새 바이트의 ASR은 과거 승인으로 대체하지 않는다. 성공을 강제하려고 결과를 지우면 실패한다.
it("거부된 loudnorm WAV의 ASR 실패와 privacy 인수 경계를 보존한다", () => {
const screening = JSON.parse(
readFileSync(".agent/evidence-v0.2.5/final-asr.json", "utf8"),
);
const manifestBytes = readFileSync(
".agent/evidence-v0.2.5/review-fixes/rejected-loudnorm-bundle/manifest.json",
);
const manifest = JSON.parse(manifestBytes.toString());
expect(screening.method.manifestSha256).toBe(
createHash("sha256").update(manifestBytes).digest("hex"),
);
expect(screening.requiredEmptyPassed).toBe(false);
expect(screening.limitations).toMatchObject({
humanListeningAcceptance: false,
legalPrivacyClearance: false,
textIsVerifiedTranscript: false,
});
for (const entry of manifest.files) {
const record = screening.files.find(
(r: { environmentId: string }) => r.environmentId === entry.environmentId,
);
expect(record).toMatchObject({
sha256: entry.sha256,
sourceSha256: entry.sourceSha256,
selectedSegmentStartSeconds: entry.selectedSegmentStartSeconds,
});
expect(record.segments).toHaveLength(
entry.environmentId === "market" ? 1 : 0,
);
}
});
// 원래 true 요구 테스트와 FAIL 로그는 release-approval/before-ambience.test.ts.txt 및
// 02-original-ASR-FAIL.log에 보존했다. 현재 검사는 실패 진단의 불변성을 강제한다.
// 실제 릴리스 OR 계약은 release-approval.test.ts에서 별도로 검사한다.
it("v0.2.5 exact WAV의 ASR 실패 진단은 원문 바이트·market segment를 보존한다", () => {
expect(
createHash("sha256")
.update(
readFileSync(".agent/evidence-v0.2.5/review-fixes/final-asr.json"),
)
.digest("hex"),
).toBe("98acdb16bc5dce57bb3609b705b63125e5511c2d5f7b26bdde54f4b7bd9a5e49");
const screening = JSON.parse(
readFileSync(".agent/evidence-v0.2.5/review-fixes/final-asr.json", "utf8"),
);
const prior = JSON.parse(
readFileSync(".agent/evidence-v0.2.5/final-asr.json", "utf8"),
);
const bytes = readFileSync("public/ambience/manifest.json");
const manifest = JSON.parse(bytes.toString());
expect(screening.tool).toEqual(prior.tool);
expect(screening.model).toEqual(prior.model);
for (const key of [
"device",
"computeType",
"cpuThreads",
"numWorkers",
"randomSeed",
"parameters",
"vadModelSha256",
])
expect(screening.method[key]).toEqual(prior.method[key]);
expect(screening.method.manifestSha256).toBe(
createHash("sha256").update(bytes).digest("hex"),
);
expect(screening.requiredEmptyEnvironmentIds).toEqual([
"mall",
"market",
"cafe",
]);
expect(screening.requiredEmptyPassed).toBe(false);
expect(screening.limitations).toMatchObject({
humanListeningAcceptance: false,
legalPrivacyClearance: false,
textIsVerifiedTranscript: false,
});
for (const entry of manifest.files) {
const row = screening.files.find(
(r: { environmentId: string }) => r.environmentId === entry.environmentId,
);
expect(row).toMatchObject({
sha256: entry.sha256,
bytes: entry.bytes,
sourceSha256: entry.sourceSha256,
selectedSegmentStartSeconds: entry.selectedSegmentStartSeconds,
});
expect(row.segments).toHaveLength(entry.environmentId === "market" ? 1 : 0);
if (["mall", "cafe"].includes(entry.environmentId)) {
expect(row.segments).toEqual([]);
expect(row.text).toBe("");
expect(row.info.duration_after_vad).toBe(0);
}
}
});