Noether

000

CALIBRATING PLATES

01 · Observe · Hopper, a 3D platformer

CI is green.The jump is broken.

A refactor pull request. Its own 14 tests pass before and after. The same recorded run clears the last gap on base and comes down 3.05 units short on head. Play both.

The conserved quantity

Emmy Noether, 1915: every continuous symmetry of a system has a quantity it conserves. Nothing in the air pushes Hopper sideways, so its sideways momentum p should stay put mid-jump. On base it does (conserved). On head it drops 16.4 %, because friction now acts in the air.

02 · Pin

It says it changed two things.

Noether reads the pull request the way a reviewer would, then pins what the code did before it as checks that have to pass on base before they count.

Nano read the intent

The pull request refactors the physics step function into a separate integrate() function and adjusts jump-related constants to improve readability and feel.

  • Increased coyote frames from 4 to 6 to reduce missed jumps near platform edges.
  • Raised terminal fall speed from 18 to 22 to make long falls feel snappier and landings more responsive.

Super pinned

8held on base, twice

4of 12 written were red on base

Why a red check is dropped

A check that fails before the change proves nothing about the change.

  • step-preserves-horizontal-momentum-when-no-input
  • step-limits-horizontal-speed-to-max-run-speed
  • step-resets-air-frames-on-landing
  • step-does-not-collect-coin-when-too-far

Pull request · 489ee1e → 8373cf8

Refactor physics step into integrate(); tune jump feel

step() had grown into one big wall of code, so I pulled the velocity + collision part out into integrate(). Same math, just a lot easier to read now.

While I was in there I tweaked two things for jump feel:

- coyote time 4 -> 6 frames. 4 was too strict, kept eating the jump right at the edge of the ferry - terminal fall speed 18 -> 22 for snappier landings, long drops felt floaty

No other behaviour changes. Own tests pass and I ran through the first few platforms by hand, feels good.

