Files
command-center/agent/managed-skills/pdf-lib-determinism-moddate-leak/SKILL.md
T
Frank 8af40c402b chore(agent): commit the machine tier and the doctrine text the checkout was already running
Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and
`merge --ff-only` refuses while tracked paths are dirty. Discard is not available here
(workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed.

- `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already
  byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new
  prose — the commit exists so the trunk merge can proceed.
- `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat
  owns (AGENTS.md § Live-sync tiers).

`ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the
only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the
inert all-zero AWS example id — and they appear inside the two skill pages that teach
that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged
gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal
dies with this landing. No `--no-verify` was used: the gate ran and its verdict is
recorded here.

Refs #61
2026-10-21 01:44:51 -07:00

2.5 KiB

name, description
name description
pdf-lib-determinism-moddate-leak Diagnose pdf-lib output that isn't byte-identical across fetches: PDFDocument.load restamps /ModDate; fake-clock determinism test; inflate object streams to diff.

pdf-lib byte-identical output: the /ModDate clock leak

Symptom

A "reconstruct-on-read, byte-identical forever" PDF endpoint passes its back-to-back refetch test but returns different bytes when fetches cross a wall-clock second. Same-second fetches match; a fetch 1s later differs (often a different stream /Length, e.g. object stream 411→420 bytes).

Root cause

PDFDocument.load() has its own updateMetadata option (defaults true) and stamps /Info /ModDate from the wall clock at load time, not just at save. doc.save({updateMetadata:false}) on its own is not enough (and in pdf-lib 1.17 updateMetadata isn't even in the SaveOptions type — only in PDFOptions on load); passing it to save also fails typecheck.

Fix

const doc = await PDFDocument.load(templateBytes, { updateMetadata: false });
// ... fill, stamp print layer ...
return doc.save(); // save never stamps when load didn't

Output becomes a pure function of (template bytes, values).

Determinism test law

A determinism test MUST straddle a time boundary. Back-to-back fetches/fills are structurally blind to per-second stamping.

  • Unit: fake the clock (@sinonjs/fake-timers or vi.useFakeTimers) → fill → jump +2 months → re-fill → expect(Buffer.from(second)).toEqual(first). No sleeps, no flake window. Faking Date outside a test framework requires patching globalThis.Date BEFORE the module import that uses it.
  • Prove it red first against the old code (expect the byte/length drift).

Byte-diff forensics (when lengths differ)

  1. Dump the differing region raw: Buffer.subarray(idx-60, idx+60) rendered latin1 with non-printables stripped — locate the exact field (D:YYYYMMDDHHmmss = clock leak).
  2. If the diff is inside a compressed object stream, inflate and compare streams:
import { inflateSync } from "node:zlib";
// find /Type /ObjStm ... stream chunks, inflateSync each, string-compare
  1. pdf-lib escapes text-show operands — inflate + unescape \\(\\[()\\\\]\\) before substring-asserting printed values.

Live proof recipe

Seed a submission, then fetch both retrieval doors at t, t+1.5s, t+3s; sha256sum all three → one hash. Same-second comparisons prove nothing. And kill any long-running dev service holding the app port first — a live :3000-style stack can hang or false-fail an e2e gate.