commit eea3f3327802f839df1c30f382026fb74ef69a63
parent 056cfc2fff24cbf93a81d2962bf1aaf333771b4e
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 8 Oct 2026 22:32:54 -0400
common: a report's notes.json is never published, exported or committed to history
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 28 insertions(+), 0 deletions(-)
diff --git a/common/publish/composeReports.test.ts b/common/publish/composeReports.test.ts
@@ -54,6 +54,8 @@ const VIDEOS = "demo-channel";
const SOCIAL = "demo-social";
const REPORT = "demo-report";
const NOW_FLOOR = new Date().toISOString();
+// What a report's notes.json says: if it shows up in anything built, a note leaked.
+const NOTES_SENTINEL = "operator-note-never-published-7f3a";
const writeJson = (file: string, value: unknown) => {
mkdirSync(path.dirname(file), { recursive: true });
@@ -172,6 +174,8 @@ function seedSite(siteId: string, extra: Record<string, unknown> = {}, withRepor
writeJson(path.join(dir, "report.json"), report());
writeText(path.join(dir, "stills", "a01.png"), "png-bytes");
writeText(path.join(dir, "sources", "s0", "page.html"), "<p>saved copy, never published</p>");
+ // umtool's operator notes live beside report.json and are never published.
+ writeJson(path.join(dir, "notes.json"), { format: "umtool-notes", version: 1, notes: [{ text: NOTES_SENTINEL }] });
seedMedia(siteId);
}
@@ -786,3 +790,27 @@ test("reports export: no browser skips the PDF with a note; no zip fails the pac
assert.equal(await exportMain({ siteId: "cited", formats: ["zip"], zipBin: path.join(ROOT, "no-such-zip") }, quiet), 1);
assert.equal(await exportMain({ siteId: "cited", formats: ["html", "md"] }, quiet), 0);
});
+
+test("a report's notes.json (umtool's operator notes) is never published, exported or committed to its history", async () => {
+ const { reportHistoryGitDir } = await import("./reportHistory");
+ const { execFileSync } = await import("node:child_process");
+ const notes = path.join(paths.sitesDir, "cited", "reports", REPORT, "notes.json");
+ assert.ok(existsSync(notes));
+ await exportSiteReports({ siteId: "cited", paths, now: () => new Date("2026-10-08T08:00:00Z"), openPdfPrinter: fakePrinter([]), onLog: () => {} });
+ await compose("cited");
+ const leaks = (root: string) =>
+ filesUnder(root).filter((f) => f.endsWith("notes.json") || readFileSync(path.join(root, f)).includes(NOTES_SENTINEL));
+ assert.deepEqual(leaks(paths.exportPublicDir), []);
+ assert.deepEqual(leaks(reportExportDir(paths, "cited", REPORT)), []);
+ const gitDir = reportHistoryGitDir(paths, "cited", REPORT);
+ const revs = execFileSync("git", ["--git-dir", gitDir, "rev-list", "--all"], { encoding: "utf8" }).trim().split("\n").filter(Boolean);
+ assert.ok(revs.length > 0);
+ for (const rev of revs) {
+ const names = execFileSync("git", ["--git-dir", gitDir, "ls-tree", "-r", "--name-only", rev], { encoding: "utf8" }).trim().split("\n");
+ assert.deepEqual(names.filter((n) => n.includes("notes")), [], rev);
+ for (const n of names) {
+ const blob = execFileSync("git", ["--git-dir", gitDir, "cat-file", "blob", `${rev}:${n}`]);
+ assert.ok(!blob.includes(NOTES_SENTINEL), `${rev}:${n}`);
+ }
+ }
+});