Skip to content

tools: add consolidate.sh, the frozen rule set for the six-repo merge - #78

Closed
balbasty wants to merge 71 commits into
mainfrom
claude/consolidate-script
Closed

tools: add consolidate.sh, the frozen rule set for the six-repo merge#78
balbasty wants to merge 71 commits into
mainfrom
claude/consolidate-script

Conversation

@balbasty

@balbastybalbasty commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

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.sh takes 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-repo runs exactly once per source repo, over main and teeny together 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 teeny stays mergeable. Changing a single path rule between runs produces zero shared commits and an empty merge-base — teeny permanently un-mergeable. Rule drift is the fatal failure mode, not branch structure.

Stage 2 therefore asserts, per repo, that a main/teeny merge-base still exists after the rewrite, and refuses to continue if one is gone:

ok fastfields-lib pre=b59a436a post=f7f8c262
ok fastfields-cpu-lib pre=67149398 post=52cf1f1c
ok fastfields-cuda-lib pre=2c894eee post=c882b586
ok fastfields-cpu-impl pre=fbe7e096 post=c8ab28b8
ok fastfields-cuda-impl pre=4ec198c1 post=03e8b558
ok fastfields-kernels pre=dc9f7f18 post=676b9925

Structure

stagewhat
1pristine clones
2path moves only, one filter-repo pass per repo over both refs
3merge the six rewritten mains (--allow-unrelated-histories)
4core/ dedupe
5include rewrite
6build system

Stage 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 on teeny, whose content differs. Phase 2 does the teeny side.

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.h copies 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 upstream DLPACK_DLPACK_H_ guard is kept verbatim and no #pragma once is added, so a consumer that also has a system dlpack can include both harmlessly. The script asserts both.
  • core/defines.h — the two colliding defines.h merge into one file with one guard. This is what makes the cuda_switch.h hazard impossible by construction: it ended in a bare #include "defines.h" that resolved to whichever copy was nearest, and with both on the -I path that choice becomes silent and wrong (FF_DEVICE undefined, FF_CPU/FF_CUDA leaking into kernel namespaces — a miscompile, not an error). The include is also made fully qualified.
  • core/autocast.hnot a duplicate pair. The CUDA copy stages through pinned host memory (cudaMallocHost/cudaFreeHost) for async H2D copies; the CPU copy uses new[]/delete[]. That is the only functional difference between them, so this is a refactor to one header with the host allocator injected behind FF_AUTOCAST_PINNED_HOST — not two files under tidier names.
  • Include rewrite is longest-prefix-first, and the ordering is load-bearing twice: the cuda_switch.h rules must fire before the "impl/kernels/ and "kernels/ prefix rules, and "impl/kernels/ before "impl/. "impl/ resolves to impl/cpu/ or impl/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.
  • Build systemmake/common.mk plus four makefiles. DIAGFLAGS is compiler-detected (clang -ferror-limit=1, gcc -fmax-errors=1, both -ftemplate-backtrace-limit=0), so make CXX=g++ works natively for the first time. src/lib/ and src/lib-cuda/ gain -MMD -MP. The Windows block is stated once instead of three times. build/ and build/lib/ output paths, all as default target, the BOUNDFLAGS/SPLINEFLAGS/FF_TEST_SPARSE semantics (including the target-specific plain-= assignments and the comment explaining why they cannot be ?=), and the split CUDA MODULES list are all preserved.

Fix in this PR: don't abort when a clone has only main and teeny

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 — the normal case for a clone staged for this script — grep -v matches no lines and exits 1. Under set -euo pipefail that 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:

=== stage 2: path rewrite (one filter-repo pass per repo, --refs main teeny)
fastfields-lib
$ echo $?
1

PIPESTATUS is 0 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 --refs argument 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) == consolidated main tip exactly (d4f90e68…), 159 ahead / 0 behindteeny is 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 at teeny's own HEAD first, so the comparison is against a branch known to be healthy.
  • main untouched: tree hash unchanged and the gate re-run gives BASELINE MATCH: 67 rows.
  • external/teeny (submodule, pinned 5c46bd4) exists on teeny only; main has no external/ and no .gitmodules.

Both consolidated histories are preserved on claude/consolidated-main and claude/consolidated-teenybackup 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.

  1. The clean merges are more dangerous than the conflicts. Each per-repo merge is three-way with the fork point as base, so git silently auto-merges main's divergence into teeny's files wherever the two touched different regions — producing hybrids neither branch ever compiled, with no conflict and no warning. Concretely, kernels' spline.h came out with main's runtime SplineVec work spliced into teeny's copy, which teeny's pushpull/utils.h cannot 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.
  2. git checkout --theirs silently keeps our copy on modify/delete.teeny deletes pushpull/{2d,3d,nd}.h and regularisers/{field,flow}/{1,2,3}d.h while main modifies them; with no "theirs" blob, --theirs fails and leaves main's content in the tree, which a following git add stages — resurrecting nine deleted files. Concrete evidence for the caveat that resolving with --theirs answers the ancestry question and says nothing about correctness.
  3. Include guards that were unique per-repo can collide here. Six repos with six include paths tolerate duplicate guards; one tree behind one -I does not — the second header silently expands to nothing. teeny's api/cpu/reg_dispatch.h and api/cuda/reg_dispatch.h both used FF_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

balbastyand others added 30 commits October 13, 2025 15:40
- 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
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
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_016AjQcY78NgbagPSbPJRr6Z
CUDA 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
balbastyand others added 22 commits August 1, 2026 02:02
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.
@github-actions

Copy link
Copy Markdown

Submodule staleness (non-blocking -- fastfields-lib#15)

submodulepinned committracksstatusbehind by
cpu1fb2b37fastfields/fastfields-cpu-lib@mainup to date0
cuda2b2ad55fastfields/fastfields-cuda-lib@mainup to date0

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.
@balbastyClaude

Copy link
Copy Markdown
CollaboratorAuthor

Closing as superseded — the base branch this PR targets no longer exists.

main has been rewritten to the consolidated six-repo tree (d4f90e6). This PR's base was the pre-consolidation main (85f987e), whose history is disjoint from the new one, so this PR is no longer mergeable in principle — not merely conflicted.

The script itself is not lost. tools/consolidate.sh is restored onto the new main in #79, byte-identical to this branch's head c55e791 (blob 4255943, mode 100755), hazards header included.

It was deliberately not merged here first. This branch's script is what built the consolidated tree; merging it and rebuilding would have changed claude/consolidated-main's SHA, and the 159 irreproducible teeny commits sit directly on top of that SHA. Restoring the file as an ordinary commit on the rewritten main avoids rebasing work that cannot be regenerated.

The pre-rewrite state remains reachable at the branch pre-consolidation-main (85f987e) if this PR's original context is ever needed.


Generated by Claude Code

Sign up for freeto 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

@balbasty@claude