Repository navigation
fix(remote): resolve the workspace root, name the GitHub surface, stop the board guessing - #289
Merged
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.shand five Python entrypoints each defaulted it to a hardcoded path under$HOME. On a developer box that is right; in a remote session$HOMEis/rootwhile the checkouts are under the session's working directory.None of them crashed — they are all written to degrade — so
pyauto-brain boardprinted a plausible board with hollow sections at exit 0, and the community leg reportedbody map not foundfor a file present one directory up from the script reading it.bin/_pyauto_root.shandagents/_pyauto_root.pyare now the one shell and one Python resolver, and they agree: explicitPYAUTO_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.
ghis not installed, but the skills are built on it19 skill bodies and 15 scripts drive
gh. A remote session has none; its access is themcp__github__*tools. Each mobile run rediscovered this by loading the procedure, running the firstgh, and re-deriving the rest through MCP —/prmalone loads ~8k tokens of close-out, 21 of whoseghcalls are dead there.skills/GITHUB_ACCESS.mdmaps every operation onto its MCP tool and names the one thing MCP cannot do (delete a branch)./prmno longer claims "every step is gh" on a surface that has none.bin/_gh.shgives scriptshave_gh/require_gh, so a missingghis stated with its alternative instead of leaving an empty command substitution to be read as a real answer.agents/conductors/release/nightly.shhad 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 nogh. The headline read clear to work in brightgreen while thirteen legs were unread.unreadable).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.ymlchecks PyAutoBrain out pinned tomain, so the narrowed allowlist is red against the current main and green once this lands. Reproduced locally before opening: 7 mismatches againstmain, 0 against this branch.Validation
test_workspace_root.py,test_gh_surface.py,test_board_degraded.py).unreadable;branch_sweep's refusal must now name the alternative.readlinkdependency introduced into a script the suite deliberately runs with a lean PATH. Both fixed.ruffclean 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