Skip to content

fix(remote): resolve the workspace root, name the GitHub surface, stop the board guessing - #289

Merged
Jammy2211 merged 2 commits into
mainfrom
claude/mobile-performance-review-5qwv8r
Aug 26, 2026
Merged

Jammy2211 merged 2 commits into
mainfrom
claude/mobile-performance-review-5qwv8r

Conversation

@Jammy2211

Copy link
Copy Markdown
Contributor

Three environment assumptions written on a developer box, each of which failed silently rather than loudly in a Claude Code remote session. Found by reviewing why recent mobile tasks were slow; every claim below was measured in a live session, not inferred.

1. The workspace root resolved into a directory that does not exist

agents/_common.sh and five Python entrypoints each defaulted it to a hardcoded path under $HOME. On a developer box that is right; in a remote session $HOME is /root while the checkouts are under the session's working directory.

None of them crashed — they are all written to degrade — so pyauto-brain board printed a plausible board with hollow sections at exit 0, and the community leg reported body map not found for a file present one directory up from the script reading it.

bin/_pyauto_root.sh and agents/_pyauto_root.py are now the one shell and one Python resolver, and they agree: explicit PYAUTO_ROOT, else beside this checkout, else the parent anyway (reported as unverified). Neither names an absolute path — a literal is only ever right for the machine it was written on, which is the defect itself. The dev-box rule turned out redundant as well as wrong: there the organs are side by side, so the derive-from-checkout rule already covers it.

2. gh is not installed, but the skills are built on it

19 skill bodies and 15 scripts drive gh. A remote session has none; its access is the mcp__github__* tools. Each mobile run rediscovered this by loading the procedure, running the first gh, and re-deriving the rest through MCP — /prm alone loads ~8k tokens of close-out, 21 of whose gh calls are dead there.

  • skills/GITHUB_ACCESS.md maps every operation onto its MCP tool and names the one thing MCP cannot do (delete a branch).
  • The gh-driving skills point at it; /prm no longer claims "every step is gh" on a surface that has none.
  • bin/_gh.sh gives scripts have_gh / require_gh, so a missing gh is stated with its alternative instead of leaving an empty command substitution to be read as a real answer.
  • agents/conductors/release/nightly.sh had no check at all — it would have failed partway through a release.

3. The board answered questions it could not ask

Seven workflows reported no runs — a claim about the world — from a process with no gh. The headline read clear to work in brightgreen while thirteen legs were unread.

  • Rows distinguish asked, nothing from could not ask (unreadable).
  • A 403-on-CONNECT is named as an egress-policy block rather than an unreachable host, since retrying never helps.
  • A sibling checkout answers when its published copy is blocked.
  • The render carries a degraded banner above the sections, a qualified headline, and a grey badge. Green is only ever emitted by a render that read everything and found nothing wrong.

Verified in practice: allowing the Pages host made the readiness leg live and it immediately surfaced a Heart verdict RED · 45 that the old board had been rendering as "clear to work".

Tenant firewall

Seven allowlist entries are dropped or narrowed in the companion PyAutoMind PR, not added. Each named the owner only because it hardcoded a workspace root under $HOME; those consumers now delegate to the resolvers, which name no absolute path. The two new resolvers carry no entry and must not need one.

The firewall check caught an earlier draft of this change doing exactly what it forbids — that is why the allowlist shrank instead of growing.

Merge order

This PR must merge before PyAutoLabs/PyAutoMind's companion PR. Mind's firewall_gate.yml checks PyAutoBrain out pinned to main, so the narrowed allowlist is red against the current main and green once this lands. Reproduced locally before opening: 7 mismatches against main, 0 against this branch.

Validation

  • 539 tests pass (24 new across test_workspace_root.py, test_gh_surface.py, test_board_degraded.py).
  • Every new guard was verified to fail against the pre-fix tree, not assumed to.
  • Two existing contracts updated deliberately: the overnight row schema gained unreadable; branch_sweep's refusal must now name the alternative.
  • Two existing tests caught real defects in this change while it was being written — a gh-driving page it had missed, and a readlink dependency introduced into a script the suite deliberately runs with a lean PATH. Both fixed.
  • ruff clean on every file touched. Two pre-existing findings in untouched code (E741, E713) left alone rather than widening the diff.

