Record what an executable was built with, alongside the executable?
Nobody has claimed this yet.
- Dominant language
- R
- Stars
- 160
- Forks
- 69
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 15
Description
OPEN
Record what an executable was built with, alongside the executable?
Note: While working on #1235 I was reminded of several longstanding issues. I asked Claude to turn them into reports. This body has been rewritten to reflect the design settled in #1254. The original version and the review that followed are in this issue's edit history.
CmdStanR cannot tell what produced an existing executable. The binary reports a handful of STAN_* flags via <exe> info and nothing else. The user header, the include paths and the rest of cpp_options are not reported. Anything else lives only in the R object, so it is gone in the next session.
The fix is a JSON record written beside the executable at build time, .<exe>.cmdstanr.json. This issue is the record itself; #1255 is what acts on it. The contract is #1254 §4 ("Contract: the build record"), whose recorded/compared table is the single statement of what the record holds and which fields drive a rebuild. This issue does not restate it.
The live bug this fixes
R/model.R:732-733 asks Make's question:
file.mtime(exe) < file.mtime(self$stan_file())
That is exe versus source, correct only if mtimes move monotonically forward. Extracting a project tarball over an existing build:
source content is now DIFFERENT
exe mtime : 2026-08-27 10:19:38.476
stan mtime: 2026-03-15 09:00:00.000
exe < stan (rebuild?): FALSE <-- no rebuild
cmdstanr reports "up to date" and runs the old binary against the new source. tar -x, unzip, cp -p, rsync -a and backup restores all preserve archival mtimes and all reach this. It is filed here rather than separately because it cannot be fixed before a record exists.
Stages (#1258 holds the full stage lists)
Stage 2: schema and helper tests. Behaviour-free: nothing writes a record beside a user's program until stage 3.
- Parser, writer and comparison helpers tested against fixtures; every required field validated in the parser, not in each caller (#1254 §4, "The record is parsed into an object, and compared as one")
- The executable hash bond, corruption, and an unsupported
format_version(#1254 §4, "Binding the record to its executable"; "Format versions") -
reported_featurestri-state round trip, encoded by key presence (#1254 §1) - Ignore patterns in this repository before anything writes a record beside a test model (#1254 §4, "The record's lifecycle follows the executable's")
Stage 3: transactional record writing.
- Stage executable and record together, install both, verify the pair, roll back both on any failure (#1254 §4, "Binding the record to its executable"). Locking and TOCTOU are out of scope for v1; #1025 is related and open.
- Populate every field in #1254 §4's table, including
request.stanc_name,tbb_dir(from the call'scpp_options, not asked ofmake) andknown_untracked_dependencies(#1257) - Accumulate injected stanc options in their own list rather than merging them into the caller's (#1254 §10)
- Document the lifecycle rule where users will look for it: whatever ignores the executable ignores the record
Measurements
- Hashing the executable: 8.5 ms, against 28.6 ms merely to spawn
<exe> info. - Hashing six realistic source files: 0.31 ms, against a rebuild floor of 6.7 s for
bernoulli.stanwith precompiled headers and 13.8 s without (#1254 §4, "Hashes rather than mtimes").
Also settled
- Absorbs #1247: canonical option spelling is part of the record format, not a separate display fix.
- Executable-only models (
cmdstan_model(exe_file =)) with a valid hash-bound record have known provenance; without one they are explicitly unprovenanced. Never automatically rebuilt (#1254 §7).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with R/model.R:732-733 to understand the mtime-based decision, then read contract #1254 §4 and the staged requirements in this issue. Review the parser, writer, comparison helpers, and transactional executable/record lifecycle already described as complete. Done means the record follows the contract, binds to its executable, and prevents stale-source decisions without breaking the stated exclusions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r
- Domain
- build-system
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100