Skip to content

Skip building an observation nothing below will read - #724

Open
v-positronic wants to merge 2 commits into
mainfrom
skip-unused-observation
Open

Skip building an observation nothing below will read#724
v-positronic wants to merge 2 commits into
mainfrom
skip-unused-observation

Conversation

@v-positronic

@v-positronic v-positronic commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

What

Session.reads_observation(obs, time_ns) says whether the call it precedes would read the observation it is given. A layer about to build an expensive one asks first, and hands on what it already has when the answer is no. ChunkedSchedule answers False while its own chunk plays; RemoteSession answers False while a round trip is in flight; the default is True.

Two layers act on it: TemporalStack skips sampling its window, and a codec skips encoding. Both still call inward — a session waiting on a function in flight is answered by being called, not by the observation it carries.

Why

A rig runs its control loop far faster than it re-queries a policy, so both layers were doing their most expensive work on every tick and the scheduling layer below was answering None without looking at either.

Measured on a real rollout (blind_20260908-135658, 663 chunks, from this repo's own spans): the history window is stacked 57 times per chunk for 1886 ms of CPU — on the thread that drives the arm — inside a chunk period of 3394 ms. 41 of those 57 samples run while the arm is playing, with no round trip open at all. The resize is 21 calls and 1261 ms, of which 17 fall inside the wait.

The two costs scale differently, which decides what this is worth. The resize scales with the round trip, so a faster server removes most of it anyway. The stack does not: it is a function of tick count alone and stays whatever the wire does. It is the floor a chunk period cannot go below, and it is the reason for the change.

Three decisions worth arguing with:

  • The query carries the observation, and a session judges it on the same field __call__ does. ChunkedSchedule makes one comparison, _obs_time(obs) < self._trajectory_end, in both places. Judging the gate on the caller's clock instead reads cheaper and is wrong wherever the two readings straddle the chunk's end: the gate declines, the call goes through, and the layer above hands the inner session a raw window. Nothing but the observation's own instant makes the two agree. A session that needs no field of it — RemoteSession, a codec — takes the argument and ignores it.
  • A delegating session keeps the default rather than forwarding. The answer is about the __call__ that gives it. A recording tap wraps another session and still reads every observation; a forwarded no would have let the layers above hand it something other than what went to the server, and nothing would have raised — the recording code accepts both stacked and unstacked frames. That failure is pinned by a test that fails against the forwarding version. Only the two layers that genuinely pass the observation through forward the question.
  • The schedule's guard is written twice, once in reads_observation and once in __call__. One predicate would have to answer before the call, which is the whole point of the query. test_the_gate_agrees_with_the_call and test_the_gate_reads_the_observation_instant_the_call_reads hold the two together, the second with the observation ahead of the caller's clock.

Verification

uv run pytest positronic pimm — 1488 passed, 8 skipped. pre-commit run clean on every touched file.

test_a_delegating_session_answers_for_itself was watched failing against the forwarding default and passing after it.

Refs

Positronic-Robotics/internal#1168.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 566fc5da7b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread positronic/policy/layers.py Outdated
A rig runs its control loop far faster than it re-queries a policy, and two layers were doing their
most expensive work on every tick regardless. `TemporalStack` stacked a whole history window per
call, and a codec resized every frame of it, and the scheduling layer below then answered `None`
without looking at either.

Measured on a real rollout: the window is stacked 57 times per chunk for 1886 ms of CPU, on the
thread that drives the arm, in a chunk period of 3394 ms. 41 of those samples run while the arm is
playing, with no round trip open at all.

`Session.reads_observation(time_ns)` says whether a call would read what it is given. A scheduling
layer answers False while its own chunk plays; a remote session answers False while a round trip is
in flight. The call happens either way — a session waiting on a function in flight is answered by
being called, not by the observation it carries — so a layer told no hands on what it already has.

The default is True and a delegating session keeps it, because the answer is about the `__call__`
that gives it: a recording tap wraps another session and still reads every observation, and a
forwarded no would have let the layers above hand it something other than what went to the server.
The two layers that forward are the two that genuinely pass the observation through.

It reads the caller's clock rather than the observation, because a served pipeline runs these layers
over observations the harness never stamped. That clock runs at or past the observation's, so the
gate can only answer True where the call then answers None: a window built and unused, never one
skipped and wanted.

Ticket: Positronic-Robotics/internal#1168 #refs
`ChunkedSchedule` judged expiry at the observation's own instant in the call and at the
caller's clock in the gate. Where the two straddle the chunk's end — an observation ahead
of the clock, which the schedule's own test permits — the gate declined and the call went
through, so the layer above handed the inner session a raw, unstacked observation.

The query now takes what the call takes, and the schedule makes the same comparison in
both. A test pins the case where the two readings differ.

Ticket: none - a review finding on an unmerged branch
@v-positronic
v-positronic force-pushed the skip-unused-observation branch from 50320b9 to baf7416 Compare September 8, 2026 20:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baf741665d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +122 to 124
reads = self._inner.reads_observation(obs, time_ns)
encoded = self._codec.encode(obs) if reads else obs
action = self._inner(encoded, time_ns)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Query inner gates with the observation they receive

Rule hidden-dependency violated:
_CodecSession queries the inner session with raw obs, but when the answer is true it calls that session with codec.encode(obs). For any codec that transforms a field consulted by an inner gate, the gate therefore decides for a different observation than __call__ receives, contrary to the new Session contract; this silently depends on every inner gate ignoring every encoded field. Query with the value that will actually be passed, or redesign the gate so its decision inputs are explicit and invariant across codecs.

AGENTS.md reference: AGENTS.md:L7-L8

Useful? React with 👍 / 👎.

Sign up for free to 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.

1 participant