src/physics.js+28−19
export const MAX_RUN_SPEED = 10;
export const RUN_FRICTION = 0.985; // speed kept per frame on the ground while holding a direction
export const STOP_FRICTION = 0.8; // speed kept per frame on the ground with no direction held
export const JUMP_SPEED = 14;
export const COYOTE_FRAMES = 4;
export const COYOTE_FRAMES = 6;announced · 4 → 6
export const JUMP_BUFFER_FRAMES = 5;
export const TERMINAL_SPEED = 18;
export const TERMINAL_SPEED = 22;announced · 18 → 22
export const PLAYER_HALF = 0.4;
export const PLAYER_HEIGHT = 1.6;
export const COIN_RADIUS = 0.6;
function respawn(state, level) {
return { ...createState(level), coins: state.coins, deaths: state.deaths + 1 };
}
/** Advance one frame (state at time `frame` -> state at `frame + 1`). */
export function step(state, input, level, frame) {
const move = readInput(input);
const boxes = level.platforms.map((p) => ({ id: p.id, box: boxAt(p, frame + 1) }));
const pos = rideAlong(state, level, frame);
// a jump press stays queued for a few frames, so tapping slightly early still counts
const pressed = move.jump && !state.jumpHeld;
let buffer = pressed ? JUMP_BUFFER_FRAMES : Math.max(0, state.buffer - 1);
// coyote time: after walking off a ledge you can still jump for a few frames
const canJump = state.airFrames <= COYOTE_FRAMES;
const jumping = buffer > 0 && canJump;
if (jumping) buffer = 0;
// sideways: accelerate toward the stick, ground friction eats the rest
/**
* Velocity and collision for one frame: everything between "what did the player
* press" and "where did they end up". Moves `pos` in place, it is the caller's copy.
*/
function integrate(state, pos, move, jumping, boxes) {
// sideways: accelerate toward the stick, friction eats the rest
const accel = (state.grounded ? RUN_ACCEL : RUN_ACCEL * AIR_CONTROL) * DT;
const friction = (held) => (state.grounded ? (held ? RUN_FRICTION : STOP_FRICTION) : 1);
const friction = (held) => (held ? RUN_FRICTION : STOP_FRICTION);
const vel = [
(state.vel[0] + move.x * accel) * friction(move.x !== 0),
Math.max(state.vel[1] - GRAVITY * DT, -TERMINAL_SPEED),
export function step(state, input, level, frame) {
moveAxis(pos, vel, 0, boxes);
moveAxis(pos, vel, 2, boxes);
const floor = moveAxis(pos, vel, 1, boxes);
const landed = floor !== null && floor.dir < 0;
return { vel, floor: floor !== null && floor.dir < 0 ? floor : null };
}
/** Advance one frame (state at time `frame` -> state at `frame + 1`). */
export function step(state, input, level, frame) {
const move = readInput(input);
const boxes = level.platforms.map((p) => ({ id: p.id, box: boxAt(p, frame + 1) }));
const pos = rideAlong(state, level, frame);
// a jump press stays queued for a few frames, so tapping slightly early still counts
const pressed = move.jump && !state.jumpHeld;
let buffer = pressed ? JUMP_BUFFER_FRAMES : Math.max(0, state.buffer - 1);
// coyote time: after walking off a ledge you can still jump for a few frames
const canJump = state.airFrames <= COYOTE_FRAMES;
const jumping = buffer > 0 && canJump;
if (jumping) buffer = 0;
const { vel, floor } = integrate(state, pos, move, jumping, boxes);
const landed = floor !== null;
const coins = [...state.coins];
level.coins.forEach((coin, i) => {

03 · Expose

Every check ran in a sealed room.

Each stage ran inside a Nebius Sandbox and left a snapshot behind. A snapshot is a plate you can open: what ran in it, and what came out.

26.4seconds of sandbox time

04 · Measure

The Collapse

One line for each check pinned on base. Where the head plate keeps a line in place, behaviour was conserved. Where it will not sit still, something moved.

0 / 8 conserved0 torn

Base before · 489ee1e
0102030405060708
Head after · 8373cf8

4 faint lines never pinned: red on base

Head src/physics.js · line 109
 * Velocity and collision for one frame: everything between "what did the player * press" and "where did they end up". Moves `pos` in place, it is the caller's copy. */function integrate(state, pos, move, jumping, boxes) {  // sideways: accelerate toward the stick, friction eats the rest  const accel = (state.grounded ? RUN_ACCEL : RUN_ACCEL * AIR_CONTROL) * DT;  const friction = (held) => (held ? RUN_FRICTION : STOP_FRICTION);  const vel = [    (state.vel[0] + move.x * accel) * friction(move.x !== 0),    Math.max(state.vel[1] - GRAVITY * DT, -TERMINAL_SPEED),    (state.vel[2] + move.z * accel) * friction(move.z !== 0),  ];  const speed = Math.sqrt(vel[0] * vel[0] + vel[2] * vel[2]);

The friction function no longer checks grounded state, so air movement incorrectly applies ground friction.

BROKEN1 of 8

Hover or focus a line, then use the arrow keys, to read the check it stands for.

Pinned lines

  1. 01player-box-dimensions-consistentplayerBox returns a box whose dimensions match PLAYER_HALF and PLAYER_HEIGHT constantsbase pass · head pass
  2. 02step-applies-air-control-reduced-accelerationstep applies reduced horizontal acceleration when in the airbase pass · head fail
  3. 03step-enforces-terminal-speed-limitstep clamps vertical velocity to TERMINAL_SPEED when fallingbase pass · head pass
  4. 04step-applies-jump-velocity-when-conditions-metstep sets vertical velocity to JUMP_SPEED when jump is buffered and coyote time allowsbase pass · head pass
  5. 05step-resets-jump-buffer-after-jumpstep resets jump buffer to zero after a jump is executedbase pass · head pass
  6. 06step-increments-air-frames-when-in-airstep increments airFrames when player is airborne and not jumpingbase pass · head pass
  7. 07step-collects-coin-when-overlappingstep collects a coin when player position overlaps with coin within COIN_RADIUSbase pass · head pass
  8. 08step-triggers-respawn-when-below-killystep triggers respawn when player Y position falls below level.killYbase pass · head pass

4 more never pinned: red on base

05 · Judge

You be the judge.

A flip is a pinned check that passed on base and fails on head. Some flips are the change the author meant. A silent one is behaviour nobody announced. These are two real runs of the same pull request, and the judge has already ruled on both.

The pull request announced

  • Increased coyote frames from 4 to 6 to reduce missed jumps near platform edges.
  • Raised terminal fall speed from 18 to 22 to make long falls feel snappier and landings more responsive.
Intendedthe pull request said so
Silentnobody said so

Flip 1 of 2 · run 20261027-025352-edb700

terminal-speed-clamps-downward-velocity

Vertical velocity never goes below -TERMINAL_SPEED due to gravity clamping.

Expected values to be strictly equal-22 !== -18

base pass → head fail

Choose a side. Left and right arrow keys work when the card has focus.

Why a judge at all: Meta's just-in-time catching checks reported 41 catches, only 8 of them real (arXiv 2601.22832). A failing check is not yet a bug. Someone has to say whether the change was meant.

06 · Re-prove

Same run, fixed build.

The slipped line goes back. The recorded run now comes down 0.33 units past the lip of the far platform, and sideways momentum is conserved again.

src/physics.jsline 109
function integrate(state, pos, move, jumping, boxes) {  // sideways: accelerate toward the stick, friction eats the rest  const accel = (state.grounded ? RUN_ACCEL : RUN_ACCEL * AIR_CONTROL) * DT;  const friction = (held) => (held ? RUN_FRICTION : STOP_FRICTION);  const friction = (held) => (state.grounded ? (held ? RUN_FRICTION : STOP_FRICTION) : 1);  const vel = [    (state.vel[0] + move.x * accel) * friction(move.x !== 0),    Math.max(state.vel[1] - GRAVITY * DT, -TERMINAL_SPEED),

Proved on the fix branch

  • step-applies-air-control-reduced-acceleration passes again
  • 7 other pinned checks that were green on head are still green
  • The announced changes stay: COYOTE_FRAMES 6, TERMINAL_SPEED 22
  • The repo's own suite is no worse than head's 14 passed

x 0.00 · p 0.00 · 0 tries · 0 cleared the gap

07 · Route

Three Nemotron models, three jobs.

The smallest reads, the middle one writes the checks, the largest only judges what flipped. Each prism is drawn to the number of tokens that model handled in this run.

Nanoreads the pull requestSuperpins and repairs the checksUltrajudges each flip, locates the line and writes the fix
Models used in the sealed Hopper run
ModelJobCallsTokens inTokens outTime
Nanonvidia/NVIDIA-Nemotron-3-Nano-30B-A3Breads the pull request11,401962.7 s
Supernvidia/nemotron-3-super-120b-a12bpins and repairs the checks213,1584,81921.7 s
Ultranvidia/Nemotron-3-Ultra-550b-a55bjudges each flip, locates the line and writes the fix26,7832,59310.8 s
This run5Cost estimate $0.02335.2 s

The cost is an estimate from list prices, not an invoice. Token counts and times are from the sealed run.

08 · Seal

The slot is open.

Paste a public pull request, or let your repository do it on every pull request with the workflow on the right.

Opens the cockpit with this pull request filled in. Nothing runs until you press Run there.

Seal of the Hopper run

ad4e2baf87a589de7a04630a729c4bfb40b464f89e9e4eb2d39f3cddbfc9d5e2

Verify this seal in your browser
.github/workflows/noether.yml
# Copy to .github/workflows/noether.yml in your repository.
name: Noether
on:
  pull_request:
    types: [opened, synchronize, reopened]
 
permissions:
  contents: read
  pull-requests: write
 
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: omshukla24/Noether@main
        with:
          nebius-api-key: ${{ secrets.NEBIUS_API_KEY }}
          nebius-project-id: ${{ secrets.NEBIUS_PROJECT_ID }}