Doctoral Research · Space Robotics Inspection with a Free-Flying Space Manipulator
A Doctoral Research Journal Aerospace Engineering

CHAIN_10 T1 — H1/H2 code-path audit (cheap kills before statistics)

Date: Jun 12, 2026. Criteria: tasks/chain10_criteria.md (pre-registered). Method: read-only evidence; every claim carries its file:line.

Question 1 — who consumes the finite-differenced tangent (dx_ds)?

The FD tangent is built at GNC/guidance/orbit_path.py:48 (and the resmooth site, :53):

self.dx_ds = np.gradient(self.x_surf, self.s_samples, axis=0)      # (N,3) arclength derivative

It leaves the class only through eval() (orbit_path.py:56-59), which returns (x, dx).

eval has exactly ONE call site in all of GNC (rg -n "\.eval\(" GNC):

GNC/guidance/ee_guidance.py:620:        x_surf, _ = self.orbit_path.eval(s_aim)

The tangent return is bound to _discarded. The anchor-mode branch added in Phase 00 T4 uses orbit.sample_analytic (the COM helix), not OrbitPath.eval, so it introduces no second consumer.

The planned consumers do not exist. parameters.yaml carries ff_route: none # none|A|B|AB analytic feedforward (consumed in Task 4/5) — but rg -n "ff_route" GNC returns nothing: no code reads the knob in any mode. The “Task 4/5” analytic-feedforward routes were never built. (Housekeeping ALERT for the user: ff_route is a dead config key whose comment promises consumers that don’t exist — candidate for removal or a # Legacy/dormant annotation; not this chain’s edit.)

Verdict vs the pre-registered condition

Criteria text (verbatim): H1 killed if the code audit shows dx inert in the adopted config, OR tangent ripple at aim points is uncorrelated with peak locations (|Spearman r| < 0.3).”

H1 KILLED by code audit (inert consumer) — and stronger than the condition anticipated: the FD tangent is discarded at its only call site and its planned consumers were never built, so it is inert in every config, not just the adopted one. No noise in dx_ds can reach any pose, command, or score.

Per the plan, the harness’s fd_ripple driver is now documentation-only (computed and tabulated, clearly labeled non-causal). Scope note: this audit kills the tangent hypothesis as stated; the surface path x_surf itself (ray-cast, savgol-smoothed, linearly interpolated) remains in the causal chain through its position values — that geometry-quality channel is covered by the T4 curvature/geometry drivers, not by H1.

Question 2 — does surface curvature enter pose construction? (H2 scope)

rg -n "curvature" GNC finds curvature consumed ONLY from the COM orbit generator (utils/curvature helpers; orbit.curvaturekappa_window in GNC/base_guidance.py, kappa_vec in GNC/com_guidance.py, kappa_cd in GNC/guidance/guidance_rollout.py). The surface path’s curvature is never computed or read anywhere in GNC.

Consequence for H2 (recorded as the plan expects): there is no direct code path from target-surface curvature to a pose command. If target geometry drives κ peaks, the route must be indirect — x_surf position → camera pose → arm reach demand — which is exactly what the T4 correlator tests (path_curv / reach drivers). H2 stays alive for the correlator, with its causal route now pinned to the indirect channel.

Context note (not a new hypothesis): the COM-orbit curvature DOES feed the COM feedforward (kappa_vec, com_guidance.py), and the helix’s curvature varies pole-to-pole — so the schedule changes when the vehicle traverses high-curvature latitudes. Adjacent to H3’s phase-alignment story; the reach/phase drivers in T4 will see it if it matters.

Audit summary

Question Finding Effect
FD tangent consumers one call site, discarded (ee_guidance.py:620); ff_route dead key H1 KILLED (all configs)
Surface curvature in pose path none — indirect channel only H2 alive, route pinned for T4