Skip to content

fix(coganchor): keep an agent's own runtime local, and read a command as a command - #32

Merged
futrime merged 6 commits into
mainfrom
fix/coganchor-agent-runtime
Aug 30, 2026
Merged

fix(coganchor): keep an agent's own runtime local, and read a command as a command#32
futrime merged 6 commits into
mainfrom
fix/coganchor-agent-runtime

Conversation

@futrime

@futrime futrime commented Aug 30, 2026

Copy link
Copy Markdown
Member

Two coganchor defects that only show up with a real agent on a real target, plus the rig
that found them.

What was wrong

  • An argv entry was read at PATH_MAX. A command is not a path, and the kernel lets
    one be MAX_ARG_STRLEN (32 pages). A truncated command is worse than an unread one: what
    reaches the target is a prefix, which runs and means something else. Codex prefixes
    every shell command with a multi-kilobyte preamble, so this was its ordinary case.
  • An npm agent's interpreter went to the target. #!/usr/bin/env node makes env walk
    PATH one execve at a time. The first candidate does not exist here, so it was not the
    agent's own by name and was sent to the target — where the name does resolve, and the
    agent itself ended up running there, reading the target's copy of its state directory and
    unable to reach the account it was signed in with. The whole search is now claimed, so a
    candidate that fails here fails with the ENOENT that makes env try the next.
  • A program inside the agent's own state directory went to the target. grok installs its
    native binary under ~/.grok/bin and re-execs it. Those directories were already answered
    from this machine as paths; this makes the same claim about executing them.

The rig

bench/coganchor-concurrency/ — real hmz, real coganchor interception, a stand-in model
provider and a mock controlled end, climbing the concurrency ladder until something stops
behaving. RESULTS.md has the numbers.

Merge notes

  • main added the cursor profile (twelve known CLIs) while this branch rewrote the same two
    paragraphs of the remote-execution guide and reference. Kept main's roster and this
    branch's wording.
  • The rig's three scripts had never been through ruff checkbench/ does not exist on
    main. Fixed rather than ignored, except INP001/T201, which are structural to a script
    run by path, as for docs/tapes/stage.py.

Verification

  • uv run pre-commit run --all-files — green (ruff check, ruff format, pyright strict).
  • uv run pytest — full suite, locally.
  • The three bench scripts, which no test covers, checked against their pre-edit versions:
    summarise.py byte-identical over the committed ladders, and standin_model.py identical
    across all three wire protocols.

🤖 Generated with Claude Code

futrime and others added 6 commits August 29, 2026 16:32
Every entry of an intercepted argv was read with PATH_MAX as its ceiling. An argv entry
is not a path: the kernel allows MAX_ARG_STRLEN, and a shell command is routinely longer
than four kilobytes.

Codex prefixes every `bash -lc` it runs with a preamble well past that, so what reached
the target was the first 4096 bytes of the command -- which parses, runs, and means
something else. It failed as a syntax error inside a truncated brace, and Codex went on
reporting each tool call as done while nothing at all happened on the target.

Truncating is worse than not reading: a prefix of a command is a different command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two ways the agent itself ended up running on the target, which is the one thing the
anchor exists to prevent.

An `#!/usr/bin/env node` line kept only the `env` here. `env` then searches PATH for the
interpreter, one execve per directory, and the first candidate names a path that does not
exist here -- not the agent's own by name, so it was sent to the target, where the name
resolves. Kimi's whole agent process ran there: it read the target's HOME, found none of
its own configuration, and reported the model it was started with as unconfigured. So the
whole search is claimed now rather than the one directory the interpreter is really in; a
candidate kept here fails here, with the ENOENT that makes `env` try the next.

And a program the agent keeps inside its own state directory was answered from this
machine as a path but not as a program. grok installs its native binary under `~/.grok/bin`
and re-execs it, so grok too ran on the target, where it reported `Not signed in`.

Codex was spared both only because its runtime is listed by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`docs/features/concurrency` says how wide a flow runs is a question about the machine and
that nothing caps it. This measures the machine: real hmz, real coganchor, the five agent
CLIs as installed, against a mock controlled end and a stand-in model provider that scripts
the same three-command turn for every backend. Both stand-ins run outside the cgroup, since
in production neither is part of the budget being sized, and the rig records the target's
own CPU so a run can say whether the ceiling it found was its own.

In 16 logical CPUs and 64 GiB: 16-24 agents run at full speed, 32-128 give the most turns
per hour, and everything is still correct into the hundreds -- 640 for claude and 192 for
kimi, both stopped by memory; 256 for codex, stopped by its own app-server; grok and dsh
never stopped inside the rig's 832 slots.

The first ceiling anybody meets is neither: hmz holds about three descriptors per
concurrent agent, so a stock 1024 soft RLIMIT_NOFILE stops every backend at about 320.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`command -v kimi` answers nothing in a shell that has not been given
`~/.local/agents/bin`, and `dirname ""` is `.`, so the rig went looking for `./kimi` and
reported the provider import as failed. $AGENT_BIN says where they are outright, the home
default stands in, and a missing one is now said plainly rather than three lines later.

Also ignores the `lab/` the rig writes beside itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main added the `cursor` profile — twelve known CLIs — while this branch rewrote
the same two paragraphs of `docs/guide/remote-execution.md` and
`docs/reference/remote-execution.md` for the `env` interpreter search and for
programs an agent runs from inside its own state directory. Kept main's roster
and this branch's wording in both.
The rig's three scripts had never been through `ruff check`: `bench/` does not
exist on main, so nothing linted them where they were written.

Fixed rather than ignored, because each was a real thing to say:

- `standin_model.py` picked its final text with `FINAL if ... else FINAL`, a
  conditional whose test was the enclosing `if` and whose arms were the same
  value. Inlined.
- `_fill` narrowed a schema onto the loop variable it came from, so the declared
  property and the narrowed dict were one name. Now two.
- `ramp.py` swapped `sys.stdout` for a file by hand and put it back at the far
  end of the body. `contextlib.redirect_stdout` is what the standard library has
  for that, and it puts stdout back however the block ends.
- `%d` formatting, a `try`/`except`/`pass`, and `open()` where the module already
  holds a `Path`.
- Docstrings on the three public names that had none.

`INP001` and `T201` are ignored for `bench/**` instead: a script run by path
inside the measured cgroup has nothing for an `__init__.py` to do, and what a
measurement writes, it prints. Same reason `docs/tapes/stage.py` is already
listed.
Copilot AI lite review requested due to automatic review settings August 30, 2026 01:24

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@futrime
futrime merged commit 38dd666 into main Aug 30, 2026
6 checks passed
@futrime
futrime deleted the fix/coganchor-agent-runtime branch August 30, 2026 01:37
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.

2 participants