fix(errors): say-what-you-know on version CONFLICT + cause-specific hints#8
Merged
Merged
Conversation
…ints
Resolves two clean-room dogfood findings (2026-06-29) that violated the
functional-honesty invariant "an error must say what it knows" — a hint that
does not point at the real fix misdirects and burns agent probe-cycles.
Finding A — a version CONFLICT disclosed neither the expected nor the actual
version, forcing agents to probe. Both optimistic-concurrency guards now disclose
both versions in the message, in structured details, and in a hint naming the
real recovery:
- _require_current_version (requirement version): message + details
{expected_version, current_version} + "Retry with --expected-version {cur}."
- update_draft (draft_revision — the sibling guard, identical defect): message +
details {expected_draft_revision, current_draft_revision} +
"Retry with --expected-draft-revision {cur}."
Finding B — _error() hardcoded "Refresh local Plainweave state and retry." on
every error, so e.g. a missing --actor VALIDATION inherited a stale-state hint
that misdirects. _error is now _error(code, message, *, hint=None, details=None),
resolving an omitted hint from a per-ErrorCode honest default map (VALIDATION /
NOT_FOUND never carry stale-state phrasing; CONFLICT's default is a legitimate
refetch-and-retry). The contract that every error carries a non-empty hint and a
dict details (assert_error, contract fixtures) is preserved. Precise hints added
where the generic default would still misdirect: missing-actor, verification
method / evidence-status, trace-relation-not-canonical. The duplicate blanket
hardcode at mcp_surface.py (INTERNAL preflight-severity) now names the allowed
values and states refreshing state will not help.
Additive only: the weft.plainweave.error.v1 envelope and the ErrorCode enum are
unchanged (errors.py / envelopes.py untouched). No allow/block/verdict tokens.
Verified: make ci green (400 passed, 91.32% cov, ruff + mypy-strict clean);
wardline scan . --fail-on ERROR exit 0; live `uv run plainweave` repro of all
three conflict/validation scenarios shows disclosed versions + precise hints.
Anti-vacuous regression tests assert the actual current value appears in message,
details, and hint (they fail against the old code) plus a guard that no service
VALIDATION path carries the blanket hint.
Closes dogfood findings A + B (built via an ultracode sweep -> implement ->
adversarial-verify workflow).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Resolves two clean-room dogfood findings (2026-06-29) against Plainweave's functional-honesty invariant — an error must say what it knows; a hint that doesn't point at the real fix misdirects and burns agent probe-cycles.
Finding A — say-what-you-know on version CONFLICT
A version mismatch reported
"expected version does not match current version"with emptydetailsand the blanket hint — the agent had to probe (try 0, try 2) to learn the real version. Both optimistic-concurrency guards now disclose both versions inmessage, in structureddetails, and in a hint that names the real recovery:_require_current_version(requirement version) →messagenames both,details: {expected_version, current_version},hint: "Retry with --expected-version {current}."update_draft(draft_revision, the sibling guard with the identical defect — extended for symmetry;--expected-draft-revisionis a real flag) → same treatment.Finding B — cause-specific hints; kill the blanket "refresh state"
_error()hardcoded"Refresh local Plainweave state and retry."on every error, so a missing--actorVALIDATION inherited a stale-state hint that sent agents down a dead path._erroris now(code, message, *, hint=None, details=None), resolving an omitted hint from a per-ErrorCodehonest default map (VALIDATION/NOT_FOUND never carry stale-state phrasing; CONFLICT's default is a legitimate refetch-and-retry). Precise hints where the generic default would still misdirect: missing-actor, verification method / evidence-status, trace-relation-not-canonical. The duplicate hardcode atmcp_surface.py(INTERNAL preflight-severity) now names the allowed values.The existing contract — every error carries a non-empty hint and a dict
details(assert_error, contract fixtures) — is preserved, which is why "no hint" was rejected in favour of the honest default map.Scope & safety
weft.plainweave.error.v1envelope and theErrorCodeenum unchanged (errors.py/envelopes.pyuntouched). No allow/block/verdict tokens._error, so they ship as one coherent PR (splitting would force an artificial dependency stack).make cigreen (400 passed, 91.32% cov, ruff + mypy-strict clean);wardline scan . --fail-on ERRORexit 0; liveuv run plainweaverepro of all three scenarios shows disclosed versions + precise hints. Anti-vacuous regression tests assert the actual current value appears in message, details, and hint (they fail against the old code), plus a guard that no service VALIDATION path carries the blanket hint.Built via an ultracode sweep → implement → adversarial-verify (4 lenses) workflow; all lenses passed.
🤖 Generated with Claude Code