Skip to content

feat: shared static_files across cases (never templated, symlinked not duplicated) - #79

Merged
yannrichet merged 4 commits into
mainfrom
feat/static-files
Jul 29, 2026
Merged

feat: shared static_files across cases (never templated, symlinked not duplicated)#79
yannrichet merged 4 commits into
mainfrom
feat/static-files

Conversation

@yannrichet

Copy link
Copy Markdown
Member

Summary

Models can now declare "static_files": files identical across every case (e.g. a shared weather CSV or a large reference dataset) that are never templated/substituted, never re-hashed per case, and (for relative paths) not duplicated on disk per case.

  • Absolute path entries are assumed already present at that same path on the calculator side too (shared/mounted storage); fz never copies, symlinks, or transfers them - only hashes them once per fzr()/fzd() call, so cache:// still reacts if the shared file's content changes.
  • Relative path entries are resolved against the cwd fzr()/fzd() was called from, identified by basename (not the full declared path, which may contain .. to reach outside input_path and would otherwise escape the case directory as a symlink name), symlinked into every case's result/temp directory (falling back to a real copy if the platform disallows symlinks, e.g. Windows without developer mode/admin), and explicitly transferred to ssh://, slurm:// (remote), and funz:// calculators via their real source path, since they live outside input_path and the generic per-case file transfer never finds them.
  • fzi() excludes them from variable discovery; .fz_hash always includes them (hashed once, memoized) so cache matching stays correct.

Also fixes tests/test_funz_udp_fallback.py's mock to accept the new static_entries kwarg threaded through run_single_case_calculation.

Docs updated: README.md, doc/model-definition.md (full write-up), doc/INDEX.md, skills/fz/reference.md, NEWS.md.

Test plan

  • New tests/test_static_files.py (8 tests, sh://): relative symlinking + basename hashing, absolute entries hashed-but-not-transferred, both readable by a calculator script, fzi exclusion, cache invalidation on shared-file content change, missing-entry warning (non-fatal), invalid-type validation, name-collision handling.
  • New tests/test_static_files_ssh.py: real SFTP transfer of a relative static_files entry over ssh:// to localhost (exercises the actual remote-transfer code path, not just same-filesystem symlink resolution); wired into .github/workflows/ssh-localhost.yml. Also runs automatically in the main ci.yml Linux matrix (not excluded, marked requires_ssh/requires_paramiko like existing SSH tests).
  • New regression step in .github/workflows/slurm-localhost.yml for static_files with local SLURM.
  • Full local suite (matching CI's ignore list for tests needing local SSH/funz servers, per this repo's convention): 858 passed, 0 failed, 82 skipped.
  • Manually verified end-to-end: relative + absolute static_files both readable by a calculator script; .fz_hash correctly records both; cache correctly invalidates when the shared file's content changes; fzi doesn't pick up spurious variables from static files; no-symlink-privilege fallback path.

🤖 Generated with Claude Code

yannrichet-asnr and others added 4 commits July 29, 2026 18:22
…t duplicated)

Models can declare "static_files": files identical across every case (a
shared weather CSV, a large reference dataset) that are never
templated/substituted, never re-hashed per case, and (for relative paths)
not duplicated on disk per case.

- Absolute path entries are assumed already present at that same path on
  the calculator side too; fz never copies/symlinks/transfers them, only
  hashes them once per fzr()/fzd() call so cache:// still reacts if the
  shared file's content changes.
- Relative path entries are resolved against cwd at call time, identified
  by basename (not the full declared path, which may contain ".." to reach
  outside input_path and would otherwise escape the case directory as a
  symlink name), symlinked into every case's result/temp directory
  (falling back to a real copy if the platform disallows symlinks), and
  explicitly transferred to ssh://, slurm:// (remote), and funz://
  calculators via their real source path, since they live outside
  input_path and the generic per-case file transfer never finds them.
- fzi() excludes them from variable discovery; .fz_hash always includes
  them so cache matching stays correct.

Also fixes tests/test_funz_udp_fallback.py's mock to accept the new
static_entries kwarg threaded through run_single_case_calculation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- test_static_files.py::test_static_files_command_can_read_both_kinds
  embedded a Windows backslash path directly into a bash script; bash
  reads backslashes as escapes, corrupting the path. Use .as_posix().
- test_static_files_ssh.py needs a real SSH server + key-based auth,
  which ci.yml only sets up on Linux (like the existing test_ssh_*.py
  files); add it to the Windows/macOS ignore lists. It still runs on
  Linux (both in ci.yml's matrix and the dedicated ssh-localhost.yml).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…_static argument

Static file declarations no longer live in the model dict - the model
doesn't need this data, since it's about how a run/compile/design is
invoked, not about the model definition itself.

- fzr(), fzc(), fzi(), fzd() all gain an input_static parameter (list of
  paths); fzd() passes it through unchanged to each iteration's internal
  fzr() call.
- CLI: new --input_static flag (repeatable, or an inline JSON list) on
  fzi, fzc, fzr, fzd and the unified fz input/compile/run/design subcommands.
- helpers.resolve_static_files()/resolve_static_file_paths() now take the
  raw path list directly instead of reading model["static_files"]; model
  validation for the old field is removed in favor of a new
  _validate_input_static() used by the four public functions.
- Docs moved from doc/model-definition.md (no longer a model concern) to
  doc/core-functions.md, with README/skills/reference.md/NEWS.md updated
  to match. Semantics (absolute vs relative resolution, symlinking,
  explicit remote transfer, hashing, fzi exclusion) are unchanged.
- Tests and the slurm-localhost.yml CI step updated to pass input_static
  as an fzr() argument instead of a model key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…_static

fzr() now logs a one-time warning (per file, not per case) when a file
under input_path contains no variables and is at least
FZ_STATIC_CANDIDATE_MIN_SIZE bytes (default 1 MiB, new config option),
suggesting it be passed via input_static instead - such a file is
otherwise re-read/re-copied and re-hashed on every case for no benefit.
Set FZ_STATIC_CANDIDATE_MIN_SIZE=0 to disable.

Detection: for text files, compares content before/after variable
substitution and formula evaluation (unchanged => no variables); binary
files are inherently variable-free. Deduplicated by resolved source path
so the warning fires once per fzr() call, not once per case.

New tests/test_static_files_warning.py (4 tests). Docs updated: README,
doc/core-functions.md, skills/fz/reference.md, NEWS.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@yannrichet
yannrichet merged commit 1594332 into main Jul 29, 2026
36 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants