Skip to content

feat(spawn): GrokMembrane — local grok CLI as a first-class pinned cell#76

Merged
darw007d merged 3 commits into
mainfrom
claude/lab/grok-membrane
Jun 16, 2026
Merged

feat(spawn): GrokMembrane — local grok CLI as a first-class pinned cell#76
darw007d merged 3 commits into
mainfrom
claude/lab/grok-membrane

Conversation

@darw007d

Copy link
Copy Markdown
Collaborator

Why

swarph_shared added grok to VALID_PROVIDERS and grok-researcher.yaml was authored referencing a GrokMembrane / _grok_env / _build_grok_argv — but the membrane class was never landed. The import-time MEMBRANESVALID_PROVIDERS lockstep guard then fired on every import, which bricked every swarph spawn (lab included), not just grok:

RuntimeError: MEMBRANES ['antigravity', 'claude', 'codex'] out of sync with
VALID_PROVIDERS ['antigravity', 'claude', 'codex', 'grok'] — add the missing provider membrane.

This was issue #75's tripwire, now actually fired. Fix = land the membrane so the grok cell path is first-class alongside claude/codex/antigravity.

What

  • GrokMembrane(uses_pinned_session=True) — reads the R5 pin store like claude and --resumes grok's native cwd-keyed session, preserving the cell's continuity + identity + mesh inbox across respawns. (Fresh-session-per-spawn would orphan grok's running identity — continuity is the whole point of a persistent cell.)
  • _build_grok_argvgrok --cwd <cwd> [--resume <id>] [--agent <role>] --always-approve + passthrough. Matches the validated manual launch.
  • _grok_env — isolated HOME (.grok-cell inside cwd) with the operator's ~/.grok/auth.json symlinked in for $0 OIDC; explicit metered-xAI leak-key pop on top of the canonical billing scrub.
  • pre_launch reuses _launch_via_tmux so swarph spawn grok-researcher lands the cell in its OWN named tmux session (never a window of another cell's session), matching the claude cell + the tmux_session in cell.yaml.

Two fixes over the field draft that unblocked this live

  • pop MESH_GATEWAY_TOKEN (the per-peer auth cutover): _TOKEN is not a scrub suffix, so without the explicit pop the cell rides the operator's shared token instead of its symlinked per-peer file — the mint≠cutover gap.
  • no --system-prompt-override: grok's interactive mode has no such flag (real flags are --prompt-file / -p, single-turn). Identity comes from --agent + grok's own durable memory. The draft's flag was a latent crash for any starter-bearing grok cell.

Verify

Dry-run against the live cell reproduces the validated launch exactly:

grok --cwd /home/ubuntu/grok-researcher --resume 15f1f31c-...-c48403 --agent grok-researcher --always-approve
  • 8 new tests (lockstep + pinned membrane; pinned / no-session / sibling-slot argv; always_approve opt-out; env isolation + scrub + token-cutover; auth symlink; dry-run pinned --resume).
  • Full spawn suite: 97 passing.

Out of scope / follow-ups

  • Version bump + PyPI release (publish is commander-gated).
  • Re-pointing lab's live editable install off the /tmp/swarph-cli-pr65 scratch checkout (currently 0.12.0) onto canonical main after merge — fixes the version skew + scratch-dir fragility.

🤖 Generated with Claude Code

darw007d and others added 3 commits June 16, 2026 08:31
…cell

`swarph_shared` added `grok` to VALID_PROVIDERS and `grok-researcher.yaml`
was authored referencing a GrokMembrane / _grok_env / _build_grok_argv —
but the membrane class was never landed. The import-time MEMBRANES↔
VALID_PROVIDERS lockstep guard correctly fired, which bricked EVERY
`swarph spawn` (lab included), not just grok.

Add the membrane so the grok CELL path is first-class alongside
claude/codex/antigravity:

- GrokMembrane(uses_pinned_session=True): reads the R5 pin store like
  claude and `--resume`s grok's native cwd-keyed session, preserving the
  cell's continuity + identity + mesh inbox across respawns (a
  fresh-session-per-spawn would orphan grok's running identity).
- _build_grok_argv → `grok --cwd <cwd> [--resume <id>] [--agent <role>]
  --always-approve` + passthrough. Matches the validated manual launch.
- _grok_env: isolated HOME (.grok-cell inside cwd) with the operator's
  ~/.grok/auth.json symlinked in for $0 OIDC; explicit metered-xAI leak-key
  pop on top of the canonical scrub.
- pre_launch reuses _launch_via_tmux so `swarph spawn grok-researcher`
  lands the cell in its OWN named tmux session (never a window of another
  cell's session), matching the claude cell + the cell.yaml tmux_session.

Two fixes over the field draft that unblocked this live:
- pop MESH_GATEWAY_TOKEN (the per-peer auth CUTOVER): `_TOKEN` is not a
  scrub suffix, so without the explicit pop the cell rides the operator's
  shared token instead of its symlinked per-peer file — the mint≠cutover gap.
- no `--system-prompt-override`: grok's interactive mode has no such flag
  (real flags are --prompt-file / -p, single-turn); identity comes from
  --agent + grok's own durable memory. The draft's flag was a latent crash
  for any starter-bearing grok cell.

Tests: 8 new (lockstep+pinned-membrane, pinned/no-session/sibling-slot
argv, always_approve opt-out, env isolation+scrub+token-cutover, auth
symlink, dry-run pinned --resume). Full spawn suite 97 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t ==)

The lockstep guard used strict equality, so shipping GrokMembrane while the
installed swarph_shared has NOT yet whitelisted 'grok' crashed the import
(MEMBRANES has grok, VALID_PROVIDERS doesn't). My membrane tests masked this:
they passed only against a swarph_shared carrying a stray local 'grok' edit;
against the released (no-grok) shared the import raised.

The guard's real purpose is to prevent a raw KeyError in run_spawn — i.e.
every VALID provider must have a membrane (subset), NOT that the two sets are
identical. An EXTRA membrane ahead of the shared whitelist is harmless:
load_cell still gates which providers are spawnable. Relaxing to subset lets
this package release independently of swarph_shared and degrades gracefully
(grok cells are rejected cleanly by load_cell as "queued for a future
release" until swarph_shared whitelists 'grok', rather than a hard import
crash). Test asserts the subset invariant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GrokMembrane (grok cell spawn path) requires swarph_shared to whitelist
'grok' in VALID_PROVIDERS (released in swarph-shared 0.3.3). Bump the
dependency pin so installs/CI resolve a shared that accepts grok cells, and
bump swarph-cli to 0.13.0 (new provider membrane = feature).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@darw007d darw007d merged commit 598546c into main Jun 16, 2026
1 of 2 checks passed
@darw007d darw007d deleted the claude/lab/grok-membrane branch June 16, 2026 08:55
darw007d added a commit that referenced this pull request Jun 16, 2026
… real grok) (#77)

* fix(spawn): GrokMembrane 0.13.1 — drop's seat-A blockers, grounded on real grok 0.2.54

Addresses the seat-A review of #76 (which merged + released 0.13.0 under
commander expedite ~1min before the verdict landed). Every fix re-derived
from the REAL grok 0.2.54 binary (strace + --help + env-symbol probe), not
help-text reasoning — the source of the original misses.

B2 (auth, BLOCKER): grok reads $HOME/.grok/auth.json; the symlink was placed
  one level too high at $HOME/auth.json → every fresh cell came up logged-OUT.
  strace-verified positive control. Now symlink at <HOME>/.grok/auth.json.
B3 (genesis-resume, BLOCKER): grok --resume REQUIRES a pre-existing session
  (claude --session-id MINTS); passing a swarph-minted UUID errored "Session
  does not exist" on first spawn. Now: grok owns its sessions — --continue when
  one exists for the cwd, nothing on genesis (grok mints). uses_pinned_session
  → False; grok moves to the fresh-session dispatch branch.
H1 (Windows launch): GrokMembrane.launch was unconditional execve — the exact
  pane-collapse pattern claude.launch fixed in 0.12.1. Added the win32
  subprocess.run / POSIX execve split.
H2 (token cutover): stop popping MESH_GATEWAY_TOKEN — the membrane never placed
  the per-peer token file it promised, so popping muted a fresh cell on the
  mesh. Match claude/codex/agy (inherit the token); cutover is a separate
  explicit feature.
H3 (scrub vars): _GROK_EXTRA_LEAK_KEYS was partly FICTIONAL (XAI_API_HOST etc
  aren't real grok vars). Replaced with the real redirect surface incl the
  sharp GROK_AUTH_PROVIDER_COMMAND / GROK_ASKPASS / GROK_GATEWAY_URL /
  GROK_OIDC_ISSUER.
H4 (sandbox): grok cell was the only sibling both auto-approve AND unconfined.
  Default --sandbox workspace (real profile; off/workspace/devbox/read-only/
  strict), independent of --always-approve; set sandbox: off to disable.
M1/M2 (identity): --agent is grok's PROFILE selector (unknown role silently
  ignored), and grok DOES have --system-prompt-override (earlier claim was
  wrong). Starter now carried via --system-prompt-override; grok added to the
  assisted-memory CURRENT_TASK re-injection branch.
M3 (symlink robustness): is_symlink + readlink validation replaces a stale/
  dangling/foreign link instead of silently keeping it.

10 grok tests rewritten to the corrected design + a Windows launch-split test;
full suite 840 passing against the REAL published swarph_shared 0.3.3 (clean
venv, no local-grok-edit contamination — the false-green class that masked B1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(spawn): GrokMembrane 0.13.1 r2 — drop's re-review must-fixes

BLOCKER — double --system-prompt-override: _build_grok_argv emits it for the
  starter AND the assisted-memory restore appended a SECOND one for
  CURRENT_TASK → grok's clap rejects a repeated flag → cell never launches
  (fires on a grok cell with starter_prompt_path + assisted_memory.enabled,
  reached on the normal tmux respawn). The M2 fix copied claude's repeated-
  --append-system-prompt (which concatenates) without grok's reject-on-repeat
  delta. FIX: CURRENT_TASK now injected via --rules (distinct flag, appends to
  system prompt); starter keeps --system-prompt-override. + regression test.

HIGH — GROK_HOME / GROK_AUTH_PATH unscrubbed: grok honors GROK_HOME OVER $HOME
  (strace-verified by reviewer), so an inherited one silently bypassed the
  ENTIRE isolated-HOME scheme (cell reads operator auth + writes sessions back
  to the shared dir). The enumerated leak-key list missed them. FIX: replace
  enumeration with DENY-BY-DEFAULT over the whole GROK_*/XAI_* namespace
  (allowlist empty) — closes the redirect class (GROK_HOME/AUTH_PATH/AUTH,
  GROK_MANAGED_CONFIG_URL, GROK_OAUTH2_*, *_URL family) instead of whack-a-mole.
  + namespace-scrub test.

MEDIUM — sandbox opt-out doc was a no-op: docstrings said extra.sandbox but the
  code reads top-level cell.sandbox. Docs → top-level `sandbox: off`. Also
  corrected the profile comment (devbox is a custom example, not a built-in;
  real built-ins off/workspace/read-only/strict) and recorded the empirical
  sandbox finding (workspace keeps the mesh net; strict/read-only mute it).

Reviewer empirically confirmed --sandbox workspace permits the cell mesh
(Landlock restrict_network=false) so the default stays workspace. Full suite
842 passing in a clean venv on real swarph_shared 0.3.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(spawn): grok sandbox profile list + delete orphaned leak-key docstring

drop's 3 sign-off doc nits (none blocked release): (1) devbox IS a reserved
built-in profile — revert the r2 'custom example' comment (her own mis-steer,
corrected); (2) delete the orphaned enumerated _GROK_EXTRA_LEAK_KEYS docstring
left fused above the deny-by-default block; (3) soften the strict/read-only
'would mute' claim to kernel-dependent (Landlock V4+/full seccomp).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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