mergedhumanPR #271Other
fix(path): fail resolution on a malformed local index
One of nine independent PRs from a correctness audit of main at df0b5f1. Each merges cleanly with the others in any order; together they pass 908 tests locally on Python 3.11 and 3.14 (CI covers 3.11 and 3.12).
Description
The pitch.
One of nine independent PRs from a correctness audit of `main` at `df0b5f1`. Each merges cleanly with the others in any order; together they pass 908 tests locally on Python 3.11 and 3.14 (CI covers 3.11 and 3.12).
Commits: `68596c0`
### Problem
A malformed local index fixture crashed `test-agent --index` with a traceback
and wrote no evidence. That covered:
- a list at the top level or under `agents`;
- a string entry;
- a non-string url;
- a file that isn't UTF-8;
- JSON nested past the recursion limit.
A non-string `card_digest` also crashed `evaluate_path`, and would crash
`verify` replay.
### Change
`_resolve` checks:
- the top level;
- `agents`;
- the entry;
- `url`, which must be a non-empty string;
- `card_digest`, which must be a non-empty string.
It treats undecodable files as unreadable. A violation records the existing
`resolution_failed` event, with a short reason that doesn't echo the value.
### Compatibility
- Well-formed indexes are unchanged.
- The evaluator, profiles and versions are unchanged.
- An empty `card_digest` used to silently disable the pin; it is now refused.
### Verification
- Full suite on the branch: 828 passed.
- The malformed shapes run through `run_path_test` and the CLI. Each gives
resolution Failed, later stages not tested, and a bundle that verifies. They
fail on `df0b5f1`.
- A real CLI run against a non-UTF-8 index exits 1 with no traceback.
### Also fixed: unusable endpoint URLs and blank agent names (commits `a4e84c9`, `dcb8039`, `8cabd1c`, `28f25ff`)
**Problem.** Some endpoint URLs were never usable, but passed resolution
anyway, from either `--url` or an index entry:
- blank or padded strings;
- `file://` or scheme-less strings;
- ports outside 1–65535.
They were then charged to the agent's `agent_card_retrieval` stage, although it
was the operator's locator that was wrong. A URL that httpx rejects outright,
such as one with a trailing newline, crashed `test-agent` with a traceback,
also on plain `--url`. A whitespace-only `--url` or `--agent-name` produced a
bundle that `nandatown verify` rejected ("run participant 1 has no valid
name").
**Fix.** `_resolve` requires an absolute http(s) URL that httpx can parse, with
a host, a port of 1–65535 or none, and no whitespace. Anything else records the
existing `resolution_failed` event, with a reason that doesn't echo the value.
A blank locator is recorded as subject `"?"`, so the bundle verifies and can
get a receipt.
A blank, whitespace-only or missing agent name now fails resolution before the
index is read. Previously, an index that listed a whitespace-only name could
produce a signed passing receipt whose subject was `?`. Non-blank names behave
exactly as before.
`28f25ff` adds three smaller fixes:
- **Blank or non-string subjects.** Resolution events now name such a subject
`?`. A non-string name or URL passed to the library fails resolution with a
verifiable bundle; before, it crashed.
- **UTF-8 index.** The local index is read as UTF-8, so a non-ASCII agent name
resolves on any locale. A non-UTF-8 index is still reported as unreadable.
- **Merge-order-independent tests.** The blank-name tests use whitespace-only
names, which #265 does not refuse. They pass whether or not #265's refusal of
empty names lands first.
**Unchanged.** Any host is still accepted (loopback, LAN and remote), and every
usable locator is recorded exactly as before. `evaluate_path`, profiles and
evaluator versions are unchanged.
**Verification.**
- Parametrized invalid URLs for `--url` and for index entries, plus blank
locators, fail before these commits and pass after.
- Full suite: 898 passed.
- A real CLI run of `test-agent --url $'http://127.0.0.1:9\n'` now reports
"First broken stage: resolution" with no traceback, and the bundle verifies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/271/head:pr-271
git checkout pr-271