doctor: resolve the harness credential check on the same AdapterHost as the agent preset check (#1015) - #1018
Open
maxie-agent wants to merge 1 commit into
Conversation
…as the agent preset check (MakePrisms#1015) `maxplayer doctor` contradicted itself in a single run, on every run, on both seats and both versions, including seats that then worked fine: PASS agent preset — registry resolves: codex (preferred argv0=codex-acp) WARN harness credential permissions — harness registry did not resolve (fix: fix the agent preset check first) The remedy named a check that had just passed, so the operator had nothing to act on. Root cause: the two checks are documented to be one `seller_agents::resolve` on one config, but they were NOT given the same inputs. `build_checks` derives `AdapterHost::for_sandbox(sandbox)` and hands it to `check_agent_registry`; `check_harness_credential_permissions` hardcoded `AdapterHost::Host` and never received it. Under `[sandbox] mode = "docker"` those differ: `Container` keeps argv0 bare for the image's PATH and never consults the host, while `Host` requires the adapter on THIS machine's PATH. A docker seat bakes its adapters into the sandbox image and installs nothing host-side, so the preset check resolved and passed while the credential check took the Err arm — on precisely the seats that were working. Threading the host through is only half of it. Under docker the host `$HOME` credential directory is not the surface in use at all: the job container inherits nothing, and the credential arrives through the daemon's environment or the MakePrisms#647 proxy (docs/SELLER-QUICKSTART.md, "An environment credential does not cross the container boundary"). Inspecting host paths for a docker seat and reporting PASS would be MakePrisms#715's inspected-the-wrong-directory-and-passed defect arrived at from the container side, so the docker arm reports what it is and measures nothing. - `check_harness_credential_permissions` takes `host: AdapterHost` and resolves with it; `build_checks` passes the same value the preset check gets. - New `Status::Skip` (`SKIP`) for not applicable / not verified: non-blocking like `Warn`, distinct from `Pass` in the report, and rendered `(why: …)` rather than `(fix: …)` because a skip has nothing to repair. Counted separately from warnings in both summary lines. - Docker seats skip with a line that states it makes no claim about the credential the jobs actually use, and does not cover the host files `[sandbox.codex_chatgpt]` / `[[sandbox.file_credentials]]` read. - The Err arm carries the real resolver error instead of the circular remedy. - Real findings are untouched: group/world-writable directories and `settings.json` still WARN with their remedy, and an unlinked harness still WARNs even when an unresolvable label rides along with it. Regression tests (each red on the base): - `docker_seat_does_not_get_a_pass_and_a_did_not_resolve_warn_in_one_run` — the reported defect, reproduced on a built-in preset so the real resolver runs. - `both_checks_agree_about_whether_the_registry_resolved` — the invariant, over both hosts. - `a_docker_seat_does_not_inspect_host_credential_directories_but_a_host_seat_still_does` — one 0777 directory, two hosts, two individually correct verdicts; red in both directions. - `a_skip_is_reported_distinctly_and_blocks_nothing` — SKIP is a reporting distinction, not a severity.
|
@maxie-agent is attempting to deploy a commit to the MakePrisms Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Fixes #1015.
The defect
maxplayer doctorcontradicted itself in a single run — on every run, both seats, both versions, including seats that then worked fine:The remedy pointed at a check that had just passed, so the operator had nothing to act on.
Root cause
The issue posed the question as "either the two checks pass different inputs to
resolve, or the Host-side resolve fails for a docker-mode seat for a reason the message hides". It is the first one.The two checks are documented to be one
seller_agents::resolveon one config, but they were not given the same inputs:build_checks(doctor.rs:2383) deriveslet agent_host = AdapterHost::for_sandbox(sandbox.as_ref())and hands it tocheck_agent_registry.check_harness_credential_permissions(doctor.rs:1983on the base) hardcodedAdapterHost::Hostand never received that value.Those differ under
[sandbox] mode = "docker", which is exactly whatAdapterHostexists to distinguish:Containerkeeps argv0 bare for the image's PATH and never consults the host PATH — the sandbox Dockerfile bakes the adapters in, so there is nothing to probe.Hostresolves argv0 against this machine's PATH and fails fast when it is absent.A docker seat bakes its adapters into the image and installs nothing host-side. So the preset check resolved via
Containerand PASSed, while the credential check resolved viaHost, gotRegistryError::AllFailed, and WARNed — on precisely the seats that were working. That is the every-run, both-seats, both-versions signature in the field report.Why threading the host through is only half the fix
Under docker the host
$HOMEcredential directory is not the surface in use at all. The job container inherits nothing from the host; the credential arrives through the daemon's own environment or the #647 credential proxy —docs/SELLER-QUICKSTART.md, "An environment credential does not cross the container boundary".So simply passing
agent_hostin would have made a docker seat inspect host paths its container cannot read and report PASS on them. That is #715's inspected-the-wrong-directory-and-passed defect, arrived at from the container side. The docker arm therefore reports what it is and measures nothing.Changes
check_harness_credential_permissionstakeshost: AdapterHostand resolves with it.build_checkspasses the same value the preset check gets, so the contradiction cannot recur by construction.New
Status::Skip(SKIP) for not applicable / not verified. Non-blocking exactly likeWarn—exit_codeandreadiness_okstill gate onFailalone — but distinct fromPassin the report, and rendered(why: …)rather than(fix: …), because a skip has nothing to repair. Counted separately from warnings in both summary lines.This is the "skip cleanly" the issue asked for, done without letting a skip read as a clean bill of health: a check that inspected nothing has not passed, and saying
PASSwould be the doctor: permission checks stop at MAXPLAYER_HOME and never look at the harness credential directory #715 failure mode in reverse.Docker seats skip with a line that states plainly that it makes no claim about the credential the jobs actually use, and that it does not inspect the host files
[sandbox.codex_chatgpt]and[[sandbox.file_credentials]]read when those are configured. Those are real host-side credential surfaces under docker; this check does not cover them and does not pretend to.The resolve-failure arm carries the real resolver error. With identical inputs this arm now implies the preset check failed too and has already told the operator what to repair, so the message never again cross-references a check that passed.
What is deliberately unchanged
Real credential-permission findings are preserved exactly:
settings.jsonstill WARN with theirchmod go-wremedy;Tests
Five regression tests, each red on the base:
docker_seat_does_not_get_a_pass_and_a_did_not_resolve_warn_in_one_run[agents]override so the real resolver runs; deterministic on CI, whereclaude-agent-acpis absent.both_checks_agree_about_whether_the_registry_resolvedAdapterHostvalues.a_docker_seat_does_not_inspect_host_credential_directories_but_a_host_seat_still_does~/.claude, two hosts, two individually correct verdicts. Red in both directions: inspect host paths under docker and the skip assertions go red; silence the check for everyone and theHostWARN goes red.a_skip_is_reported_distinctly_and_blocks_nothingSKIPis a reporting distinction, not a severity — visible in output, no effect on exit code or the boot gate.harness_credential_check_absent_directory_is_warn_not_silent_skipWarn, with a comment on why it must not be swept intoSkip.Verified with
cargo test -p maxplayer --no-default-features --features wallet,acp: 204 unit + 19 integration tests pass, 0 failures; the doctor module is 85 passed / 0 failed.Scope
Touches one file,
crates/maxplayer/src/doctor.rs. No overlap with #1013 (probe naming) or #996 (sandbox). Does not revisit #689, and does not address #770 (credential check PASSing on a still-writable surface), which is a different defect in the same check.