🤖 Generated with Claude Code

https://claude.ai/code/session_012PDoeCuecjutdnwkxsTqg7


Generated by Claude Code

claude added 2 commits August 26, 2026 13:40
…p the board guessing

Three environment assumptions written on a developer box, each of which failed
silently rather than loudly in a Claude Code remote session.

1. The workspace root. `agents/_common.sh` and five Python entrypoints each
   defaulted it to a hardcoded path under `$HOME`. On a developer box that is
   right; in a remote session `$HOME` is `/root` while the checkouts are under
   the session's working directory, so every one of them resolved into a
   directory that does not exist. None crashed — they are all written to
   degrade — so `pyauto-brain board` printed a plausible board with hollow
   sections at exit 0, and the community leg reported "body map not found" for
   a file present one directory up from the script reading it.

   `bin/_pyauto_root.sh` and `agents/_pyauto_root.py` are now the one shell and
   one Python resolver, and they agree: an explicit `PYAUTO_ROOT`, else beside
   this checkout, else the parent anyway, reported as unverified. Neither names
   an absolute path — a literal here is only ever right for the machine it was
   written on, which is the whole defect. The dev-box rule was redundant as
   well as wrong: there the organs *are* side by side, so rule 2 already covers
   it.

2. `gh`. The skills spell their GitHub steps as `gh` commands — 19 bodies and
   15 scripts. A remote session has no `gh` at all; its access is the
   `mcp__github__*` tools. Each mobile run rediscovered this by loading the
   procedure, running the first `gh`, and re-deriving the rest through MCP.
   `skills/GITHUB_ACCESS.md` maps every operation onto its MCP tool and names
   the one thing MCP cannot do (delete a branch); the gh-driving skills point
   at it, and `/prm` no longer claims "every step is gh" on a surface that has
   none. `bin/_gh.sh` gives the scripts `have_gh`/`require_gh`, so a missing
   `gh` is stated with its alternative instead of leaving an empty command
   substitution to be read as a real answer. `nightly.sh` had no check at all.

3. The board. Where it could not ask, it answered anyway: seven workflows
   reported "no runs" — a claim about the world — from a process with no `gh`,
   and the headline read "clear to work" in brightgreen while thirteen legs
   were unread. Rows now distinguish "asked, nothing" from "could not ask"; a
   403-on-CONNECT is named as an egress-policy block rather than an unreachable
   host, since retrying never helps; a sibling checkout answers when its
   published copy is blocked; the render carries a degraded banner above the
   sections, a qualified headline, and a grey badge. Green is only ever emitted
   by a render that read everything and found nothing wrong.

`board/AGENTS.md` records what a remote render can and cannot reach, and which
hosts the environment's network policy needs for a complete one.

Tests: 24 new across three files, plus two existing contracts updated (the
overnight row schema gained `unreadable`; branch_sweep's refusal now has to
name the alternative). The guard tests fail against the previous tree, and one
of them caught a gh-driving page this change had missed.

539 PyAutoBrain tests pass. `ruff` clean on every file touched; the two
pre-existing findings in untouched code are left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PDoeCuecjutdnwkxsTqg7
`objects.githubusercontent.com` was named as a host to add; it is already in
the default Trusted list and was never blocked here. The host that is actually
refused for CI artifacts is Azure Blob Storage
(`productionresultssa*.blob.core.windows.net`) — verified against the proxy's
own record of refused CONNECTs, which lists exactly two hosts: the Pages board
and that one.

Also replaces the invented settings path with the real one (the environment
selector at claude.ai/code, Network access → Custom), notes the
include-defaults checkbox whose absence would silently drop PyPI and GitHub,
and records that the allowlist is per environment with no organization-level
equivalent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PDoeCuecjutdnwkxsTqg7
@Jammy2211
Jammy2211 merged commit 640eb27 into main Aug 26, 2026
2 checks passed
@github-actions
github-actions Bot deleted the claude/mobile-performance-review-5qwv8r branch August 26, 2026 20:06
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