Uh oh!
There was an error while loading. Please reload this page.
tools: add consolidate.sh, the frozen rule set for the six-repo merge - #78
tools: add consolidate.sh, the frozen rule set for the six-repo merge#78balbasty wants to merge 71 commits into
Conversation
- distance.cpp: dt_l1 called FF_CUDA/FF_CPU::dt_euclidean instead of dt_l1 in both device branches, so the L1 transform was never invoked. - Makefile: object compile rule was missing -fPIC (shared-lib link failure). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Documents the layered DLPack port, the per-module status across layers, the CPU bugs fixed this pass, two CUDA/spline bugs found but not yet verifiable here, the per-module porting pattern, and a task breakdown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Public ff:: sym_matvec[_backward], sym_addmatvec_, sym_submatvec_, sym_solve[_], sym_invert[_] taking DLTensor, dispatching on device to the cpu/cuda libs (cuda guarded by FF_WITH_CUDA). Makefile: add posdef to MODULES (CPU-only build links; the cuda branch is compiled only under FF_WITH_CUDA).
…ration-v5r416 Fix distance L1 dispatch and add -fPIC to shared library builds
…GRATION Public ff:: resample/restriction/spline_coeff dispatching on device to the cpu/cuda libs (cuda guarded by FF_WITH_CUDA). Makefile: add the three modules to MODULES (CPU-only build links). MIGRATION.md: refresh status matrix, record the threadpool multi-module link fix and the module bug fixes, note the cuda host-launcher gap and the resample/restriction/spline_coeff naming.
Surfaced while building the nanobind bindings against fastfields-lib: - distance.cpp/posdef.cpp used 'using namespace FF;' then defined the dispatchers at global scope, so symbols were emitted as ::dt_euclidean / ::sym_matvec instead of ff:: as the headers declare -> ff:: callers got undefined references. Wrap the bodies in FF_NAMESPACE_BEGIN(FF)/END like resize/restrict/splinc. - distance.h defined bound_t/spline_t without the FF_LIB_BOUND_SPLINE_T guard the other headers use -> redefinition when co-included. Add the guard. - Makefile RPATH used $$ORIGIN unquoted, so the recipe shell expanded $ORIGIN to empty (runpath became /../lib). Single-quote it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
pushpull: pull/push/count/grad exposed through cpu-lib/cuda-lib/lib (dim x spline x bound x dtype dispatch); extrapolate de-templated to a runtime arg to keep -O2 build times sane. regularisers: flow_matvec/flow_diag and field_matvec/field_diag for absolute/membrane/bending. Many impl+kernel bug fixes (namespace/lookup/typos, a C++11 function-pointer-NTTP blocker, negative array-bound params under dynamic C, missing includes). CPU tests: pushpull 104, reg_field 272, reg_flow 282; full 8-module lib links and distance/posdef/ resize/restrict/splinc regressions stay green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
…ration-v5r416 Claude/jitfields fastfields migration v5r416
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
…compile in progress
Add a self-contained GitHub Actions workflow (test.yaml) that installs clang and runs `make all`, a build/link check that compiles libfastfields.so and builds + installs libfastfields-cpu.so. There are no standalone tests at this level; correctness is gated by fastfields-cpu-lib. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Add a zensical documentation site describing the public C++ API at the feature level (Distance, Posdef, Resampling, Pushpull, Regularisers), the layered kernels->impl->lib->bindings architecture, and the DLPack/device- dispatch design. Sourced from CLAUDE.md and MIGRATION.md. Includes a Docs GitHub Pages deploy workflow (docs.yaml) and zensical.toml (no mkdocstrings handler; this is C++, not Python). Ignore the zensical build output (site/). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Lead with what the library offers and how to build it; drop the 'you are here' hierarchy diagram and the jitfields backstory from the landing page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
make USE_CUDA=1 builds+installs libfastfields-cuda and links it into libfastfields (default stays CPU-only). pushpull's CUDA path is gated behind FF_CUDA_NO_PUSHPULL so the link resolves while pushpull is out of the cuda-lib MODULES; a CUDA pushpull call falls through to the existing unsupported-device throw. All eight lib modules compile clean in the FF_WITH_CUDA config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
The submodule install copied libfastfields-{cpu,cuda}.so to a relative
PREFIX (../build); since make -C resolves the cpu/cuda symlinks to their
physical dirs, that landed in the wrong tree. The CPU link only worked
because of a stale artifact already in build/lib; the USE_CUDA link then
failed with 'cannot find -lfastfields-cuda'. Pin PREFIX with $(abspath).
Verified: make USE_CUDA=1 links libfastfields.so against both backends with
no unresolved ff::cuda symbols, and the default CPU-only build still links.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6ZCUDA launchers now exist for every module and cuda-lib links 7 modules; the hub builds an optional FF_WITH_CUDA variant. Record the fable correctness fixes and the items tracked as issues; drop the stale 'launchers missing' / 'copy_if_needed under-copy' notes (resolved/verified). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
…#9) The default GITHUB_TOKEN is scoped to one repo, so submodules: recursive fails to clone the private sibling repos ("Repository not found"). Pass an explicit cross-repo token to actions/checkout (used for both the main clone and the recursive submodule fetch). No-op until the CI_SUBMODULE_TOKEN repository secret is provisioned. Part of #8. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
Stale gitlinks (dev tree symlinks cpu/cuda). Point `cpu` at fastfields-cpu-lib@main and `cuda` at fastfields-cuda-lib@main (both now token'd + bumped through to kernels), so the real-submodule CI checkout resolves the current tree. Co-lands with the checkout-token PR #9. Part of #10. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
…path) (#7) * build: make Makefile Windows-portable (clang .dll, no -fPIC/-soname/rpath) Mirror the cpu-lib portability change: OS=Windows_NT detection unified with MINGW*/MSYS into an FF_WINDOWS block that switches SOSUF to dll and clears PICFLAG / SONAME_PREFIX / RPATH (all POSIX-only). The link and object rules reference the variables instead of hard-coding -fPIC and -Wl,-soname, leaving the Linux/macOS/CUDA builds unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z * chore: address review — align vars, rename IS_WINDOWS, extract SONAME_FLAG Mirror the cpu-lib readability fixes: align PICFLAG/SONAME_PREFIX, rename FF_WINDOWS -> IS_WINDOWS, and extract the inline soname $(if ...) into a named SONAME_FLAG variable used in the libfastfields link recipe. No functional change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z --------- Co-authored-by: Claude <noreply@anthropic.com>
#14) Bumps cpu -> fastfields-cpu-lib b2e187c (was f4260b7, long stale). Picks up: - restrict/pushpull grain_size max() LLP64 deduction fix (macOS, #6); - the Windows-portable cpu-lib Makefile (clears -fPIC/-soname on MSVC); - the null-strides DLPack fallback and prior CPU fixes. Part of fastfields-bind-py#6 (unblocks the macOS from-source build; also un-stales the Windows leg's cpu Makefile). Co-authored-by: Claude <noreply@anthropic.com>
The hub dispatched each op on a single operand's device and forwarded every tensor to that backend, so a mixed-device input (e.g. out on CPU, inp on CUDA) hit the wrong backend and read a device pointer as host memory -> segfault/garbage. - Add require_same_device(ref, others...) helper (checks.h): compares device_type AND device_id, variadic, C++11. - Call it at the top of every multi-tensor public entry across all 8 modules (single-tensor ops need no check); optional weight tensors guarded with 'if (weight.data) require_same_device(...)'. - Replace the generic 'unsupported device' with a clear 'built without CUDA support' error when a CUDA tensor is seen but !FF_WITH_CUDA. - tests/test_device_check.cpp: standalone, asserts type/id mismatch throw and matching/single-tensor do not (all pass). Closes#16. Co-authored-by: Claude <noreply@anthropic.com>
…commits (#13) (#20) .gitmodules cpu+cuda URLs git@github.com: -> https://github.com/, and bump cpu -> fastfields-cpu-lib main (cd0a5a5) and cuda -> fastfields-cuda-lib main (2c894ee), both of whose .gitmodules now use HTTPS down the chain. A recursive clone of this repo resolves the entire cpu+cuda tree over public HTTPS (no SSH key). Penultimate layer of the fastfields-lib#13 cascade. Co-authored-by: Claude <noreply@anthropic.com>
Forward the linear-elastic shears (Lamé mu) and div (Lamé lambda) penalty weights from the public flow_matvec/flow_diag entry points to the CPU and CUDA backends, matching the new cpu-lib signature. Doc updated to describe the combined elastic stencil. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Public flow_relax forwarding (sol, hes, grd, penalties, nb_iter) to the CPU or CUDA backend. CPU path builds and is tested via fastfields-cpu-lib. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
…v + flow_relax Points the cpu-lib submodule at cpu-lib/main (5e86aae), which transitively pins the fixed kernels and carries the shears/div + flow_relax exposure. Part of fastfields-lib#25 pin cascade; cuda pin unchanged (cuda-lib landing tracked separately). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
…gration-v5r416 reg_flow: thread shears/div + flow_relax through the DLPack dispatch
Completes the fastfields-lib#25 pin cascade: cuda-lib/main (5a87eec) now carries the shears/div + flow_relax CUDA routing (compile-validated under nvcc). cpu pin already updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
pushpull: device dispatch for pull/push/count/grad_backward
… pins (#47) * reg: expose the in-place accumulate entry points at the hub + bump pins This layer had no accumulate entry point at all, which is precisely why nothing was reachable from Python and the wrappers had to fake it with `inp + field_matvec(...)`. Adds all 12 -- {field,flow}_{matvec,diag,kernel}_{add_,sub_} -- each with the standard CPU/CUDA device_type dispatch (and require_same_device on the two-tensor matvec forms). These mirror the original jitfields C-level `op='+'`/`op='-'` entry points and are in-place only: an out-of-place accumulate is a caller-side clone followed by the same call, not a second kernel. Bumps the cpu/cuda submodule pins to their rebased-onto-main commits (which carry both the BoundVec/runtime-boundary-condition plumbing already on main and this accumulate restoration). MIGRATION.md records what jitfields actually had (Op<op> = set/iadd/isub in csrc/lib/regularisers/{field,flow}/utils.h), the per-layer gap table, and the trailing-underscore naming rationale. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z * reg: expose the in-place accumulate entry points at the hub + bump pins This layer had no accumulate entry point at all, which is precisely why nothing was reachable from Python and the wrappers had to fake it with `inp + field_matvec(...)`. Adds all 12 -- {field,flow}_{matvec,diag,kernel}_{add_,sub_} -- each with the standard CPU/CUDA device_type dispatch (and require_same_device on the two-tensor matvec forms). These mirror the original jitfields C-level `op='+'`/`op='-'` entry points and are in-place only: an out-of-place accumulate is a caller-side clone followed by the same call, not a second kernel. Bumps the cpu/cuda submodule pins to their rebased-onto-main commits (which carry both the BoundVec/runtime-boundary-condition plumbing already on main and this accumulate restoration). MIGRATION.md records what jitfields actually had (Op<op> = set/iadd/isub in csrc/lib/regularisers/{field,flow}/utils.h), the per-layer gap table, and the trailing-underscore naming rationale. Fixes a duplication bug from the previous push on this branch: the earlier cherry-pick's silent "clean" auto-merge (no conflict markers reported) had left the pre-rename field_matvec_add/field_matvec_sub declarations and definitions in place alongside the correctly renamed field_matvec_add_/field_matvec_sub_ ones, breaking the build ("no member named 'field_matvec_add' in namespace 'ff::cpu'"). Rebuilt the diff from the verified-correct pre-reset commit and reapplied it cleanly by hand; every file now has exactly the 16 expected reg entry points, no dupes (cross-checked function-definition counts across cpu-lib, cuda-lib, lib, bind-py, numpy, torch and cupy). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z * fix: restore field_matvec/flow_matvec declarations dropped by header dedupe The previous commit on this branch (9612f60) fixed a duplication bug in reg_field.cpp/reg_flow.cpp/reg_field.h/reg_flow.h left by an earlier bad auto-merge, but the regex used to strip the stale un-renamed field_matvec_add/field_matvec_sub declarations from the two headers was too greedy and also ate the field_matvec/flow_matvec declarations themselves (no doc-comment gap between them for the regex to anchor on), breaking every downstream build with "no member named 'field_matvec' in namespace 'ff'". Rebuilt reg_field.h/reg_flow.h from a clean origin/main baseline by hand: rename field_matvec_add/sub -> _add_/_sub_ in place (no delete+reinsert), then insert the four new field_diag/kernel_add_/sub_ declarations before field_relax. reg_field.cpp/reg_flow.cpp were already correct (they went through function-body-anchored, not doc-comment-anchored, deduplication). Verified: both .cpp files -fsyntax-only clean against the reconstructed headers, and every ff::* call in bind-py's src/ext.cpp resolves to a declaration in these headers (cross-checked programmatically). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z * reg: rename accumulate entry points to verb-first (addmatvec_/adddiag_/...) Repo-owner decision: the C++ and Python surfaces should be consistent with each other and with this codebase's own existing convention, even where that means diverging from jitfields' own (noun-first) naming. {field,flow}_matvec_add_ -> {field,flow}_addmatvec_ {field,flow}_matvec_sub_ -> {field,flow}_submatvec_ {field,flow}_diag_add_ -> {field,flow}_adddiag_ {field,flow}_diag_sub_ -> {field,flow}_subdiag_ {field,flow}_kernel_add_ -> {field,flow}_addkernel_ {field,flow}_kernel_sub_ -> {field,flow}_subkernel_ Extends the verb-first + trailing-underscore convention already used for posdef (sym_addmatvec_ / sym_submatvec_) to reg_field/reg_flow. Only the public ff:: dispatch functions rename; field_matvec/field_diag/field_kernel (plain) and field_relax/field_forward/field_precond{,_}/*_rls (and flow equivalents) are unchanged. MIGRATION.md's naming section is rewritten (not just renamed): it now correctly distinguishes jitfields' own naming (field_matvec_add, kept as historical description) from fastfields' new naming (field_addmatvec), rather than mechanically renaming a passage that was describing jitfields. Bumps cpu/cuda pins to their renamed commits. Verified: make all, EXIT=0, 0 errors, all 12 renamed symbols exported from libfastfields.so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z --------- Co-authored-by: Claude <noreply@anthropic.com>
The six C++/CUDA repos had no formatting, lint or spell-check configuration of any kind, while the five Python repos have run ruff + codespell since they were created (fastfields-lib#18, item D6). Adds, identically across all six C++ repos: * `.clang-format` -- derived from the code that is already here rather than taken off the shelf: 4-space indent (the dominant indent level in every repo), an 80-column limit (the p90 line length is 68-78), `T * p` / `T & r` middle alignment, function braces on their own line with control-flow braces attached, and `CUDEV`/`CUGLOB`/`CUHOST` declared as attribute macros so clang-format stops parsing them as return types. * `.codespellrc` -- the same checks the Python repos run, with the project's domain vocabulary (`nd`, `numer`, `mone`, the `t<name>` DLTensor locals, M. Unser) allow-listed and the vendored `dlpack.h` skipped. * `.github/workflows/lint.yaml` -- codespell (blocking, pinned to the same 2.4.3 the reusable python-lint.yml pins) plus a clang-format check. The clang-format check is scoped to the lines a pull request touches (`git clang-format --diff <merge-base>`) rather than the whole tree. The tree predates the config and is hand-column-aligned in many places, so a whole-tree `--dry-run --Werror` flags essentially every file; gating on that would mean either a permanently red main or a mass reformat bundled into an unrelated change. Diff-scoped checking makes new and modified code conform from now on and leaves the one-shot reformat as its own reviewable PR. No source file is reformatted here. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
…uda pins (#50) A `cudaStream_t` is a pointer and therefore 64-bit, but every public `stream` parameter in the hub's device-dispatch functions was declared `int`. `ff::dt_euclidean` and friends inspect the DLTensor's device and forward the same argument to either `FF_CPU::` or `FF_CUDA::`: if (is_cuda) return FF_CUDA::dt_euclidean(inp_out, voxel_spacing, stream); else return FF_CPU::dt_euclidean(inp_out, voxel_spacing, stream); so both backends must agree on the parameter's type. Widens 114 parameter declarations across the 8 modules to `intptr_t`, matching the now-widened cpu/cuda dispatch layers. This is a signature-only change (verified with the same non-signature-line grep used in the cpu-lib/cuda-lib PRs: empty). Bumps `cpu` -> d969e2d (fastfields-cpu-lib#61) and `cuda` -> 33aa9b3 (fastfields-cuda-lib#35). Verified with `make all CXX=clang++`: the full hub (cpu library + hub object files + link) builds successfully end to end against the bumped pins, producing libfastfields.so linked against libfastfields-cpu.so. This is a real, complete build of the CPU path; the CUDA path is not linked by this target (CUDA needs `USE_CUDA=1` and nvcc, exercised at the cuda-lib layer instead). Refs #4 Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
* ci: cache clang++ builds with ccache CXX="ccache clang++" on the existing make invocation -- the Makefile forwards $(CXX) transparently into both this repo's own object rule and the recursive `cpu` sub-make (a command-line CXX override propagates to sub-makes), so no Makefile change is needed -- plus actions/cache to persist ~/.ccache across runs, keyed on a source hash with a restore-keys fallback. part of fastfields-lib#18 * ci: fix ccache cache dir mismatch (apt ccache defaults to ~/.cache/ccache) apt's ccache (4.x on ubuntu-latest) defaults CCACHE_DIR to ~/.cache/ccache (XDG), not the legacy ~/.ccache actions/cache was pointed at -- a real CI run on the sibling fastfields-cpu-lib PR showed the cache-save step silently no-op with "Path(s) ... do not exist", so nothing would ever persist. Pin CCACHE_DIR explicitly so the two agree. part of fastfields-lib#18 * ci: trigger a second run to verify ccache hit rate Empty commit -- second consecutive CI run on this branch, now that CCACHE_DIR is pinned, to confirm the actions/cache-restored ~/.ccache is actually warm and clang++ compiles hit it. part of fastfields-lib#18 * ci: trigger a clean second run (previous two overlapped and raced on the cache save) The prior two runs on this branch overlapped in time (this workflow has no concurrency/cancel-in-progress group, unlike the sibling repos), so both built cold and raced to save the same cache key -- one lost with "another job may be creating this cache". This commit is spaced out to get an uncontended warm-cache run for comparison. part of fastfields-lib#18 --------- Co-authored-by: Claude <noreply@anthropic.com>
fastfields-cpu-lib#62 (field_matvec_rls's RLS/shared-weight broadcast path reading the wrong stride, corrupting output for wc=1, C>1) was fixed at the cpu-impl and cuda-impl layers and threaded up through cpu-lib#64 and cuda-lib#38. This hub's cpu/cuda pins still pointed at pre-fix commits (cpu: d969e2d, cuda: 33aa9b3), so despite every individual repo's own main looking fixed, the fix was not reachable through this hub's pin chain -- libfastfields.so would still link against the broken field_matvec_rls. Bumps: cpu -> 4d46a02 (fastfields-cpu-lib main HEAD) cuda -> e96440c (fastfields-cuda-lib main HEAD, post cuda-lib#38) Verified with a real `make all CXX=clang++` full build: compiles and links libfastfields-cpu.so (via `make -C cpu install`) and libfastfields.so end to end against the bumped pins. All modules compiled clean (only benign -Wc++20-extensions and -Wpass-failed=transform-warning diagnostics, no errors); confirmed the RLS/JRLS symbols are present in the resulting libfastfields-cpu.so via nm. CUDA path not linked by this target (needs USE_CUDA=1 + nvcc), already verified separately at the cuda-lib layer in cuda-lib#38. part of fastfields-cpu-lib#62 Co-authored-by: Claude <noreply@anthropic.com>
…ib#15) (#53) Adds the consumer-side half of fastfields-lib#15's automation to this repo, which pins two submodules (`cpu` -> fastfields-cpu-lib, `cuda` -> fastfields-cuda-lib) -- the exact layer the incident described in fastfields-lib#15 hit: fastfields-lib#50 captured the `cpu` pin as a raw SHA off an unmerged fastfields-cpu-lib branch before the RLS-broadcast fix (cpu-impl#48, picked up by cpu-lib#64) had actually merged there, and nobody could see that at review time. * submodule-staleness.yaml -- calls fastfields/.github's reusable cpp-submodule-staleness.yml on every push/PR, reporting both pins' staleness as non-blocking ::warning::s + a PR comment. Verified against this repo's own current state: BOTH the `cpu` pin (d969e2d) and the `cuda` pin (33aa9b3) right now reference commits off unmerged PR branches in their respective repos ("fix: widen the ... stream parameter to intptr_t") that were later squash-merged under different SHAs on those repos' main -- both show up as "diverged", not merely "behind" (6 and 3 commits respectively, plus the pin's own unmerged commit). * submodule-bump.yaml -- unlike the single-submodule repos, this one has to pick which pin to bump: a `plan` job resolves the target from either the workflow_dispatch `submodule` choice input (cpu/cuda/all) or the repository_dispatch client_payload.path, then a matrix job calls the reusable cpp-submodule-bump.yml once per selected pin. No notify-parent.yaml here: this repo's only consumer (fastfields-bind-py) is out of scope for fastfields-lib#15's named repo list. Needs fastfields/.github#6 merged first for the `uses: fastfields/.github/...@main` references to resolve. SUBMODULE_DISPATCH_TOKEN still needs to be added as a secret on fastfields-cpu-lib and fastfields-cuda-lib (contents:write on this repo) for their automatic dispatch to fire; see each of their notify-parent.yaml. No submodule pin values touched here. part of fastfields-lib#15, fastfields-lib#10 _Workstream: claude-jitfields-to-fastfields_ Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
) Bumps the cpu pin to fastfields-cpu-lib's main HEAD (b9afce3, includes #73, which flips field_rls_is_jrls() to the correct RLS/JRLS predicate and closes#65), and the cuda pin to fastfields-cuda-lib's main HEAD (b8e5e9c, includes #40, the CUDA-side mirror of the same fix). No source changes in this repo -- gitlink pin bumps only. cpu: 4d46a02 -> b9afce3 cuda: e96440c -> b8e5e9c Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
Picks up fastfields-cpu-lib#82, which bumps fastfields-cpu-impl to #69's fix for #51 (relax_bending_rls_/relax_bending_jrls_ used a 2*niter colour-loop bound instead of pow<ndim>(3)*niter, leaving most of the field unrelaxed for bending-order RLS/JRLS relaxation) and adds a deterministic regression test for it. part of #33 Co-authored-by: Claude <noreply@anthropic.com>
#64) Under `make -j`, the top-level `libfastfields.so` link rule only listed $(OBJECTS) as prerequisites -- its dependency on the cpu (and cuda, when USE_CUDA=1) sub-library was expressed only as a sibling under the recipe-less `lib:` target, not as a real prerequisite of the link rule itself. Make is free to run independent prerequisites of `lib:` in any order/concurrently under -j, so the link step could run before `make -C cpu install` finished producing build/lib/libfastfields-cpu.so, failing with `ld: cannot find -lfastfields-cpu`. Add the cpu .so (and $(CUDA_DEP)) as real prerequisites of the $(BUILDDIR)/libfastfields.$(SOSUF) rule so make -j can't schedule the link before they exist. The recipe now lists $(OBJECTS) explicitly instead of $^, since $^ would otherwise pull the .so prerequisites into the link command's positional arguments. Closes#56 Co-authored-by: Claude <noreply@anthropic.com>
…block (#66) fastfields-cpu-lib#65 fixed `field_rls_is_jrls()`, which had RLS and JRLS exactly backwards: RLS is the genuine per-channel weight (wc == nc) and JRLS ("joint") is the single weight broadcast across all channels (wc == 1), matching jitfields' `joint = 'j' if wgt.shape[-1] == 1 else ''` and nitorch's `membrane_weights(..., joint=True)`, which reduces the weight map over the channel axis to a single channel. The implementation was corrected there (and mirrored on the CUDA dispatch layer in fastfields-cuda-lib#40), but this public header's doc block still carries the pre-fix labelling, so it now states the opposite of what the code does -- and the opposite of the runtime error message the libraries raise on a bad weight shape ("must be 1 (JRLS) or match the channel count (RLS)"). Documentation only: no signature, no behaviour, no generated code changes. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
The hub header still warns that the `bending` order "has a known self-adjointness bug in its varying-weight coefficient math". That was fixed upstream by fastfields-kernels#38 (bending RLS/JRLS cross-term self-adjointness), and the fix is in the tree this repo actually pins: fastfields-lib@main cpu -> fastfields-cpu-lib bf57a02 impl -> fastfields-cpu-impl e97249c kernels -> fastfields-kernels b09b284 $ git merge-base --is-ancestor 5c77586 b09b284 && echo in-tree in-tree fastfields-cpu-lib's own copy of this doc block (one layer down, same operator) was updated at the time and already says all three orders are verified self-adjoint; only the hub was left behind, so the two headers currently contradict each other. cpu-lib's suite backs the corrected wording: run_2d_matvec_rls_symmetry covers order=3 under DCT2/DST2/DFT and passes. Replace the warning with cpu-lib's accurate text, which also keeps the one caveat that is still real (Zero boundary at bending order, tracked as fastfields-kernels#34 finding S1). Documentation only. Found during an independent re-review of the fastfields-cpu-lib#65 fix chain. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
…age (#68) cuda: b8e5e9c -> 6ae52bd (fastfields-cuda-lib#46) Completes the propagation of fastfields-cuda-impl#41, which moved cuda-impl's kernels pin to main after it had fallen nine commits behind. The CUDA path was missing two field/flow regulariser correctness fixes the CPU path already had: fastfields-kernels#64 make_kernel_bending_rls rescaled its whole coefficient table by 0.25, running the membrane penalty at half strength whenever the bending-order RLS/JRLS kernel is built. Upstream measured max |matvec_bending - matvec_bending_rls(w=1)| of 1.8e+00 (2D) / 1.7e+00 (3D) before, ~1e-14 after. fastfields-kernels#52 guards the weight-map neighbour reads in the RLS/JRLS kernels (out-of-bounds read). With this the CPU and CUDA paths finally sit on the same kernels commit (b09b284). cpu: bf57a02 -> 13af367 (fastfields-cpu-lib#83) Adds ground-truth coverage for the wc == 1 (JRLS broadcast) direction, which had none: the #65 predicate bug mis-routed both weight-map layouts, but only wc == nc was regression-tested. Also unswaps the RLS/JRLS labels in cpu-lib's public header and test comments. No source changes here; both sub-libraries' own CI (including cuda-lib's 51-minute nvcc build) is green on the pinned commits. Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
…ent (#70) `spline_coeff` accepted `bound=zero` and returned results bit-identical to `bound=dct1`: the kernels' prefilter only derives initial/final recursion conditions for dct1, dct2/replicate and dft, and everything else falls through to the primary `utils<B>` template, which is the dct1 path. The caller asked for zero-padding and silently got whole-point mirroring, with no diagnostic. Add `ff::require_splinc_bound` in splinc.h, called at the top of the hub's `spline_coeff` before device dispatch -- mirroring `jitfields.splinc`'s `checkbound`. The hub is the single dispatch point every binding goes through, so one check covers the CPU and CUDA backends and numpy/cupy/torch alike; fastfields-torch#29 had mitigated this at the torch entry point only, leaving fastfields.numpy and fastfields.cupy exposed. Orders 0/1 stay exempt (the prefilter is the identity there), as do out-of-range orders, so the backend's "unsupported spline order" remains the error those callers see. Also adds a `make test` target for the standalone header-only tests in tests/ (which previously had no way to run and no CI step) and wires it into the Test workflow, so tests/test_splinc_bound.cpp and the pre-existing tests/test_device_check.cpp actually gate. Fixes#65
`ff::dt_mesh` passed the optional `nearest_vertex` to `require_same_device` alongside the real operands. Callers signal "not wanted" with a null-data placeholder DLTensor whose device fields are zeroed, so every `return_nearest=False` call -- the default on all three Python wrappers -- was rejected with the misleading "all tensors must be on the same device". Guard the check on `nearest_vertex.data`, matching posdef's optional `weight` and what checks.h documents. Fixesfastfields/fastfields#32 (cause 2 of 2).
fastfields-cpu-lib#84 (0f62f52), which carries with it fastfields-cpu-impl#70 (e4afcf0) and fastfields-kernels#78 (be7be08) -- the resolution of fastfields-kernels#75. The user-visible part: `distance_mesh`'s BVH is no longer a raw byte buffer reinterpret_cast to polymorphic `Node`s whose lifetime never began (55 UBSan reports on the cpu-lib mesh suite, 0 now). The node buffer is a real `new Node[2*M]` -- correct, and ~half the memory of the old over-allocation (24 MB vs 46 MB at M=100k). `ff::dt_mesh` on a CPU tensor is the entry point that reaches it; results are unchanged (the UB devirtualised in practice), this makes them defined. Also in the range: cpu-lib's test gate gained an ASan/UBSan job, which is what would have caught the above -- and #74 before it -- without a human running a sanitizer by hand. Bumped range is one cpu-lib commit: 0f62f52 ci: add an ASan/UBSan lane to the test gate (#84) Refs fastfields-kernels#75 Co-authored-by: Claude <noreply@anthropic.com>
Rapid successive pushes on the same branch were racing and overlapping, colliding on the ccache-save step, because test.yaml lacked the concurrency block other C++/CUDA repos in the org already carry (e.g. fastfields-cpu-lib's test.yaml/lint.yaml). Add the same group/cancel-in-progress convention so a newer push cancels a superseded in-flight run instead of racing it. Fixes#54 Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
* solve_field: device dispatch for field_cg (CPU only) + bump cpu pin Hub entry point for the Jacobi-preconditioned conjugate-gradient solve of `(H + L) x = g` landing in cpu-lib -- the first slice of the pure-C++/CUDA solver umbrella (#34). Unlike every other module here, this one has no `FF_CUDA::` branch: the solver's dot products need a device-side reduction that the cuda backend does not expose yet. Rather than let a CUDA tensor fall through to the host path and read device memory as host memory, CUDA tensors get an explicit "not implemented on CUDA yet" error, and the header says so. Wiring the CUDA backend stays open on #34. Also records solve_field in MIGRATION.md's status matrix, including what the module deliberately does *not* have (no kernels-layer code: CG is composed from operators that already exist) and what remains on #34 -- the flow/"grid" flavour, the V-cycle/FMG driver, CUDA, and Python. Refs #34 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z * chore: repoint cpu pin to the merged (squashed) cpu-lib#86 commit The submodule pin was set to the feature branch's pre-squash tip (5ba455b), which no longer exists on cpu-lib after #86 was squash-merged as dbd3274. Repoint to the actual merged commit. --------- Co-authored-by: Claude <noreply@anthropic.com>
…it (#77) Moves cpu dbd3274 -> 1fb2b37 and cuda 6ae52bd -> 2b2ad55, the current mains of fastfields-cpu-lib and fastfields-cuda-lib. This is the top of a bottom-up cascade that closes a live pin skew. Before it, the two backends resolved to different kernels commits: CPU path : lib -> cpu-lib -> cpu-impl -> kernels be7be08 CUDA path: lib -> cuda-lib -> cuda-impl -> kernels b09b284 (three behind) The CUDA side was missing kernels#74 (heap over-read from the past-the-end FaceIterator, a memory-safety fix), #76 (make the host BVH/normal builders visible to nvcc's host pass -- a CUDA-specific fix the CUDA path lacked) and #78 (drop the pointless virtual destructors). After this commit every path resolves to kernels 1df9fd3: lib -> cpu-lib 1fb2b37 -> cpu-impl 5e2c78e -> kernels 1df9fd3 lib -> cuda-lib 2b2ad55 -> cuda-impl 83fa026 -> kernels 1df9fd3 The cascade also carries the diag_bending/diag_all corner cross-term correctness fix (kernels#81) to both backends, its regression test (cpu-lib#89), the reg_flow Lame dispatch routing (cpu-lib#87) and the cuda-impl include guards (cuda-impl#48). Claude-Session: https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z Co-authored-by: Claude <noreply@anthropic.com>
#76) * tools: add a re-runnable test-baseline gate for the repo consolidation The six C++/CUDA repos are about to be consolidated into one, which rewrites git history and relocates every file. The only mechanical proof that such a migration changed nothing is that the test suite produces identical results before and after -- so that evidence needs to be a re-runnable artifact rather than a number recorded once by hand. tools/test-baseline.sh builds fastfields-cpu-lib's suite once per configuration leg and emits a sorted, machine-comparable report: suite <TAB> config <TAB> checks <TAB> failures The legs mirror .github/workflows/test.yaml exactly -- the three-way BOUNDFLAGS/SPLINEFLAGS matrix (static, dynamic, cuda-default) plus the separate ASan+UBSan job -- and add two more: `default` (a bare `make test`, so a change to the Makefile's target-specific defaults shows up rather than silently weakening the gate) and `lib` (fastfields-lib's own two standalone argument-validation tests). tools/test-baseline.expected records the measured result for all six legs at the commits named in its header: 13 cpu-lib suites totalling 53988 checks in each of the five cpu-lib configs, and 14 checks across fastfields-lib's two. Zero failures everywhere, including under the sanitizers. `--check` compares a fresh run against that file and exits non-zero on any failure, any suite that did not run, or any check-count difference, so the migration gate is a single command with a single pass/fail condition. Three details in here are load-bearing and are documented at length in the script rather than left to be rediscovered: * Every leg goes through `make test`. The Makefile sets BOUNDFLAGS and SPLINEFLAGS with target-specific plain `=` assignments on `test:` (they cannot be `?=` -- the global `?=` defaults already count as set at parse time, so a target-specific `?=` would never fire). Building a test binary by its own path does not enter that context and would silently measure the fully-static policy instead of the requested leg. * -DFF_TEST_SPARSE is hard-coded into TESTCPPFLAGS, so it is not a configuration axis -- it is on for every leg here and in CI. * The Makefiles are clang-only by default (CXXFLAGS picks up -ferror-limit and -ftemplate-backtrace-limit), so `make CXX=g++` fails on the flags rather than the source. The script detects a non-clang compiler and replaces CXXFLAGS wholesale, which is what makes --cxx g++ work. Measurement only -- no source, test or Makefile is touched. * tools: re-record the baseline against the aligned main pin chain The first recording in this branch was measured against cpu-lib dbd3274 / kernels be7be08. While it was being taken, three things landed upstream: kernels 1df9fd3 fix(regularisers): correct diag_bending/diag_all corner cross-term cpu-impl 5e2c78e deps: bump kernels pin to that fix cpu-lib 1fb2b37 test(reg): cover the diag_bending/diag_all corner cross-term bug (+ db3c3b5 perf(reg_flow) routing) The old recording was internally consistent -- every leg was measured against one frozen set of clones that was never re-fetched -- but it described a tree that main has since moved past, so it would have failed as a gate for the wrong reason. Re-recorded against the current pin chain, which is now fully aligned (each recorded pin equals the pinned repo's own main tip). The only rows that move are reg_field and reg_flow, in all five cpu-lib legs, which is what the new corner cross-term coverage should do and nothing else: reg_field 18284 -> 19250 (+966) reg_flow 11415 -> 16347 (+4932) per leg 53988 -> 59886 (+5898) The other eleven suites are unchanged to the check, and all five legs still agree with each other exactly. Still zero failures everywhere, including under ASan+UBSan. The header now also records that the pin chain is aligned, and why that is worth stating: when a pin lags its repo's main (as the kernels pin did for the first recording), "clone main and follow the pins" and "check out main everywhere" are different trees, and a baseline has to say which one it means. * tools: report a leg-set mismatch plainly instead of as a huge diff --check compares whole reports, so running a subset of the recorded legs diffed as 'every recorded row vanished' -- which reads like catastrophic breakage rather than the operator error it is. Compare the config sets first and name both sides.
Takes six pristine clones and produces the finished consolidated tree deterministically (~8s end to end), so a lost container costs minutes rather than a day. The rules are frozen on purpose and the file says so in capitals at the top: git filter-repo runs exactly ONCE per source repo, over main and teeny together in a single pass. Identical rules applied to both refs rewrite their shared ancestors to identical SHAs, so the merge-base survives and teeny stays mergeable; changing one path rule between runs produces zero shared commits and an empty merge-base, which makes teeny permanently un-mergeable. Rule drift is the fatal failure mode, not branch structure -- so stage 2 asserts every repo still has a main/teeny merge-base and refuses to continue if one is gone. Stage 2 does path moves only. Content edits (core/ dedupe, include rewrite, build system) are ordinary commits on main in stages 4-6, because they could not be replayed identically on teeny, whose content differs. The six repos' output path sets are disjoint by construction, so stage 3's five --allow-unrelated-histories merges cannot conflict.
Caught by building twice, not by inspection. Writing the lib-cuda dependency rule as `-Xcompiler -MMD -Xcompiler -MP` is wrong: nvcc does not hand the host compiler the original .cpp, it hands it a generated /tmp/tmpxft_*.cudafe1.cpp, so the host-produced .d names that temporary as the prerequisite. nvcc deletes it on exit, so the FIRST build succeeds and the SECOND dies with No rule to make target '/tmp/tmpxft_000044bc_00000000-6_distance.cudafe1.cpp', needed by 'build/obj/lib-cuda/distance.o' nvcc's own -MMD resolves dependencies against the real source and its headers. There is no nvcc equivalent of -MP, so a deleted header still requires clearing the stale .d by hand; that is noted in the makefile. lib-cpu is compiled by the host compiler directly and keeps -MMD -MP unchanged.
The ref-prune before `git filter-repo` deletes every ref that is not main or teeny. When the source clone carries *nothing but* those two -- which is the normal case for a clone staged specifically for this script -- `grep -v` matches no lines and exits 1. The script runs under `set -euo pipefail`, so that exit status propagates out of the pipeline and aborts the whole run at the first repository, before anything is rewritten: === stage 2: path rewrite (one filter-repo pass per repo, --refs main teeny) fastfields-lib $ echo $? 1 with no diagnostic, because filter-repo's stdout is sent to /dev/null. `PIPESTATUS` for the pipeline is `0 1 0` -- git succeeded, grep found nothing to delete, the delete loop ran cleanly. Deleting no refs is the correct outcome, not an error, so the grep is wrapped to swallow the no-match status. This is plumbing, NOT a path rule: no filename callback, no --refs argument and no rule ordering is touched, and the prune still deletes exactly the same refs it did before (here, none). Re-verified after the change that the rewrite is still deterministic -- two runs produce identical SHAs for all 12 rewritten refs and an identical consolidated tree hash -- since that property is what keeps teeny mergeable and the whole migration rests on it. Also records, in the header, the three failure modes that building `teeny` on top of this surfaced: silently hybridised files from *clean* auto-merges, the way `git checkout --theirs` resurrects deleted files on modify/delete conflicts, and include-guard collisions that only become reachable once the six repos share one -I.
balbasty
commented
Aug 19, 2026
Closing as superseded — the base branch this PR targets no longer exists.
The script itself is not lost. It was deliberately not merged here first. This branch's script is what built the consolidated tree; merging it and rebuilding would have changed The pre-rewrite state remains reachable at the branch Generated by Claude Code |
Phase 1 of the six-repo consolidation. This PR adds only the re-runnable rule set — no consolidated history is merged anywhere by it.
tools/consolidate.shtakes six pristine clones and produces the finished consolidated tree in about 8 seconds, so if a container is reset the work costs minutes rather than a day. (That contingency was not hypothetical — see "Phase 2 results" below.)The frozen rule
The file carries the warning in capitals at the top.
git filter-reporuns exactly once per source repo, overmainandteenytogether in a single pass (--refs main teeny).Identical rules applied to both refs rewrite their shared ancestors to identical SHAs, so the merge-base survives and
teenystays mergeable. Changing a single path rule between runs produces zero shared commits and an empty merge-base —teenypermanently un-mergeable. Rule drift is the fatal failure mode, not branch structure.Stage 2 therefore asserts, per repo, that a
main/teenymerge-base still exists after the rewrite, and refuses to continue if one is gone:Structure
mains (--allow-unrelated-histories)core/dedupeStage 2 is deliberately path-moves-only. Content edits live in stages 4–6 as ordinary commits on
main, because they could not be replayed identically onteeny, whose content differs. Phase 2 does theteenyside.The six repos' output path sets are disjoint by construction, so stage 3's five merges cannot conflict — that is what lets the duplicate
dlpack.h/autocast.h/defines.hcopies arrive intact and be deduplicated in a visible, reviewable commit in stage 4 rather than resolved as a merge conflict.Notes on stages 4–6
core/dlpack.h— three byte-identical copies collapse to one; the upstreamDLPACK_DLPACK_H_guard is kept verbatim and no#pragma onceis added, so a consumer that also has a system dlpack can include both harmlessly. The script asserts both.core/defines.h— the two collidingdefines.hmerge into one file with one guard. This is what makes thecuda_switch.hhazard impossible by construction: it ended in a bare#include "defines.h"that resolved to whichever copy was nearest, and with both on the-Ipath that choice becomes silent and wrong (FF_DEVICEundefined,FF_CPU/FF_CUDAleaking into kernel namespaces — a miscompile, not an error). The include is also made fully qualified.core/autocast.h— not a duplicate pair. The CUDA copy stages through pinned host memory (cudaMallocHost/cudaFreeHost) for async H2D copies; the CPU copy usesnew[]/delete[]. That is the only functional difference between them, so this is a refactor to one header with the host allocator injected behindFF_AUTOCAST_PINNED_HOST— not two files under tidier names.cuda_switch.hrules must fire before the"impl/kernels/and"kernels/prefix rules, and"impl/kernels/before"impl/."impl/resolves toimpl/cpu/orimpl/cuda/depending on which library is including it — the one thing the old symlink layout encoded implicitly. Includes that kept same-directory adjacency are deliberately left alone. The script also checks for include-guard collisions once the tree is flattened.make/common.mkplus four makefiles.DIAGFLAGSis compiler-detected (clang-ferror-limit=1, gcc-fmax-errors=1, both-ftemplate-backtrace-limit=0), somake CXX=g++works natively for the first time.src/lib/andsrc/lib-cuda/gain-MMD -MP. The Windows block is stated once instead of three times.build/andbuild/lib/output paths,allas default target, theBOUNDFLAGS/SPLINEFLAGS/FF_TEST_SPARSEsemantics (including the target-specific plain-=assignments and the comment explaining why they cannot be?=), and the split CUDAMODULESlist are all preserved.Fix in this PR: don't abort when a clone has only
mainandteenyThe ref-prune before
git filter-repodeletes every ref that is notmainorteeny. When the source clone carries nothing but those two — the normal case for a clone staged for this script —grep -vmatches no lines and exits 1. Underset -euo pipefailthat aborts the entire run at the first repository, before anything is rewritten, and with no diagnostic because filter-repo's stdout goes to/dev/null:PIPESTATUSis0 1 0— git succeeded, grep found nothing to delete, the delete loop ran cleanly. Deleting no refs is the correct outcome, so the grep now swallows the no-match status.No path rule was touched. No filename callback, no
--refsargument and no rule ordering changed, and the prune still deletes exactly the refs it did before. Determinism was re-verified after the fix, since that is the property the whole migration rests on: two runs produce identical SHAs for all 12 rewritten refs and an identical consolidated tree hash (828c9164…).Phase 2 results (consolidated
teeny)Built with this script, unmodified apart from the fix above.
merge-base(main, teeny)== consolidatedmaintip exactly (d4f90e68…), 159 ahead / 0 behind —teenyis a strict descendant, one merge base, no criss-cross.teeny's suite: 12 suites, 33,534 checks, 0 failures, byte-identical before and after consolidation. It was measured atteeny's own HEAD first, so the comparison is against a branch known to be healthy.mainuntouched: tree hash unchanged and the gate re-run givesBASELINE MATCH: 67 rows.external/teeny(submodule, pinned5c46bd4) exists onteenyonly;mainhas noexternal/and no.gitmodules.Both consolidated histories are preserved on
claude/consolidated-mainandclaude/consolidated-teeny— backup branches, not a publication step, and trivially deletable.Three hazards Phase 2 surfaced
Recorded in the script's header so they travel with it. Anyone repeating this operation should expect all three.
main's divergence intoteeny's files wherever the two touched different regions — producing hybrids neither branch ever compiled, with no conflict and no warning. Concretely, kernels'spline.hcame out with main's runtimeSplineVecwork spliced into teeny's copy, which teeny'spushpull/utils.hcannot compile against. Re-checking-out every path the owning branch owns, then re-running the stage 4 dedupe and stage 5 include rewrite, is mandatory, not a tidy-up.git checkout --theirssilently keeps our copy on modify/delete.teenydeletespushpull/{2d,3d,nd}.handregularisers/{field,flow}/{1,2,3}d.hwhilemainmodifies them; with no "theirs" blob,--theirsfails and leaves main's content in the tree, which a followinggit addstages — resurrecting nine deleted files. Concrete evidence for the caveat that resolving with--theirsanswers the ancestry question and says nothing about correctness.-Idoes not — the second header silently expands to nothing.teeny'sapi/cpu/reg_dispatch.handapi/cuda/reg_dispatch.hboth usedFF_REG_DISPATCH_H. Stage 5's guard check is what catches this, and it is a hard failure on purpose.🤖 Generated with Claude Code
https://claude.ai/code/session_016AjQcY78NgbagPSbPJRr6Z
Generated by Claude Code