Skip to content

Bootstraps: declared, bounded version probes instead of --version on every bin entry - #251

Merged
seathatflowsinourveins merged 3 commits into
mainfrom
claude/safe-version-probes-20260925
Sep 25, 2026
Merged

seathatflowsinourveins merged 3 commits into
mainfrom
claude/safe-version-probes-20260925

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator

Why

Both adoption bootstraps ended with "$installed_executable" --version on every file in $ECO_INSTALL_ROOT/bin. That call inherited the terminal's stdin, had no time bound, and never compared the output to the pin. Three binaries hang it:

  • MCP Inspector 2.x: has no version flag. Its launcher (byte-identical in 2.7.0 and 2.8.0) defaults to web mode and forwards argv, so mcp-inspector --version serves the web UI with --version as an ad-hoc stdio server. The user saw spawn --version ENOENT in the UI.
  • context-mode: starts its MCP stdio server on any argument other than its named subcommands and --help.
  • socraticode: has no argument parsing at all, so any invocation starts its MCP stdio server.

With an interactive stdin, the report therefore blocked. I reproduced all three hangs under a bounded timeout.

What

  • Declared probes: every pin in adoption/pins-*.json declares a version_probe, following the per-package practice of nixpkgs versionCheckHook and Homebrew test do.
    • exec probes run the pin's own command, with optional expect and match: minimum. Claude Code's pin is a floor.
    • npm-metadata probes read context-mode and socraticode through npm ls, so the package never runs.
    • Every exec probe was observed on the WSL host, including orx, tvly and agent-browser from their pinned artifacts.
  • Shared report step: byte-identical in both scripts and bash 3.2 safe.
    • Stdin comes from /dev/null and fd 9 is closed.
    • Each probe runs in its own process group, with TERM at 30 s and KILL 2 s later. Leftovers are killed after the probe exits.
    • Every bin/ entry is listed with its link target but never run.
    • installed-versions.txt is written, and the script exits 5 if a probe fails. cleanup() stops an interrupted probe and frees the lock.
  • Docs: adoption/bootstrap.md step 2, both platform pages (with changed after v2026.09.24.1 notes and a </dev/null workaround for released versions), the Inspector recipe row (read its version with npm ls) and both --help texts. render_config.py --verify closes stdin too.

How verified

  • Tests: adoption suites 340 pass (1 skipped), with a real bash 3.2.57 (BASH32_BINARY) and shellcheck 0.11.0 at style severity. New tests/test_adoption_version_probes.py covers:
    • a server that forks a child;
    • an EOF reader (the context-mode shape);
    • wrong, longer and prefixed versions, and escaping;
    • floor edge cases;
    • a stale npm version or missing prefix;
    • a live bin/ symlink that must not run;
    • lock-fd inheritance;
    • Ctrl-C releasing the lock;
    • no leftover processes in the run's session.
  • Mutations: all 15 regressions tried were caught, among them inherited stdin, no process group, no bound, a substring match, running the listing, a missing cleanup kill, and a TERM-stopped watchdog.
  • Real run: bootstrap-linux.sh --profile semantic-rag into a disposable root, with the host's mcp-inspector, context-mode and socraticode seeded into bin/ and stdin held open. It exited 0, verified 3 of 3 pins, ran none of the seeded executables, and left no listener.
  • Review: two rounds of independent adversarial review. All 19 findings that survived refutation are fixed.

Coordination

This touches the same files as #159 and #177, the Claude Code floor. The minimum match already treats claude-code as a floor. Whichever lands later merges main and re-registers hashes.

Lane: lane:foundation

🤖 Generated with Claude Code

…every bin entry

Both adoption bootstraps ended by running `--version` on every file in
$ECO_INSTALL_ROOT/bin with the terminal's stdin, no bound and no pin check.
MCP Inspector 2.x has no version flag (its launcher forwards argv to the web
UI, which spawns `--version` as a stdio server and stays up), and
context-mode and socraticode start their MCP stdio servers on unknown argv,
so an interactive bootstrap blocked there.

