Skip to content

onboard_member.py isn't idempotent for lab_manual.tex, and reports CV build success it hasn't verified #15

Description

@jeremymanning

Two related problems in the onboarding path, both found by running scripts/onboard_member.py on a scratch copy of the repo.

Part 1: add_member_to_lab_manual has no duplicate guard

scripts/onboard_member.py writes a new member into three places. Two of those writes check whether the member is already present; the third does not.

  • add_to_spreadsheet (scripts/onboard_member.py:855) checks for an existing row before appending.
  • add_to_cv (scripts/onboard_member.py:965-973) checks for an existing entry before appending.
  • add_member_to_lab_manual (scripts/parse_lab_manual.py:125-170) does neither. It locates the appropriate \begin{itemize} section for the member's role and inserts a new \item unconditionally.

Reproduction

  1. Copy the repo to a scratch directory so nothing committed is touched.
  2. Run the onboarding flow for a new member (e.g. an undergrad RA) and let it write to lab_manual.tex.
  3. Run it again with exactly the same inputs. Run it a third time.
  4. grep the member's name in the relevant itemize block of lab_manual.tex.

Expected

The second and third runs are no-ops for lab_manual.tex — the member appears exactly once, matching the behavior of the spreadsheet and CV writers.

Actual

The count of \item entries for that member goes 1 → 2 → 3 across the two re-runs. Every re-run appends another identical \item. The spreadsheet and CV, run in the same invocation, correctly stay at one entry each — so a re-run leaves the three sources of truth disagreeing with each other.

This has happened in production

The lab-manual history already contains a cleanup commit for exactly this failure mode: 80db6d1, "Remove duplicate undergrad entries from re-onboarding." Re-running onboarding is a normal thing to do (a typo in an email address, an interrupted run, a member whose role changed), so this will keep recurring until the guard exists.

Suggested fix

Give add_member_to_lab_manual the same pre-insert check its siblings have: before inserting the \item, scan the target itemize block for the member's name (normalized the same way the CV and spreadsheet checks normalize it) and return early — reporting "already present," not "added" — if found. This makes the whole onboarding script idempotent, which is what its callers already assume.

Part 2: build_cv.py reports success without verifying the PDF

compile_pdf and validate_output in scripts/build_cv.py report a successful CV build based on the LaTeX invocation completing, without confirming that the PDF on disk is actually new and complete. Two ways this goes wrong:

  • Stale PDF. If the LaTeX run fails but a PDF from a previous build is still sitting on disk, the check passes and the build is reported as successful. The onboarding script then tells the operator the CV was rebuilt when the file still reflects the previous state — the new member is silently missing.
  • Truncated PDF. A partial LaTeX failure can leave a short, valid-enough PDF in place. The observed trigger here is the Monaco font failure mode: when the font isn't resolvable, the run degrades and the emitted PDF is well under the real page count, but nothing in validate_output notices.

The net effect is the same in both cases: onboard_member.py prints a success message for a CV build it has not verified, and the operator has no signal that anything went wrong.

Note on current state

The committed JRM_CV.pdf is currently fine — 14 pages, correct content. This is a missing guardrail, not a broken artifact. Nothing needs to be regenerated; the request is to stop reporting unverified success.

Suggested fix

In validate_output:

  1. Capture the PDF's mtime (or hash) before the LaTeX run and require it to change afterward, so a stale file can never pass.
  2. Assert a minimum page count (the CV is ~14 pages; anything in the low single digits means a degraded run) and a plausible minimum file size.
  3. Treat a nonzero LaTeX exit status, or a Font ... not found / missing-font warning in the log, as a hard failure rather than something to fall through.
  4. Propagate the real result up to onboard_member.py so the operator sees a failure when the build failed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions