chore(pre-push): make hook + CLAUDE.md instructions worktree-compatible#176
Conversation
A linked git worktree's `.git` is a file (gitdir pointer), not a directory, so the previous hook's literal `.git/REVIEW_OK_<sha>` and `.git/REVIEW_OVERRIDES.log` paths fail to resolve under bash path traversal. Any push from a worktree is forced through the SKIP_PREPUSH_REVIEW escape hatch even when the reviewer has created a valid marker. Compute `git_common_dir=$(cd "$(git rev-parse --git-common-dir)" && pwd)` once at top of the hook and route both the marker check and the override log through it. The cd+pwd round-trip normalises both the main-checkout case (where rev-parse returns the literal ".git") and the worktree case (where it returns an absolute path), so the error message always prints a copy-pasteable absolute path. CLAUDE.md §Pre-push gate's documented `touch` command is updated to the same pattern so the reviewer's workflow matches what the hook actually checks. Verified locally in both the main checkout and a linked worktree under ~/.config/superpowers/worktrees/task/: refuse-without-marker (exit 1), accept-with-marker (exit 0), and SKIP escape hatch (exit 0 + logged) all behave correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 210c90b. Configure here.
…al manuals
Restructure docs/sphinx/ from a flat en/ja layout to a hybrid Sphinx
portal + per-module project layout. The portal under portal/{en,ja}/
hosts a card-based landing page; each of the seven modules (tr / eq /
ti / fp / wr / wrx / tot) lives under modules/<mod>/{en,ja}/ as an
independent Sphinx project with its own conf.py, intersphinx mapping
to the portal, and a 14-page split (build / hello-world / parameters /
parameter-setting / state / context-manager / faq / quickstart /
api-reference / design / mcp / testing + appendix-sensitivity, plus
appendix-mdlkai for tr).
Theme switched from sphinx_rtd_theme to furo so the right sidebar
renders an automatic "On this page" TOC; a shared CSS overlay
(_shared_static/toc_h2_only.css) hides H3+ to keep that sidebar
focused on the four-group page index. MyST extensions dollarmath /
amsmath are now enabled so $...$ and \$\$...\$\$ render as math.
Each module documents its full registered parameter set, the C ABI
function count and routing, the 4-layer test suite (with concrete
1e-10 equivalence procedures and required xfail discipline), the MCP
server tool list and registration steps for Claude Desktop / Code /
Cursor, and a sensitivity appendix scoped to deterministic and
physics-scaling input-output relationships. Cross-module references
use plain text paths (each module is its own Sphinx project so
{ref}<mod/foo> would emit undefined-label warnings).
LaTeX/PDF infrastructure:
- conf_common.py uses file-name (DejaVuSerif.ttf etc.) lookups
instead of fontconfig names so xelatex on macOS finds the
TeX-Live-bundled fonts (otherwise nullfont fallback hides all
glyphs in the rendered PDF).
- top_of_page_buttons drops "edit" since html_context isn't wired up
(would warn under -W otherwise).
- Makefile gains per-module + per-language pdf targets generated by a
PDF_RULE macro; xelatex exit codes are tolerated (warnings are
routine) but the .xdv-existence check makes real failures explicit
and per-pass logs are kept for diagnosis.
- Portal intersphinx_mapping now lists all seven modules instead of
just tr+eq.
Adds .claude/skills/sphinx-module-manual.md so future agents can
reproduce the page split, naming conventions, and required sections
when filling new modules or syncing language trees.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…bootstrap
The seven module Makefiles (tr/eq/ti/fp/wr/wrx/tot) embedded GNU-ld-only
flags (`-Wl,-soname`, `-Wl,--start-group`/`--end-group`,
`-Wl,--unresolved-symbols=ignore-all`, `-Wl,-rpath,'$ORIGIN/...'`) on
the .so link lines, so Apple ld rejected them and the .so step always
failed on macOS. The static archive (.a) and CLI binary paths were
never affected — only the Phase L-4 shared-library targets used by
the Python wrappers via ctypes.
Each Makefile now picks ld flags based on `uname -s`:
- `-soname` → `-install_name,@rpath/...` on Darwin
- `--start-group / --end-group` → empty on Darwin (Apple ld resolves
cycles automatically)
- `--unresolved-symbols=ignore-all` → `-undefined,dynamic_lookup`
- `$ORIGIN` (tot rpath) → `@loader_path`
Apple's dlopen does eager symbol resolution rather than lazy binding,
so any unresolved imports cause dlopen to fail at load time. Linux's
ld is more permissive in shared-library mode and lets unresolved
symbols be deferred to runtime. To match that behaviour, each
graphics-stubs file (`tr/tr_graphics_stubs.f90` and friends) now
exposes no-op stand-ins for the symbols that the .so happy path never
actually reaches but Apple's loader still demands resolved:
- tr : VIEWRTLIST, VIEWGTLIST, GETKRT, GETKGT, MOVE2D, DRAW2D,
EQGOUT, grd2d_mod
- ti : MOVE/TEXT/NUMBD, MOVE2D, DRAW2D, EQGOUT, grd1d_mod, grd2d_mod
- fp : MOVE2D, DRAW2D, EQGOUT, grd1d_mod, grd2d_mod
- tot : NUMBD
Verified: all seven .so files build clean on macOS arm64; dlopen
succeeds for every module; tr advances 3 steps and returns the
expected scalars (T=0.030, BETAN=0.3226).
`scripts/setup.sh` is a one-shot bootstrap that mirrors the CI
workflow's clone + patch + PIC-build chain so a fresh macOS or
Linux developer can run `scripts/setup.sh` once and end up with all
seven .so files. The script reuses the existing
`scripts/apply-bpsd-patches.sh` for the patch step but checks the
log for genuine patch-step failures (rather than swallowing them
together with the expected "first-run .so build failed" tail).
`.gitignore` now excludes `/bpsd/` and `/gsaf/` so an in-tree clone
of either external dependency cannot be vendored accidentally.
Documentation:
- tr/ja/applications.md (new): three Python wrapper patterns
(StableTrRunner / sweep() / auto_setup()) that build on top of
Trlib for production-style usage. Explicitly scoped to
library-level patterns; LLM/MCP scenarios moved to mcp.md.
- tr/ja/mcp.md: new "使用シナリオ" section with two LLM dialogue
examples (parameter sweep, validate-driven auto-fix) and a
cross-link to applications.md. `jsonc` Pygments lexer corrected
to `json`.
- tr/ja/index.md: applications added to the User-guide toctree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`Eq()` with default config (`MODELG=2`, analytic toroidal geometry) had
no usable run path: `eq.run()` defaulted to `mode=1` which calls
`equnit::eq_load`, but `eq_load` only handles `MODELG ∈ {3, 5, 8}`
(file-based geometries) and rejects `MODELG=2` with `UNKNOWN MODELG`.
The doc-noted `mode=0` slot was reserved as
`EqlibNotImplementedError`, so callers had to explicitly bump
`MODELG=3` and supply a `KNAMEQ` file just to get past `init`.
The legacy `eqx2` CLI exposed two separate run paths:
- `R` (Run) → `EQCALC` — analytic G-S solve (used with MODELG=2)
- `L` (Load) → `EQ_READ` + `EQCALQ` — EQDSK file load (used with
MODELG=3/5/8)
The Phase L-3 C ABI mapped only the `L` path to `mode=1`. This commit
maps the `R` path to `mode=0`, restoring functional parity with the
CLI:
eq_api_run(mode=0): EQCALC + EQCALQ (analytic + post-process)
eq_api_run(mode=1): equnit::eq_load (unchanged)
EQCALQ is added on top of EQCALC because EQCALC alone solves the
fixed-boundary GS equation but does not run the flux-surface average
post-processing that fills `qaxis` / `qsurf` / `betat` / `betap` /
`pvol` (eqcalq.f90's pipeline). The CLI achieves the same effect via
sequenced `R` then `F` menu commands; on the .so the two-step is
fused so `eq.run(mode=0)` produces a complete, ready-to-inspect
state.
Verified on macOS arm64:
Eq().set_params(RR=3.0, RA=1.0, BB=3.0, RIP=1.5)
Eq().run(mode=0)
state.scalars =>
raxis=3.0709, qaxis=0.9918, qsurf=3.7668,
betat=8.47e-05, betap=0.0085, pvol=59.16,
npsmax=21, nrgmax=33
`mode=1` (EQDSK path) is unchanged — verified via the existing
`MODELG=2 + mode=1` rejection path.
Also adds `EQGOUT` and `grd2d_mod` no-op stubs to
`eq_graphics_stubs.f90` so libeqapi.so dlopens on macOS (Apple's
eager symbol resolution catches what Linux's lazy binding ignored).
Documentation updates (eq/ja):
- hello-world.md: split into two minimal examples (mode=0 analytic
and mode=1 EQDSK), with a flowchart for picking the right mode.
- faq.md (Q2): mode table mapping to legacy CLI commands; mode=0
is no longer "reserved / not implemented".
- design.md: `eq_run` table updated to describe both mode=0 and
mode=1 paths and the `EQCALC + EQCALQ` chain.
- parameter-setting.md: validate snippet now shows both mode=0
and mode=1 follow-ups.
- index.md: overview rewritten to mention both run paths up front;
suggested-reading-order entry for hello-world reflects the dual
coverage.
- mcp.md / context-manager.md: drop dead `{ref}` cross-module
syntax (was emitting `undefined label` warnings) and switch
`jsonc` → `json` Pygments lexer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The auto_setup() example in tr/ja/applications.md used `RIP=15.0` for
ITER preset, which fails with `TrlibParamError: tr_set_param('RIP'):
ierr=1` because the tr registry exposes the two-point ramp
(RIPS = start current, RIPE = end current) but no aggregate `RIP`
symbol. Verified via running the example: the original `auto_setup()`
crashed at the very first `set_params` call.
Fixed all three presets (ITER / JET / DIIID) to specify
`RIPS = RIPE = <Ip>` for steady-state. Verified all three now run
to completion:
ITER : BETAN=0.074, TAUE1=0.738, WPT=1.46e+01
JET : BETAN=0.200, TAUE1=0.473, WPT=1.52e+00
DIIID : BETAN=0.311, TAUE1=0.212, WPT=3.10e-01
Added an inline note clarifying that tr's two-point ramp differs from
eq's single `RIP` parameter, so future reuse of the preset table for
eq (or tot via `eq:RIP` prefix) needs to translate the convention.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This branch (chore/pre-push-hook-worktree-compat) accumulated three
distinct workstreams over the past few days, all of which are now
pushed and verified end-to-end on macOS. The handoff doc captures:
- the four commits that landed (Sphinx hybrid layout, macOS .so
build path + setup.sh, eq mode=0, applications.md preset fix)
- the four remaining tasks the user prioritised, in order:
1. applications.md verification (mostly done; one validate
path left to confirm)
2. eq mode=0 reachability via tot prefix routing + eq_mcp
tool definition (NEXT)
3. mode=0 sync into eq/en/ pages (was authored before mode=0
landed in this branch)
4. application-pattern parity for eq / ti / fp / wr / wrx / tot
- the macOS environment setup (scripts/setup.sh + ssh-add for git
push) so a new session can pick up without re-deriving anything
- design notes that should not be relitigated (cross-module {ref}
is broken by design, U+301C must not appear in en pages, eq's
R+F fusion in mode=0, graphics-stub rationale)
The remote and local heads agree at abb7e24; nothing is pending in
the worktree besides this handoff doc itself.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bfdadf5 implemented `eq.run(mode=0)` (analytic Grad-Shafranov via EQCALC + EQCALQ), but the eq-mcp `run` tool's docstring and the README still said "mode=0 is reserved for future direct-solve modes and currently returns EQ_ERR_NOT_IMPL". Update both to describe the analytic GS path (use with MODELG=2 + RR/RA/BB/RIP/...). Also adds `test_handle_run_mode_zero` so mock-level tests guard the mode=0 forwarding from the MCP layer to `eq.run`. Verified end-to-end with the real libeqapi.so: handle_run(0) and handle_run_and_get_state (mode=0) both produce the same scalars as direct `Eq().run(mode=0)` (RAXIS=3.0709, QAXIS=0.9918, QSURF=3.7668, BETAT=8.47e-05). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two corrections to docs/sphinx/modules/tr/ja/applications.md after abb7e24 (DEVICE_PRESETS RIP -> RIPS/RIPE) shifted the numeric behaviour and exposed a logic gap. 1. `auto_setup()`: the old `if MODELG in {3,5,7,8} and eq_file:` only set KNAMEQ when an eq_file was supplied, leaving the default KNAMEQ='eqdata' in place when eq_file=None. validate's FILE_MISSING check fires only on `LEN_TRIM(KNAMEQ)==0` (tr_api.f90 :408), so the documented "MODELG=3 + eq_file=None" path silently passed validate and crashed inside tr.run() with ierr=3 instead of producing the intended FILE_MISSING diagnostic. Always overwrite KNAMEQ with `eq_file or ""` so validate catches it. Verified: now reproduces the documented FILE_MISSING + RuntimeError flow. 2. Refresh the three "expected output" code blocks (StableTrRunner, sweep, auto_setup ITER) to the actual values produced by the current build: - StableTrRunner: T=1.000s, BETAN=0.283, retries=0 (default params don't diverge, so retry doesn't fire; the retry-log example moved to its own block). - sweep: 9-point grid with WPT in MJ. - auto_setup ITER (ntmax=10): BETAN=0.074. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bfdadf5 added mode=0 (analytic Grad-Shafranov via EQCALC + EQCALQ) to the JA Sphinx pages but left the EN side at the ca0aeb2 baseline, where mode=0 was still described as "reserved / not implemented". Translates the JA updates to EN: - index.md overview now leads with both run paths; reading-order item updated for dual coverage. - hello-world.md split into Section A (mode=0 analytic, with RR=3.0/RA=1.0/BB=3.0/RIP=1.5 and the verified raxis=3.0709 / qaxis=0.9918 output) and Section B (mode=1 EQDSK), plus an ASCII flowchart for picking the mode. - parameter-setting.md appended a mode=0 follow-up snippet after the validate example. - faq.md (Q2) reframed from "why default mode=1" to a 2-row mode/CLI/setup table; mode=0 description no longer says "EqlibNotImplementedError". - design.md eq_run table extended with a Legacy CLI column (R + F vs L) and an admonition explaining the EQCALC + EQCALQ chain. mcp.md and context-manager.md needed no changes (already authored without {ref} cross-module syntax or jsonc lexer). EN-page conventions preserved: ASCII characters only (no U+301C ~, no Unicode box-draw); identifiers (MODELG, KNAMEQ, EQCALC, ...) kept verbatim. Sphinx eq-en build passes warning-free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caught in code review of df0338d..303c835: bfdadf5 implemented mode=0 (analytic Grad-Shafranov) and the eq-mcp/Sphinx docs were synced to it, but the canonical Python wrapper docstring in python/eqlib/eqlib.py:274-285 still claimed: "0 is reserved for an EQCALQ-style direct solve and currently returns EQ_ERR_NOT_IMPL" so `help(Eq.run)` was actively misleading users. Rewrite to describe both modes (1 = EQDSK load, 0 = analytic GS via EQCALC + EQCALQ) with the matching MODELG ranges. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the tr applications.md pattern to eq. Three sample patterns wired around the analytic GS solve (mode=0): 1. smart_run — selects mode=0 (analytic) vs mode=1 (EQDSK file load) based on whether an eqdsk_file path is supplied. eq has no DT-style retry knob, so a mode-selector replaces the StableTrRunner shape. 2. sweep — RR x BB grid (3x3) executing mode=0 per point; reports qaxis / qsurf / betap. Each point ~50ms (analytic). 3. auto_setup with validate — DEVICE_PRESETS for ITER/JET/DIIID with automatic RB=1.2*RA derivation (default RB=1.2 < RA causes EQMAGS to PAUSE on ITER-scale geometry). Hooks into eq's native validate() so MODELG=3 + empty KNAMEQ surfaces FILE_MISSING; OUT_OF_RANGE (e.g. NRMAX=9999) fires as a warn-only diagnostic. All numeric outputs verified end-to-end against libeqapi.so: smart_run: raxis=3.0709 qaxis=0.9918 sweep (9 pts): qaxis 0.473..2.269, qsurf 1.555..8.789 auto_setup ITER: raxis=6.249 qaxis=0.596 qsurf=3.565 Sphinx eq-ja build passes warning-free; applications added to the toctree under "使い方ガイド". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the tr/eq applications pattern to wr (ray tracing). wr has
no native validate() and no continuous DT knob, so the patterns are
adapted to its geometry-driven failure mode:
1. safe_run — diagnostic wrapper that catches WrlibRunError and
inspects a params dict to enumerate likely causes (RF out of band,
RKR0 ~ 0, RPI outside [RR-RA, RR+RA], NSMAX < 1). More useful
than a retry loop because wr errors are mis-configurations, not
numerical instability.
2. sweep — RFIN[1] x ANGPHIN[1] (LH freq x toroidal angle) 3x3 grid
on the wr_iter_lhcd fixture; collects pos_pwrmax_*, pwrmax_*,
nstp_end[0] per point. Modeled on python/wrlib/tests/test_sweep.py.
3. auto_setup with hand-rolled preflight — DEVICE_PRESETS = {
ITER_LHCD, TST2_EC } derived from real fixtures (including
MDLWRI=101, MDLWRQ=0, MDLWRW=0 needed for run success). preflight
classifies issues as REQUIRED/ARRAY_LEN (blocking) vs OUT_OF_RANGE
(warning), mirroring tr/eq validate shape.
Reproduced numeric outputs against libwrapi.so:
safe_run OK: pos_pwrmax_rl=4.2002, nstp_end=[100]
sweep (9 pts): all pos_pwrmax_rl=4.2002, nstp_end mostly 100
auto_setup ITER_LHCD: pos_pwrmax_rl=4.2002 nstp_end=100
auto_setup TST2_EC: pos_pwrmax_rl=0.2200 nstp_end=200
Documented Layer-2 stub limitation in a {note} admonition: pwrmax_*
scalars stay 0 because absorption post-processing is not wired;
points users to wrx for actual absorption profiles.
Sphinx wr-ja build passes warning-free; applications added to toctree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the tr/eq applications pattern to fp (Fokker-Planck). Three samples adapted to fp's velocity-space solver nature: 1. StableFpRunner — catches FplibCalcFailedError (which is also FplibOverflowError) and halves DELT from 1e-3 down to 1e-6. A yellow callout explains the EPSFP/LMAXFP non-linear solver context that makes DELT halving work. 2. sweep — RR x BB 3x3 grid mirroring python/fplib/tests/test_sweep.py (NRMAX=10, NPMAX=30, NTHMAX=30, DELT=1e-3, ntmax=10). Collects RNT[0] (center density), RTT[0], RWT[-1] (edge energy). A "なぜ 9 点とも同じ値になる理由" callout explains that fp is a velocity- space solver: (RR,BB) only matter when MHD equilibrium / FOW bouncing is wired in. Suggests PTPR x PN as the physically meaningful sweep instead. 3. auto_setup with hand-rolled validate_fp_params — DEVICE_PRESETS for ITER/JET/JT60SA. Validate checks NSMAX>=1, NPMAX/NTHMAX/NRMAX >5. Notes that fp has no native validate() (unlike tr/eq) and that STOP / issue #142 risk lives in fp_init for NSMAX<1. Reproduced live numeric outputs against libfpapi.so: StableFpRunner: timefp=0.0050, RNT[0][0]=0.9546, RTT[0][0]=4.5545 sweep (9 pts): all RNT[0]=0.9985, RWT[-1]=0.105721 (uniform; doc explains why) auto_setup ITER: timefp=0.0100, RNT[0][0]=0.9985, RTT[0][0]=4.9807 validate(NPMAX=4): [INVALID] NPMAX rejection Sphinx fp-ja build passes; the 4 warnings emitted are pre-existing autodoc complaints from python/fplib/state.py:FpState.to_dict (not introduced here, also fire on master). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… + species helper
Extends the tr/eq applications pattern to ti (transport-ion). Three
samples adapted to ti's multi-species nature:
1. StableTiRunner — with-managed TiLib that catches TilibRunError
(ierr=3) and halves DT until floor 1e-5. Mirrors StableTrRunner.
2. sweep — RR x BB 3x3 grid with NSMAX=2 (defaults seed PA/PZ/PN/PT
internally). Collects T, central T0=RTA[0][0], central
n0=RNA[0][0], residual_loop_max, icount_loop_max. A tip explains
flat results because MODEL_*=0 defaults skip the actual transport
solve.
3. auto_setup with hand-rolled validate + _apply_species helper —
the critical piece for ti. _apply_species({1:{'A':...,'Z':...,
'n':...,'T':...}, 2:{...}}) expands a Python dict into 1-origin
PA[i]/PZ[i]/PN[i]/PT[i] set_param calls so users never type
indices. hand_validate checks NSMAX>=1, len(species)==NSMAX,
contiguous 1-origin keys, and presence of A/Z/n/T per species.
Two SPECIES_PRESETS (DD = e+D, DT_with_C = e + D/T-avg + C) and
three DEVICE_PRESETS (ITER/JET/DIIID) with only RR/BB (ti has no
equilibrium solve, so no RIP).
Reproduced live numeric outputs against libtiapi.so (with adpost/
ADPOST-DATA and ADF11-bin.data correctly placed):
StableTiRunner ITER-like: T=0.100s T0=4.999keV retries=0
sweep (9 pts): uniform T=0.1, T0=4.9995, n0=0.9986
ITER + DD preset: T=0.100s T0=4.999keV n0=0.9986
DT_with_C preset (3 species): T=0.050s
Documented data-file requirements (ADPOST-DATA / ADF11-bin.data) in
an admonition.
Sphinx ti-ja build: 1 warning emitted is pre-existing in api-reference.
md (verified by rebuilding without changes — same warning fires).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s + ray helper
Extends the tr/eq applications pattern to wrx (extended ray tracing).
Three samples adapted to wrx's per-ray array indexing:
1. safe_run + ray-launch sanity report — pure-Python preflight checks
each ray's RPI/RF/ANGT against [RR-(RA+RB), RR+(RA+RB)] etc.; on
WrxlibRunError it prints which ray is the suspect (since geometry-
error retry is futile).
2. fan_sweep via _apply_rays — fan_sweep([8,10,12]) returns pwr_tot,
pwr_nsa (per-species sum), and pwr_nsa_nray (per-ray x per-species
2D matrix).
3. auto_setup + hand-rolled _hand_validate + DEVICE_PRESETS[
"ITER_LHCD_2ray"] — preset splits {shape, rays}; validate enforces
NSMAX>=1, non-empty rays, REQUIRED_RAY_KEYS = ("RF","RPI","ZPI",
"ANGT") per ray. NRAYMAX = len(rays) auto-derived.
The _apply_rays(wrx, rays) helper takes [{"RF":170e3, "RPI":8.0,
"ZPI":0.5, "ANGT":10.0}, ...], hides 1-origin Fortran indexing,
fills defaults (PHII=0, ANGPHI=0, UU=1, MODEW=1), and writes
RFIN[i]/RPIN[i]/... via set_param.
Reproduced live numeric outputs against libwrxapi.so (ITER 170 GHz,
NSMAX=2 D+e):
Single ray (ANGT=10): pwr_tot=0.7840, pwr_nsa=[0.7840, 0.0]
3-ray fan ANGT=8/10/12: pwr_tot=1.9122
2-ray ITER preset: pwr_tot=1.0073
Bad geometry (RPI=1.0 < 2.0): preflight flags ray 1 then ierr=3
Caveats noted in {note} admonition: historical libgrf::grd1d SEGV
(retired by PR #123), WRX_RUN_OK=1 default since PR #166, and the
pwr_nray[i]==0.0 quirk (use pwr_nsa_nray row sums).
Sphinx wrx-ja build: warning-free (-W --keep-going passes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ L-7 placeholder
Extends the applications pattern to tot (the orchestrator). Three
samples adapted to L-6's TR-only run scope:
1. namespaced setup — Tot()'s 1-init brings up tr/ti/fp/wr/eq; each
module's params are addressed via <ns>:<name> (eq:RR, tr:NSMAX,
etc.). Demonstrates the canonical prefix table and a 1-step run
to confirm tr_present=1.
2. transport_step wrapper — wraps tot.run() + state.scalars
aggregation (T, WPT, BETAN, TAUE1, Q0). At L-6 only the TR scalars
are populated (state.{ti,fp,wr}_present == 0 by design — see
tot_api.f90 comment block).
3. L-7 future coupling pipeline — pseudo-code showing the intended
wr -> tr (RF heating), fp -> tr (current drive), eq -> tr (time-
stepped equilibrium) chain. Currently only step (4) tot.run(ntmax)
is wired; the table at the end lists the proposed L-7 APIs
(run_module, run_pipeline, couple, per-module state aggregation).
A prominent {warning} admonition at the top spells out the L-6
constraint so readers do not assume coupling is live.
Reproduced live numeric outputs against libtotapi.so:
namespaced 1-step: tr_present=1, T=0.010s
transport_step (ntmax=10): T=0.1, WPT=10.01, BETAN=0.2872,
TAUE1=11.42, Q0=2.519
Cross-module {doc} refs were avoided per repo convention (each module
is its own Sphinx project); references to other modules' applications
pages use plain text + path. Sphinx tot-ja build is warning-free.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review caught the original explanation as factually wrong. The
sweep doc said nstp_end=100 because "Layer-2 stub clips NSTPMAX=2000
to 100 internally", but the actual limit comes from wrexecr.f90:505:
NSTPLIM = MIN(INT(SMAX/DELS), NSTPMAX)
With ITER fixture SMAX=5.0 / DELS=0.05, INT(SMAX/DELS)=100 is the
binding constraint — there is no stub-side clipping of NSTPMAX. The
"nstp_end < limit means early termination" conclusion stands; only
the rationale is updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the eq applications page (smart_run / sweep / auto_setup with native validate). Mirrors a3ccfda's JA structure 1:1; numeric outputs preserved verbatim. Listed under "User guide" in the toctree, matching the JA placement. Verified: sphinx eq-en build is warning-free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the wr applications page (safe_run / sweep / auto_setup with hand-rolled preflight). Mirrors 91e29e1 + ae0d35e in JA. Includes translation of all in-code diagnostic messages (safe_run notes, preflight [REQUIRED]/[ARRAY_LEN]/[OUT_OF_RANGE] tags, auto_setup print/RuntimeError messages) so the example output blocks match the actual code output. Numeric values (pos_pwrmax_rl=4.2002, nstp_end, etc.) preserved verbatim. Verified: sphinx wr-en build is warning-free; zero JP characters remain in the file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the fp applications page (StableFpRunner / sweep / auto_setup with hand-rolled validate). Mirrors a975602. Numeric outputs (timefp=0.0050, RNT[0][0]=0.9546, etc.) preserved verbatim. Tip admonition explaining velocity-space-solver behavior of the 9-point sweep retained. Verified: sphinx fp-en build clean (the 4 pre-existing autodoc warnings from python/fplib/state.py:FpState.to_dict are not from this change — they fire on master too). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the ti applications page (StableTiRunner / sweep / auto_setup + _apply_species helper). Mirrors 279b766. Numeric outputs (T=0.100s, T0=4.999keV, etc.) and species/device preset names preserved verbatim. Data-file precondition admonition (for ADPOST-DATA / ADF11-bin.data) preserved with same admonition class. Verified: sphinx ti-en build clean (the 1 pre-existing api-reference warning is not from this change). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the wrx applications page (safe_run / fan_sweep / auto_setup + _apply_rays helper). Mirrors 3b86091. Numeric outputs (pwr_tot=1.9122, pwr_tot=1.0073, etc.) and PR #123 / PR #166 caveat references preserved verbatim. Verified: sphinx wrx-en build is warning-free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
English version of the tot applications page (namespaced setup + transport_step + L-7 future placeholder). Mirrors 4f99e15. Numeric outputs (T=0.010s, WPT=10.01, BETAN=0.2872) preserved verbatim. The L-6 limitation warning admonition at the top is preserved with its full force. Cross-module file references in plain text were switched from tr/ja/applications.md to tr/en/applications.md (and likewise for eq) so the EN reader gets the EN version. Verified: sphinx tot-en build is warning-free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review caught 3 phrasing inconsistencies that came from the 6
EN translations being authored by different sub-agents in parallel:
- "Extension ideas" -> "Possible extensions" (was split between
eq/fp and wr/ti/wrx/tot)
- "Combined patterns" -> "Combination patterns" (was split between
eq/wrx and wr/fp/ti/tot)
- "Auto-stabilising" -> "Auto-stabilizing" (BrE in fp, AmE in ti)
Now consistent across all 6 EN applications pages. No semantic
changes; pure copy-edit. Sphinx builds for all 6 en modules pass
warning-free.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… 5, 8}
Codex review caught the mode-selection flowchart in eq/en/hello-world.md
:72 (and the JA mirror at eq/ja/hello-world.md:71) showing only
"MODELG=3 with KNAMEQ set" for the mode=1 branch, contradicting the
prose 11 lines above which already correctly states "you need MODELG
∈ {3, 5, 8}". Readers using MODELG=5 (boundary) or MODELG=8 (extended
EQDSK) would have been silently excluded by the diagram.
Both ja/en flowcharts now match the prose. Sphinx eq-ja and eq-en
builds remain warning-free.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to 89295ea (handoff for the session that finished 2026-04-27 morning). Captures: - 19 push'd commits from 2026-04-27 PM (df0338d..c477a1d): eq mode=0 propagation (eq_mcp / eqlib / EN docs), tr applications auto_setup KNAMEQ fix + numeric refresh, 6 modules' applications .md ja+en, EN wording unification, eq hello-world flowchart fix. - 8 prioritised follow-ups for the next session, with file paths and rationale (top: L-7 cross-module coupling in tot; second: add Codex review to CLAUDE.md pre-push gate). - Design-rule reminders not derivable from the code: cross-module {ref}/{doc} forbidden, EN must avoid U+301C, state.scalars keys are lowercase, eq RB defaults to 1.2 (must scale with RA), library -reachable PAUSE traps stdin in pytest. - Slide deck files (docs/slides/) intentionally left uncommitted per user direction; a regen recipe is recorded. - A copy-pastable "next session opening prompt" so the next run can start zero-context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): add L-7a cross-module coupling design Design for incremental Python-side scalar coupling between fp and tr in the tot orchestrator. Establishes TotPipeline class skeleton in a new file (python/totlib/pipeline.py) without touching the legacy Tot wrapper. Covers fp -> tr driven current (RJT volume integral -> PNBCD scalar) only; profile/coordinate transforms deferred to L-7b via BPSD. Two Codex review passes: 9 findings (HIGH 2 / MED 5 / LOW 1) all addressed inline. Pre-flight Research R1 (singleton coexistence + finalize/init state-leak) / R2 (rjt_volint extraction) / R3 (tr param name confirmation) gated before implementation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(plan): add L-7a cross-module coupling implementation plan Bite-sized TDD task list executing the L-7a design spec (0a12bd0) across 4 phases / 16 tasks / 3 sub-PRs: - Phase 0: Pre-flight research R1/R2/R3 (singleton coexistence, rjt_volint extraction, tr driven-current param confirmation) - Phase 1 (PR 1): TotPipeline class + errors + mock unit tests - Phase 2 (PR 2): compute_rjt_volint helper + COUPLING_RULES population + fp -> tr 1e-10 equivalence test - Phase 3 (PR 3): run_pipeline MCP tool + docs Each PR phase ends with the CLAUDE.md pre-push gate (code-reviewer + codex:codex-rescue + REVIEW_OK marker + --forked --timeout=120 --timeout-method=signal pytest). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(spec): record L-7a R1 (singleton coexistence) outcome R1-a (concurrent live) and R1-b (re-init state leak) verified per the L-7a design spec. Outcome and fallback decision recorded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(spec): record L-7a R2 (rjt_volint extraction) outcome R2 PASS. compute_rjt_volint(state, *, R0, a) verified against fp internal aggregate rtotalIP at 1e-10 (sanity case NRMAX=10, RR=3, RA=1, default fixture: helper -2.769528e-13 MA == fp stdout -2.7695E-13 MA). FpState shape characterized: top-level dataclass attrs [nrmax, nsamax, npmax, nthmax, ntg2, timefp, RNT/RWT/RTT/RJT/RPCT/RPWT]; **no `.scalars` dict** (asymmetric to TrState — R1 concern #3 confirmed). FpState exposes no grid info (RG/DV/VOLR/RA/RR absent), so helper takes R0, a as kwargs and assumes uniform rho-grid in [0,1] (matches fp/fpprep.f90:97-99). RJT units: [MA/m^2] (matches RJS in fp/fpcale.f90:290 where RJ_P = RJS*1.D6 converts to A/m^2). Helper applies *1e6 internally and returns total I in [A]. fplib unchanged — helper lives in totlib/pipeline.py (Phase 2 Task 2.1). Probe stdout (truncated): FpState attributes: RJT/RNT/RPCT/RPWT/RTT/RWT (list[list[float]]), npmax=50, nrmax=1, nsamax=1, ntg2=2, nthmax=50, timefp=0.01. Grid candidates RG/DV/VOLP/RM/RMAX/DR/RA/RR/RB: ALL ABSENT. Scalar candidates AJT/AJT_driven/.../scalars: ALL ABSENT. Sanity check (NRMAX=10): fp stdout: total plasma current [MA] -2.7695E-13 helper: compute_rjt_volint = -2.769528e-07 A = -2.769528e-13 MA (1e-10 match) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(spec): record L-7a R3 (tr driven current param) outcome PARTIAL PASS: PNBCD is NOT in tr_param_registry. Registered scalar candidates are PICCD, PECCD, PLHCD (dimensionless current-drive factors) and PLHTOT (LH input power [MW]). Selected PLHCD as L-7a skeleton target with documented unit mismatch (Amperes -> dimensionless factor); rigorous physical conversion deferred to L-7b. Picked a = tr.RA for compute_rjt_volint(state, R0=tr.RR, a=tr.RA) because fp's radial mesh is RA-normalized (fpcale.f90:34, 56). Active-drive fixture for Phase 2.3 equivalence test: fp.set_param('E0', 0.001) # induction E-field [V/m] yields ~0.946 MA (vs ~1e-13 MA for default), well above the 1e-10 equivalence-test floor. MODEL_WAVE+PABS_LH path failed with rc=3 and needs additional setup; E0 is the minimum-cost active-drive trigger. Open issue surfaced: tr lacks a registered scalar that semantically accepts an external driven current in MA. L-7b should add such a param (or expose PNBCD via the registry). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(l7a): R4 decision gate — accept skeleton coupling, update spec/plan R3 confirmed PNBCD is not registered in tr_param_registry.f90; only PLHCD/PICCD/PECCD (dimensionless multipliers) accept set_param. Per user decision (option A), L-7a proceeds as API-plumbing skeleton with PLHCD as the dst_param; physical fidelity (proper EXTERNAL_DRIVEN_I scalar in tr) is moved to L-7b follow-ups. Spec updates: - §2 table: PNBCD -> PLHCD with skeleton-coupling caveat - §3 non-goals: explicit "physical fidelity is L-7b scope" - §5.2 CouplingRule: src_state_key now (state, params) 2-arg callable - §5.3 PipelineResult + new _state_to_scalars adapter (handles FpState which lacks the .scalars dict per R1/R2) - §5.4 TotPipeline: self._params tracking for callable rules - §6.2 run_pipeline: uses _state_to_scalars + records dst_param injections back into _params - §12 follow-ups: tr EXTERNAL_DRIVEN_I_MA addition + fplib .scalars property consideration Plan updates: - Task 1.4: __init__ adds self._params; set_param records into it - Task 1.5: _extract_source 2-arg callable + _state_to_scalars helper + 5 new tests (callable+params, set_param tracking, adapter) - Task 2.1: compute_rjt_volint(state, *, R0, a) signature with the R2-derived implementation pasted in - Task 2.2: COUPLING_RULES uses lambda wrapper pulling tr:RR/tr:RA from params; dst_param=PLHCD; documents skeleton nature - Task 2.3: equivalence test uses E0=0.001 active-drive fixture (R3), PLHCD instead of PNBCD, _state_to_scalars adapter, plus a sanity guard that the integral magnitude is non-trivial (>= 1e3 A) - Test count expectations updated throughout Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): add TotPipeline error hierarchy Five new exception classes for the L-7a pipeline: - TotPipelineError (base, extends TotlibError) - TotPipelineUnknownModuleError, TotPipelineCouplingError, TotPipelineLifecycleError - TotPipelineRunError carrying partial_result + failed_step_index + failed_module attributes for debugging mid-pipeline failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): add CouplingRule/PipelineStep/PipelineResult dataclasses Pure data carriers for the run_pipeline orchestrator. CouplingRule is frozen (immutable) so the registry can be hashable in the future. PipelineResult.last(module) returns the most recent matching step, to_dict() is the MCP serialization format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(totlib): match typing convention from surrounding modules Code-quality reviewer flagged divergence: pipeline.py used PEP 585 (dict[...], list[...]) while totlib/state.py and sibling state.py files use Dict[...], List[...] from typing. Match the surrounding codebase. Also addresses two reviewer nits: explicit "frozen=True rationale" docstring on CouplingRule, and explicit shallow-ref caveat docstring on PipelineResult.to_dict. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): add module registry + lazy import helpers _MODULE_REGISTRY maps short names (fp/tr/eq/wr/wrx/ti) to (package, wrapper_class, base_error_class) triples. _import_wrapper / _import_module_error keep the lazy-load contract from spec section 5.5: only modules used in the current pipeline get their lib*.so / errors.py loaded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(totlib): correct ti wrapper class name + add parametrized registry tests Code-quality reviewer flagged a coverage gap: only fp was exercised by the registry-resolves-class tests. Adding parametrized tests across all 6 modules immediately surfaced a real bug: the ti wrapper class is `TiLib` (capital L), not `Tilib` as the registry claimed. The sibling `TilibError` IS lowercase, so ti is the only entry where the wrapper-class and error-class casing diverge — easy to miss. Fixed registry entry; added a comment flagging the asymmetry. Spec and plan updated to match. Now 31/31 pipeline tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): add TotPipeline lifecycle (init/set_param/close) Implements the lifecycle subset of TotPipeline: - __init__ creates empty _modules / _params / _closed=False; opens nothing. - _ensure_module lazy-instantiates a wrapper on first reference, gated by the closed flag. - set_param routes strings to set_param_str, numerics to set_param, and records the value into _params after the wrapper accepts it (so failed wrapper validation keeps _params consistent). - close is idempotent and finalizes all modules even when one raises; the first exception is re-raised so the root cause is visible. - Context manager support via __enter__/__exit__. run_pipeline / _extract_source / COUPLING_RULES are deferred to Task 1.5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(totlib): guard set_param_str when module wrapper lacks the method Code-quality reviewer flagged that wrlib.Wrlib, wrxlib.Wrxlib, and tilib.TiLib do not expose set_param_str (only fp/tr/eq do). Routing a string value through TotPipeline.set_param previously raised a bare AttributeError leaking from getattr. Now raises a typed TotPipelineCouplingError with a clear message identifying the module. Added a regression test using a mock wrapper without set_param_str. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): implement TotPipeline.run_pipeline orchestrator - _state_to_scalars adapter (handles fp's missing .scalars dict + the standard .scalars-bearing modules uniformly). - COUPLING_RULES registry (empty at this commit; ('fp','tr') is filled in Phase 2 after R2/R3 outcomes are baked into pipeline.py). - _validate_steps performs side-effect-free pre-flight checks. - _extract_source resolves callable src_state_key with (state, params) signature; string keys go through _state_to_scalars. - run_pipeline iterates steps, applies coupling rules between adjacent pairs, and wraps per-module errors as TotPipelineRunError with the partial PipelineResult attached for debugging. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(totlib): export TotPipeline + dataclasses at package level Backwards-compatible: legacy 'from totlib import Tot' / 'TotState' imports continue to work. New 'from totlib import TotPipeline, CouplingRule, PipelineStep, PipelineResult' is added for L-7a users. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(totlib): broaden run_pipeline catch + cover Codex review gaps Codex independent review (2026-04-28) flagged 1 MED + 2 LOW gaps the prior reviewer missed: MED1: run_pipeline's except clause was (base_err, TotPipelineCouplingError), which let TypeError/ValueError from wrong kwargs escape without TotPipelineRunError wrapping — so partial_result was lost. Broadened to `except Exception`. KeyboardInterrupt and SystemExit still propagate (they are BaseException, not Exception). The lazy `_import_module_error(name)` call is kept for forward-compat debugging clarity (annotated noqa). LOW2: test_set_param_records_into_params_dict only exercised the numeric path of set_param. Added a string-value entry so the string path's _params bookkeeping is also verified. LOW3: test_run_pipeline_partial_failure_carries_partial_result did not assert on coupling_applied. Added an empty-list assertion (regression guard against silently dropping coupling metadata). Plus a new test test_run_pipeline_typeerror_from_bad_kwargs_is_wrapped that exercises the broadened catch path directly. 61 tests pass under --forked --timeout=120 --timeout-method=signal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(totlib): address PR #178 Bugbot LOW findings Three independent fixes surfaced by the Cursor Bugbot pass on PR #178: 1. close() no longer silently drops secondary finalize errors (pipeline.py:259-269). When more than one module's close() raises, the caller now sees an ExceptionGroup carrying every collected error instead of just errors[0]. Single-failure path is unchanged (the original exception type is preserved). Tested via the new test_close_raises_exception_group_when_multiple_modules_fail. 2. set_param's _params bookkeeping no longer diverges from the value the wrapper actually received (pipeline.py:235-249). Numeric inputs are coerced via float() at the boundary, but _params used to store the original (e.g. True or int 2). Coupling rules reading self._params now see the same float the underlying module saw. Existing test_set_param_records_into_params_dict updated for the int → float coercion; new test_set_param_records_bool_as_float pins the bool path explicitly. 3. run_pipeline no longer calls _import_module_error per step (pipeline.py:329-336). The call had become dead after the broad "except Exception" catch in commit 941f353 — base_err was F841'd noqa'd, the comment misleadingly claimed "lazy-import semantics," and the call still triggered an .errors module import per step. _validate_steps already gates on _MODULE_REGISTRY membership pre-execution, and _ensure_module raises on bad names, so removing the dead call doesn't reduce safety. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(totlib): address PR #178 remaining Bugbot findings (4 items) Bugbot's second pass on PR #178 surfaced 4 issues that survived the first round of LOW fixes (e1e5a37). All four addressed in this commit, with targeted regression tests: #1 (LOW) PipelineResult.to_dict() now returns defensive copies of inner scalars dicts and coupling_applied lists. Previously, MCP- side mutation of the JSON payload would propagate into the originating PipelineStep. New test_to_dict_returns_defensive_copies asserts the contract. #2 (LOW) _validate_steps now accepts list-of-2-element steps in addition to tuple-of-2-element. JSON-decoded MCP payloads deserialise tuples as lists, so the API now round-trips through to_dict() without manual coercion. New test_run_pipeline_accepts_list_shaped_steps regression guard. #3 (LOW) set_param now explicitly rejects bool inputs with TotPipelineCouplingError instead of silently coercing to 1.0/0.0 via the numeric branch (Python's bool is a subclass of int). The previous test_set_param_records_bool_as_float (which documented the silent-coercion behavior) is replaced with test_set_param_rejects_bool that asserts the explicit rejection. #4 (MED) ExceptionGroup compatibility shim for Python 3.10. The close() multi-failure path uses builtin ExceptionGroup (3.11+); tot_mcp's pyproject.toml declares requires-python=">=3.10". The shim probes for the builtin first, falls back to the exceptiongroup PyPI backport if available, and degrades gracefully to raising errors[0] only when neither is reachable (same as pre-fix behavior). CI runs 3.11+ so the existing ExceptionGroup test still passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(totlib): add compute_rjt_volint helper
Computes the fp driven-current volume integral as a scalar in Amperes.
Implementation derived from spec §8 R2 outcome. No fplib changes —
helper operates on Python-side state attributes only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(totlib): clean up test_pipeline_helpers + add nr=0 case
Drops the duplicate MagicMock and unused pytest imports inherited
from the plan template, fixes the unit label in the uniform-single-
species docstring (3.14159 MA -> 3.14159e6 A), and adds a regression
test that exercises the compute_rjt_volint nr<1 short-circuit branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(totlib): populate COUPLING_RULES[('fp','tr')]
Single rule wires fp's compute_rjt_volint to tr's PLHCD param (per
spec §8 R2/R3 outcomes). The src_state_key is a callable that pulls
tr:RR/tr:RA from TotPipeline._params. PLHCD is dimensionless, so this
is a skeleton coupling — L-7b adds a proper EXTERNAL_DRIVEN_I scalar.
The callable short-circuits to 0.0 when tr:RR/tr:RA are absent from
params; real pipelines always set these before run_pipeline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(totlib): drop COUPLING_RULES lambda short-circuit; fix pre-existing test
The fp→tr rule's src_state_key lambda was masking missing tr:RR/tr:RA
params with a silent 0.0 fallback. This violates "trust internal code"
policy — a missing required param should surface as a coupling error,
not be papered over.
Revert the lambda to the plan-exact form (raw params["tr:RR"]/["tr:RA"])
and instead update test_run_pipeline_typeerror_from_bad_kwargs_is_wrapped
to set tr:RR/tr:RA before invoking run_pipeline. That test is about
TypeError wrapping at module.run(), not coupling-rule validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(totlib): split _extract_source error paths + add lambda regression tests
Phase 2 introduced the first callable src_state_key in production
COUPLING_RULES. _extract_source's combined except-block was routing
the lambda's KeyError("tr:RR") through the string-key branch, which
rendered the rule.src_state_key as <function <lambda>> and claimed
the key was missing from prev_state.scalars (it's in self._params).
Split the callable and string paths so each gets an accurate error
message. Add two regression tests: one pins the fp→tr lambda's
behaviour against argument-swap regressions, the other guards the
new error-routing path by asserting "tr:RR" appears in the surfaced
message.
Also refresh the stale registry header comment ("filled in Phase 2"
was true at write-time but is wrong now that the rule is populated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(totlib): add fp→tr equivalence test at 1e-10 tolerance
Compares pattern X (hand-written Fplib + compute_rjt_volint + Trlib
+ tr.set_param('PLHCD', ...)) vs pattern Y (TotPipeline.run_pipeline).
Per CLAUDE.md, equivalence tests at 1e-10 must pass — no SKIP allowed.
Uses E0=0.001 active-drive fixture (R3 outcome) to ensure the integral
is non-trivial (>= 1e3 A sanity guard). Requires libfpapi.so + libtrapi.so.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
D1 半分: eqdata.ITER01 fixture を使った single-run T1 と RR×BB の 3×3 sweep + matplotlib heatmap T3。T2 (JET-like) は eqdata.JET fixture が無いため deferred、T4-T6 は D2。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex HIGH: spec was citing eq-side fixture eq_iter01_params.py whose
RIP/RB/etc. are not in the tr registry — switched to tr-side
tr_iter01_params.py (lines 21-47/51-56/60-62) which uses RIPS/RIPE
and lets EQDSK supply the geometry. T1 step 5 extension `RIP` →
`RIPS`. Codex LOW: §6 "four {doc}" → enumerate all six. T3 step 4
gains explicit placeholder-output guidance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex MED: spec called eqdata.ITER01 an "EQDSK" fixture, but MODELG=3 reads TASK/EQ binary via EQRTSK; EQDSK (EQDSKR) is MODELG=5. Replaced "EQDSK" → "TASK/EQ binary" throughout (§3.1 goal/step 2/ step 3, §7 reviewer focus). Codex LOW: §3.1 step 2 now notes that test_sweep.py overrides RR/BB after apply() while T1 intentionally does not. tr_param_registry.f90 line ranges narrowed from :78-115 (too broad) to :78-84 (geometry block) + :114-115 (RIPS/RIPE). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex HIGH: T3's RR×BB sweep is broken under MODELG=3. Trace:
tr_run -> tr_prep -> tr_set_metric -> eq_load (reads eqdata) ->
tr_bpsd_get (writes RR=device%rr etc. at trbpsd.f90:171-178). Any
user set_param("RR", ...) is silently clobbered before the loop
starts. (test_sweep.py technically runs but its sweep is a no-op,
hidden by a finite-WPT-only assertion.)
Switched T3 axes to RIPS × PNBR0 (plasma-current start × NB heating
amplitude) — both tr-side knobs not in BPSD's overwrite list.
Page MUST explain why RR/BB sweep was rejected. Step-5 shape-
optimisation extension now points readers to MODELG=2 (analytic
equilibrium) where geometry knobs survive.
Added §9 criterion 8a + §7 reviewer focus bullet for sweep-axis
clobber check.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three HIGH from round 4:
- RIPS clobbered: trbpsd.f90:370-374 recalibrates RIP/RIPS/RIPE
from metric-derived current under MODELG ∈ {3,5,8,9}, after
eq_load. set_param("RIPS", ...) is a no-op.
- PNBR0 mislabeled: trinit.f90:420 documents PNBR0 as NBI radial
deposition POSITION (M), not amplitude. trpnb.f90:50,56 confirms
Gaussian-center usage.
- PNBTOT missing: actual NB amplitude PNBTOT (trinit.f90:419) is
default 0, unset in tr_iter01_params.py, AND not in
tr_param_registry.f90 — set_param("PNBTOT", ...) would raise
INVALID. NBI heating is OFF in every ITER01 run today (separate
finding; tracked outside this spec).
Switched T3 axes to PT[1] × PN[1] (axis ion temperature × axis
ion density). Both registered (parse_array_subscript at
tr_param_registry.f90:101-106), both survive BPSD pull
(trbpsd.f90:183-204 writes RN/RT, not PN/PT), both physically
interpretable (WPT ≈ ∫(3/2)nT dV). Page now has explicit gotcha
section enumerating all three rejected alternatives with their
failure mechanism.
Two MED:
- BPSD pull timing: said "tr_init" but actually happens on first
tr_run call (tr_run -> tr_prep -> tr_set_metric -> tr_bpsd_get).
Fixed §3.1 step 2 + §7 reviewer focus.
- Criterion 8a renumbered to 9; subsequent criteria shifted
9-12 → 10-13.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HIGH: tr/trprof.f90:227-228 only covers PN→RN; PT→RT is at :230-231. Citation now spans both ranges. MED: WPT formula was incomplete — it sums over all bulk species plus fast-particle tail (zero when MDLUF=0). Updated to Σ_s ∫(3/2)n_s T_s dV with citations to tr/trrslt_globals.f90:115-127 (WST(1:NSM)) and :315-322 (WTAILT). LOW: parse_array_subscript is at line 74 (call) + 209-230 (impl), not at 101-106 (which is the array CASE block). Citations split. LOW: "EQDSK device" was imprecise for MODELG=3 (which uses EQRTSK / TASK/EQ binary). Replaced with "loaded equilibrium device (TASK/EQ binary under MODELG=3, dispatched via EQRTSK — eq/eqfile.f90:108-115)". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six tasks: en page, ja page, en toctree, ja toctree, sphinx render check, pre-push gate. All page content embedded verbatim in the plan steps. Self-review pass confirms acceptance-criterion mapping and zero placeholders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T1: ITER-like single run using the shipped eqdata.ITER01 fixture (MODELG=3 / EQRTSK / TASK/EQ binary). Geometry comes from the equilibrium load via BPSD; only non-geometry knobs are user-set. T3: PT[1] × PN[1] parameter sweep with matplotlib heatmap. Page includes an explicit gotcha section enumerating three rejected sweep axes (RR×BB, RIPS×*, PNBTOT×*) with their failure mechanism, because under MODELG=3 the BPSD broker pull silently clobbers geometry/current overrides. D2 will cover T2 (JET-like, needs fixture decision), T4 (tr2 equivalence), T5 (tot-coupled), T6 (numerical blow-up). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code blocks byte-identical to the en page; prose translated. Same T1 + T3 coverage, same gotcha section enumerating the three rejected sweep axes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inserted after applications; reading order is hello-world → parameters → parameter-setting → input-files → state → context-manager → faq → applications → tutorials. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror of the en change; same reading order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex HIGH: T3 sweep code didn't chdir, so a reader running T3
standalone (or after exiting T1's chdir context) wouldn't find
eqdata.ITER01. Added the same os.chdir setup to T3's first
Python block in both en and ja (code blocks remain byte-identical
between languages).
Codex LOW: ja "no-op になります" tightened — set_param("RR", ...)
is accepted then overwritten by BPSD, not literally a no-op.
Reads "受理された後に上書きされます (実質 no-op)" now.
Codex LOW: WPT formula now shows the explicit + WTAILT term
(general formula), with the note that WTAILT=0 in this fixture
because MDLUF=0. Matches tr/trrslt_globals.f90:315-322.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The boundary test asserts "no crash, no NaN" but currently fails
deterministically with WPT=NaN for NSMAX ∈ {3, 4} because the
tst2 equilibrium import path (tr/trprof.f90:202-208) copies
RNU/RTU(...,3..NSMAX) into RN/RT, and eqdata.TST-2 was built
for NSMAX=2 so species 3+ slots are NaN. NaN propagates to
WPT/BETA*/TAUE* via tr/trrslt_globals.f90:69,117,126,315-341.
Mark as xfail(strict=True) so CI stays green while the real
fix (validate active species against profile-data extent, or
zero-fill species > NSU) lands in a separate PR. strict=True
auto-flags this marker for removal once the fix lands (XPASS
becomes a CI red).
Issue: #189
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec for adding PNBTOT (NBI total power, MW) to tr_param_registry and verifying via Layer-1 equivalence at 1e-10 with NBI on. Scope: 7 files (registry, fixture, .in, baseline, en/ja tutorials, MCP server, LaTeX manual) + 2 build steps (tr2, libtrapi.so). PNBTOT=25.0 chosen on codebase corpus grounding (matches tr/in/tr.ITER01.in:38). Codex round-1 review folded in (.in scope necessity, MDLNB default sufficiency, MDLNB=1 → no PNBVY/W/CD needed, stale doc surfaces). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI on 28a996f went red because test_NSMAX_in_range XPASSed in the GitHub Actions environment but was marked xfail(strict=True), so pytest reported [XPASS(strict)] FAIL. The local-vs-CI divergence shows the failure is environment-dependent (likely libtrapi.so build / pytest-forked heap layout), not deterministic as the original commit message claimed. Per CLAUDE.md narrow exception: switch to strict=False, keep the XFAIL_REMOVE_WITH_#189 grep string in the docstring as the removal trigger when the underlying RNU/RTU(...,3..NSMAX) NaN bug is fixed. Locally still XFAILs deterministically; CI now tolerates XPASS. Removal expected alongside the #189 fix PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folds in Codex round-2 findings on the PNBTOT spec: HIGH: - §8.2: replace bogus `--output` flag with positional + stdout form, matching §4.4. The script has no --output flag. MED: - §8.1: document tr/Makefile dep behaviour — non-PIC has explicit trcomm.f90 dep (line 535), PIC variant uses generic %.o rule (line 257) without inter-module tracking; add `make clean` workaround. - §10 Risks: add BPSD-driven SIGABRT row (cross-ref PR #140 + feedback_use_valgrind.md), schema-drift parse-failure row, and near-zero→finite tolerance row for compare_metrics.py 1e-10. - §12 AC: add direct `set_param("PNBTOT", 25.0)` smoke test as independent proof of registry wiring; add `.in` content grep AC. Other: - Update HEAD reference 28a996f → 20d87da after CI hot-fix. - Update reviewer agent name feature-dev → superpowers (CLAUDE.md has the legacy name; the actually-available agent is superpowers:code-reviewer). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MED: - §10: drop the compare_metrics.py near-zero risk row — Codex confirmed compare_metrics.py guards _rel_err with max(.., 1e-300) so the over-stated "div-by-zero" risk does not exist. Replace the schema-change row to correctly describe the SCALAR_KEYS allowlist behaviour (silent drop of unknown keys) and remove the factually-wrong "--help-less stderr" wording. - §10: tighten BPSD risk row with explicit memory-file paths ($CLAUDE_MEMORY_DIR/memory/feedback_use_valgrind.md, PR #140 + project_bpsd_species_kid_oob_patch.md). - §12 AC: extend the registry-confirmation one-liner with tr.run(0) so the smoke covers the full set_param → tr_prep → trpnb.f90:46 consumer path (set_param alone only proves the CASE is wired, not that the value travels). Rename "end-to-end" → "registry- and-consumption" to avoid contract overlap with §8.3. §9 reviewer agent name (Codex MED) intentionally left as-is: spec describes the working agent name and explicitly notes the CLAUDE.md legacy reference; updating CLAUDE.md is a separate PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step-by-step plan for implementing the PNBTOT registry extension per spec 2026-05-05-tr-pnbtot-registry-design.md. Decomposes into: - Task 1: pre-flight build + baseline equivalence sanity - Task 2: registry change (USE + CASE) → C1 - Task 3: fixture change (intentional failing equivalence) - Task 4: .in change - Task 5: baseline regen + atomic C2 commit (fixture + .in + baseline) - Task 6: D1 tutorials en + ja (drop INVALID claims) - Task 7: MCP server + LaTeX manual → C3 - Task 8: pre-push gate (parallel reviewers + REVIEW_OK marker) - Task 9: verify CI green 3 commits total to align with the atomic physics-state requirement (C1 = registry alone; C2 = fixture+.in+baseline together; C3 = docs). Each task lists exact diffs, expected outputs, and fallback paths for the known build / mod-pic / BPSD edge cases from spec §10. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #187 (e049a1e) added AJRFT to the libtrapi.so C state struct and Python wrapper SCALAR_FIELDS, but missed two paired Phase-0 artefacts: - tr/trregress.f90 USE TRCOMM list and WRITE statements - test_run/scripts/extract_tr_metrics.py SCALAR_KEYS allowlist The asymmetry made test_equivalence.py::TestEquivalence::test_iter01 fail at 1e-10 with `compare_metrics FAIL: scalars.AJRFT: missing` because libtrapi.so output now has 14 scalars (incl AJRFT) but the phase-0-generated baseline still had 13. CI did not surface this because eqdata.ITER01 is not staged into test_output by the workflow, so the equiv test SKIPped (per CLAUDE.md feedback_equivalence_must_pass this is the invisibility pattern). This commit: - Adds AJRFT to trregress.f90 USE list and one WRITE line - Adds "AJRFT" to extract_tr_metrics.py SCALAR_KEYS - Regenerates test_run/baselines/tr_iter01/metrics.json from a fresh tr2 build on a Linux box (clavius), so the baseline includes AJRFT and is consistent with chore branch's libtrapi.so Layer-1 equivalence (test_iter01) now PASSES at 1e-10 locally. Out of scope for this commit: - tr_tst2 baseline regen (eq_tst2 has a separate pre-existing eq drift ~3e-9 rel_err under develop's eq physics; needs its own investigation) - python/trlib/state.py (already updated by PR #187) - CI workflow changes to actually run run_tests.sh + equiv (orthogonal) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per pre-push code review on 24b1b12: the extractor unit-test fixture (test_run/scripts/tests/fixtures/sample_tr_regress.dat) is the third paired artefact PR #187 missed. Without it, future copy-paste from the fixture as a "what does a real dump look like" reference would silently omit AJRFT, recreating the same asymmetry class this PR is closing. - Adds AJRFT=0.0E+00 line after AJT in sample_tr_regress.dat - Adds assertIn("AJRFT", ...) and value-equals-0.0 assertion to test_extracts_scalars so the fixture stays locked Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds PNBTOT (NBI total input power, MW) to the Layer-3 parameter registry so libtrapi.so clients can drive NBI through set_param. The other NBI scalars (PNBR0/PNBRW/PNBENG/PNBRTG) were already registered (L-6 work) but had no effect because PNBTOT — the only knob that turns NBI on (trpnb.f90:46) — was missing. CASE inserted before PNBR0 so the NBI block reads in physical order: total-power -> deposition-shape -> beam-energy -> tangency. PNBTOT was already in trcomm_param.f90:35; this commit only adds the registry USE + CASE wiring. Spec: docs/superpowers/specs/2026-05-05-tr-pnbtot-registry-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Atomic update of the three files that must move together — splitting
them temporarily breaks Layer-1 equivalence by leaving fixture and
baseline in physically inconsistent states:
- python/trlib/tests/fixtures/tr_iter01_params.py: SCALARS gains
PNBTOT=25.0 (placed before PNBR0 to match registry order); L-6
docstring listing updated. The "Values copied verbatim from
test_run/inputs/tr_iter01.in" docstring claim (line 21) is restored
by the .in update in this same commit.
- test_run/inputs/tr_iter01.in: namelist gains PNBTOT=25.D0 so tr2
baseline matches libtrapi.so replay. 3-space indent + Fortran
double literal style match the surrounding namelist convention.
- test_run/baselines/tr_iter01/metrics.json: regenerated via
./test_run/run_tests.sh tr_iter01 + extract_tr_metrics.py on
clavius (Linux box; macOS can't build tr2 due to missing graphics
libs). Scalar shifts from NBI-off baseline:
WPT 41.13 -> 72.69 MJ (NBI added ~31 MJ stored energy)
BETAN 0.211 -> 0.373 (β raised by heating)
BETAP0 0.318 -> 0.744
TAUE1 2.27 -> 1.91 s (confinement degraded under L-mode)
AJRFT 0.0 unchanged (NBI doesn't drive RF current)
All consistent with 25 MW NBI activation through trpnb.f90 TRNBIA
Gaussian deposition (PNBR0=0, PNBRW=1, PNBENG=1000 keV).
PNBTOT=25.0 chosen on codebase corpus grounding: tr/in/tr.ITER01.in:38
uses the same value with the same ITER01 geometry params (RR=6.2,
RA=2.0, BB=5.3, ITER eqdata via MODELG=3 + KNAMEQ).
Layer-1 equivalence (test_equivalence.py::test_iter01) PASSES at 1e-10
with the new baseline; test_tst2 unaffected (still SKIPPED locally
on missing eqdata, separate pre-existing concern per the AJRFT
bookkeeping commit 24b1b12).
Spec: docs/superpowers/specs/2026-05-05-tr-pnbtot-registry-design.md §4.2-4.4
Plan: docs/superpowers/plans/2026-05-06-tr-pnbtot-registry.md Tasks 3-5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Now that PNBTOT is registered (commit 5c12dcb), four user-facing surfaces are no longer truthful: - D1 tutorials (en + ja): remove the bullet stating PNBTOT raises INVALID; replace the trailing pending-registration footnote with a working `set_param("PNBTOT", <MW>)` instruction - python/mcp-servers/tr_mcp/server.py: add PNBTOT to the nbi group metadata so MCP-driven workflows can discover the parameter - docs/manual/task-library-manual.tex: add \texttt{PNBTOT} to the NBI row of the parameter table Spec: docs/superpowers/specs/2026-05-05-tr-pnbtot-registry-design.md §4.5-4.7 Plan: docs/superpowers/plans/2026-05-06-tr-pnbtot-registry.md Tasks 6-7 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t removal Per pre-push reviewers (in-house MED-1 + Codex LOW) on the C3 docs commit b28a7f8, the PNBTOT bullet removal in T3's "what fails silently" list left two structural defects in both en + ja tutorials: 1. Missing blank line: the prose paragraph "PT[1] and PN[1] survive: ..." was attached directly to the RIPS×RIPE bullet's final line. CommonMark/MyST folds this into the bullet as continuation text, collapsing the carefully-staged contrastive structure (failures vs survivors). Insert a blank line between the last bullet and the paragraph. 2. Numerical inconsistency: the introductory sentence said "Three intuitive sweep axes ... all fail silently" but the list now enumerates only two (RR×BB, RIPS×RIPE) since the PNBTOT case was resolved by registration. Update to "Two intuitive sweep axes ... both fail silently" with parallel en/ja wording. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@cursor review Triggering after the AJRFT bookkeeping ( Local in-house + Codex pre-push reviews returned approve with C2 commit-message accuracy fixed in PR description addendum (only RT shifted; AJ/RN/QP bit-identical; single end-of-run snapshot — verified via |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f0811de. Configure here.
Summary
$(cd "$(git rev-parse --git-common-dir)" && pwd)so both the main checkout and any linked worktree share the same marker set, keyed on content (SHA) rather than workspace.touchcommand to match — the oldtouch .git/REVIEW_OK_$(git rev-parse HEAD)only worked from the main checkout's working tree root.Why
A linked worktree's
.gitis a file (a gitdir pointer to<main>/.git/worktrees/<name>/), not a directory. Bash path traversal through a file fails, so the previous hook's literal.git/REVIEW_OK_<sha>path check always reported "no marker" when invoked from a worktree, even when a valid marker existed. The only way to push was via theSKIP_PREPUSH_REVIEW=1escape hatch, which defeats the gate's purpose. With the fix, worktree-based PR workflows (branching off develop into~/.config/superpowers/worktrees/<project>/<branch>/and pushing from there) pass the gate the same way main-checkout pushes do.The
cd … && pwdround-trip also normalises the main-checkout case:git rev-parse --git-common-dirreturns the literal.git(relative to CWD) there, so the error message used to print a non-copy-pasteabletouch .git/REVIEW_OK_<sha>if the user wasn't shelling from the repo root. Now the error always shows an absolute path.Test plan
Exercised in both environments:
/Users/k-yoshimi/Dropbox/cursor/task): no marker → exit 1; marker present → exit 0;SKIP_PREPUSH_REVIEW=1 SKIP_PREPUSH_REVIEW_REASON=test→ exit 0 and line appended toREVIEW_OVERRIDES.log.~/.config/superpowers/worktrees/task/docs-sphinx-static-placeholder, where.gitis a file): same three cases, all correct. Error message shows the absolute<main>/.git/REVIEW_OK_<sha>path.git pushfrom the feature branch passed the hook (see "pre-push: review marker present — OK" in the push output that created this branch).No Fortran or Python code changed; no pytest run.
Reviewed by
superpowers:code-reviewer: 0 HIGH, 1 MED addressed in this branch (the cd+pwd absolute-path fix), 1 MED + 2 LOW non-blocking.🤖 Generated with Claude Code
Note
Medium Risk
Medium risk because it changes CI workflow/build ordering and introduces new Makefile/stub linkage paths for
totC-ABI tests, which can break builds across environments; the rest is largely documentation and tooling updates.Overview
CI now exercises
totend-to-end C-ABI and equivalence coverage. Thepython-testsworkflow is updated to runmake -C tot libs+tot_api_check_all(Layer 2/4 C drivers) and to generate Layer 1eqdatabaselines for bothdemo2014andht6m, while also expanding the workflow branch filters so the long-livedchore/pre-push-hook-worktree-compatbranch actually triggers Actions.Docs and tooling are reorganized and hardened. The Sphinx manual is migrated from the old
docs/sphinx/{en,ja}trees to a hybridportal/{en,ja}+modules/<mod>/{en,ja}structure with an expanded Makefile (HTML/PDF/linkcheck targets), shared Furo theme + MyST math support, and updated README; large amounts of legacy single-tree content are removed and replaced by per-module pages (notablyeq), plus a newsphinx-module-manualskill.Developer workflow improvements. The pre-push gate is made worktree-compatible (marker stored under
git --git-common-dir) andCLAUDE.mdnow requires an additional Codex review step;.gitignoreis extended for newtotC-ABI artifacts and external dependency clones, and the LaTeX manual gets a small parameter table correction.Reviewed by Cursor Bugbot for commit f0811de. Bugbot is set up for automated code reviews on this repo. Configure here.
Addendum: PNBTOT Registry Extension (commits 5c12dcb..f0811de)
This branch has accumulated additional work beyond the original
pre-push-hook scope:
24b1b12ftest+tr: backfill AJRFT in regression dump (PR tr+totlib: physical EXTERNAL_DRIVEN_I scalar (L-7b-i) #187 follow-up)d17f71ectest: backfill AJRFT in extractor unit-test fixture5c12dcb2feat(tr): register PNBTOT in tr_param_registry — C1d5b4da37test(tr): drive ITER01 fixture with PNBTOT=25 MW (NBI on) — C2 (atomic fixture+.in+baseline)b28a7f87docs(tr): wire PNBTOT through user-facing surfaces — C3 (en/ja tutorials, MCP, LaTeX)f0811de2docs(tr): restore paragraph break + fix list count after PNBTOT bullet removalSpec:
docs/superpowers/specs/2026-05-05-tr-pnbtot-registry-design.mdPlan:
docs/superpowers/plans/2026-05-06-tr-pnbtot-registry.mdDeferred review notes (not folded back into commit messages per CLAUDE.md no-amend rule)
C2 baseline-diff scope clarification: the commit body claims "Profile RT/AJ shifted across all 50 radial points × 11 timesteps". Empirically:
RTshifted (max Δ ≈ 4.6 keV near-axis at NR=1, scaling to ~0.006 keV at NR=50 — physically plausible for centered-Gaussian 25 MW NBI on a 100-step run).AJ,RN,QPare bit-identical.tr_regress.datcaptures one end-of-run snapshot, not 11 timesteps.Why AJ unchanged despite 2× temperature rise on-axis: current is prescribed by the
RIPS=2 → RIPE=7ramp; conductivity-T (j(T)) coupling is not in the active model under MDLNF=1 + MDLNB=1 + this 100-step horizon.C2 baseline regen environment (for future bisect reproducibility):
dcccf84fwith both upstream patches applied (docs/external-patches/bpsd/{bpsd-plasmaf-intent-out-init,bpsd-species-kid-oob-fix}.patch)Out-of-scope follow-ups (separate PRs)
tr_tst2baseline AJRFT backfill — depends on resolving the pre-existingeq_tst2~3e-9 physics drift first24b1b12f) — silent invisibility for the totlib L-7b-i pipelineeqdata.ITER01sotest_equivalence.py::test_iter01actually runs on CI (currently SKIPs, hiding the AJRFT-class drift discovered in this PR — perfeedback_equivalence_must_pass.mdinvisibility pattern)