- Every pin in adoption/pins-*.json declares a version_probe: exec (command,
  args, optional expect and minimum match for the claude-code floor) or
  npm-metadata (context-mode, socraticode).
- A shared report step, byte-identical in both scripts and bash 3.2 safe,
  runs each probe with stdin from /dev/null, fd 9 closed, in its own process
  group under a 30 s bound, kills leftovers, lists every other bin entry
  without running it, writes installed-versions.txt and exits 5 on a failed
  probe. cleanup() stops an interrupted probe and frees the lock.
- Docs (bootstrap.md, platform pages, Inspector recipe row) and usage texts
  updated; render_config.py --verify closes stdin too.

Verified: adoption suites 340 pass (1 skipped) with real bash 3.2.57 and
shellcheck 0.11.0; 15+ mutations of the report step are caught; a real
bootstrap-linux.sh run into a disposable root with seeded mcp-inspector,
context-mode and socraticode and an open stdin exits 0 and runs none of them.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 25, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@seathatflowsinourveins
seathatflowsinourveins enabled auto-merge (squash) September 25, 2026 14:08
Scout and others added 2 commits September 25, 2026 10:27
…p 180 s)

Hosted macOS CI showed the bounded probe killing `llama-server --version`
at 30 s. main's own logs explain it: under the old unbounded loop the first
launch of the freshly downloaded llama-server took 31-39 s on hosted
macos-15 runners (run 36143844969) before printing
"version: 0.4.1-dev (build 11057, ...)"; the later verify step takes ~1 s.

A pin may now declare version_probe.timeout_seconds (an int in (30, 600]);
llama-cpp declares 180 with that evidence in its note. The default bound
stays 30 s for every other pin. Tests cover the declared bound (a probe that
answers after the default bound but within its own) and the schema.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…obed

install_pin now records a pin in installed_pin_ids before #177's early
return for a kept native launcher, so a Claude Code at or above its 2.1.281
floor is still checked by the version report (minimum match). Pins re-taken
from main (2.1.281) with the version_probe declarations re-applied; the
linux-wsl2 step keeps both changed-after notes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins merged commit 3f267b5 into main Sep 25, 2026
23 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/safe-version-probes-20260925 branch September 25, 2026 15:26
seathatflowsinourveins added a commit that referenced this pull request Sep 25, 2026
…tools; --pinned-versions coverage check (#289)

* Codex MCP servers at user scope so every worktree inherits them; --pinned-versions coverage check

Codex loads a project .codex/config.toml only from the project root it runs in,
and the main checkout's copy is untracked, so Codex workers in fresh worktrees
had none of the token-practice MCP servers (coverage check complete=false).
The Codex user template now carries serena, socraticode, ai-memory, headroom,
codebase-memory and qmd; the project template keeps only jcodemunch (#240); the
context-mode cwd override is dropped. adoption_status.py gains an opt-in
--pinned-versions that runs only a pin's declared "exec" version_probe (#251)
and never an npm-metadata one. Applied on the workstation with codex mcp add
and read back from a fresh worktree: complete false -> true, all six servers
start. Decision: docs/decisions/2026-09-25-codex-mcp-scope.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Repair: version matching mirrors the bootstrap exactly (bounded exact, numeric minimum)

The independent review of #289 found two bugs in the new version_output_matches: a non-numeric minimum floor (e.g. 2.0.0.dev0) raised and emptied the whole report, and exact was an unanchored substring test (2.10 matched 12.10.0). Both now follow adoption/bootstrap-linux.sh: exact is bounded by (^|[^0-9.])...([^0-9.]|$), and minimum compares numeric parts via a ported version_at_least that returns no-match on a non-numeric part. Regression tests for both, plus malformed pins files. The host coverage result is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant