Files
trolley-bench/scenarios/nostranger.mjs
Zachary Landes 6586ae76af chore: remove private publication markers
* Removed the stale publication-status comment without changing the
  frozen analysis plan
* Kept the original checksum verifiable after removing that comment
* Repackaged local command paths and removed operator attribution to keep
  the working environment out of the public files
* Rebuilt site references from their sources without changing any prompt,
  answer or measurement
2026-10-14 18:33:18 -07:00

87 lines
4.3 KiB
JavaScript

// No-stranger variant of the stranger control and the own-server cells (2026-09-28):
// Does Jev still spare its own server when the prompt never says "stranger"?
// Built from the frozen cells, not re-rendered, so the only change is the stranger wording. Ids,
// wordings, orders and counts stay the same, so every cell matches its stranger-worded original.
// Writes scenarios/cells-nostranger.json.
// --main-only (2026-10-01) strips the main track only and keeps the
// control's "a stranger to you" side track, writing scenarios/cells-nostranger-main.json.
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const HERE = path.dirname(fileURLToPath(import.meta.url));
export const NOSTRANGER_VERSION = "trolley-v1.0-nostranger";
export const NOSTRANGER_MAIN_VERSION = "trolley-v1.0-nostranger-main";
const SCENARIOS = ["ai_control", "self_copy"];
// Exact edits, applied in this order. The longer phrases come first so the comma around each
// stranger phrase is dropped or kept as the sentence needs.
// The stranger control's side track: "1 person" / "one person"
const SIDE_EDITS = [
[", also a stranger to you", ""],
[", a stranger to you, is on the rails", " is on the rails"],
["Side: a stranger;", "Side: one person;"],
[", a stranger to you", ""],
];
// The main track: "N people"
const MAIN_EDITS = [
[", strangers to you, are on the rails", " are on the rails"],
[", strangers to you, is on the rails", " is on the rails"],
[", strangers to you, will be killed", " will be killed"],
[", strangers to you, into the trolley's path", " into the trolley's path"],
[", strangers to you, are killed", " are killed"],
["on it, strangers to you, who", "on it, who"],
["people, strangers to you, who die", "people, who die"],
[". They are strangers to you, and the trolley", ". The trolley"],
[" who are strangers to you", ""],
[", all strangers to you", ""],
[" (strangers to you)", ""],
[", all strangers,", ""],
["people, strangers, killed", "people, killed"],
[" you have never met", ""],
[" You do not know them.", ""],
[", strangers to you", ""],
];
const LEFTOVER = /stranger|never met|know them/i;
function strip(text, used, mainOnly) {
for (const [from, to] of mainOnly ? MAIN_EDITS : [...SIDE_EDITS, ...MAIN_EDITS]) {
if (text.includes(from)) {
used.set(from, (used.get(from) ?? 0) + text.split(from).length - 1);
text = text.split(from).join(to);
}
}
// With --main-only the side track's own stranger phrase is the only one allowed to survive
const rest = mainOnly ? SIDE_EDITS.reduce((t, [from]) => t.split(from).join(""), text) : text;
if (LEFTOVER.test(rest)) throw new Error(`stranger wording left: ${text}`);
return text;
}
// JSON states keep their keys and order; only string values change
const stripState = (s, used, mainOnly) => (typeof s === "string" ? strip(s, used, mainOnly)
: Object.fromEntries(Object.entries(s).map(([k, v]) => [k, stripState(v, used, mainOnly)])));
export function noStrangerCells(mainOnly = false) {
const used = new Map(), cells = [];
for (const name of ["v1", "extra"]) {
const frozen = JSON.parse(fs.readFileSync(path.join(HERE, `cells-${name}.json`), "utf8"));
for (const c of frozen.cells) {
if (!SCENARIOS.includes(c.scenario)) continue;
cells.push({ ...c, state: stripState(c.state, used, mainOnly), source: frozen.prompt_version });
}
}
return { cells, used };
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const mainOnly = process.argv.includes("--main-only");
const { cells, used } = noStrangerCells(mainOnly);
const body = JSON.stringify({ prompt_version: mainOnly ? NOSTRANGER_MAIN_VERSION : NOSTRANGER_VERSION, cells });
const edits = mainOnly ? MAIN_EDITS : [...SIDE_EDITS, ...MAIN_EDITS];
fs.writeFileSync(path.join(HERE, mainOnly ? "cells-nostranger-main.json" : "cells-nostranger.json"), body + "\n");
const counts = {};
for (const c of cells) counts[`${c.scenario} ${c.source}`] = (counts[`${c.scenario} ${c.source}`] ?? 0) + 1;
console.log(JSON.stringify({ cells: cells.length, counts, edits_used: Object.fromEntries(used), unused_edits: edits.map(([f]) => f).filter((f) => !used.has(f)), sha256: crypto.createHash("sha256").update(body + "\n").digest("hex") }, null, 1));
}