Skip to content

Harden venue, catalog, cart, and checkout contracts - #33

Merged
mekedron merged 9 commits into
mekedron:mainfrom
saneruby:agent/mcp-audit-hardening-clean
Jul 26, 2026
Merged

Harden venue, catalog, cart, and checkout contracts#33
mekedron merged 9 commits into
mekedron:mainfrom
saneruby:agent/mcp-audit-hardening-clean

Conversation

@saneruby

@saneruby saneruby commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR closes the repository-owned defects confirmed by a full CLI/MCP audit:

  • resolve venues consistently by exact name, slug, object ID, and Wolt URL without relying exclusively on discovery results
  • expose canonical venue identity and distinguish immediate, scheduled, scheduled-only, closed, and address-delivery states
  • support the advertised delivery and fee sort aliases with typed invalid-sort errors
  • load partial grocery assortments through deterministic category, search, and item hydration instead of reporting false successful empty menus
  • normalize item venue context, price/currency, category, upstream language variants, availability, units, weight, balance, and primary images
  • coordinate credential refresh and persistence safely across processes while preserving typed upstream error classes
  • preserve basket identity and contents while validating mutations, unavailable items, venue references, and requested checkout delivery modes
  • return fees, promotion data, and canonical venue identity when upstream provides them
  • keep complete typed MCP output in structuredContent and a short summary in content, with an explicit legacy duplication opt-in
  • cancel Chrome DevTools Protocol work without racing concurrent WebSocket reads or writes

The implementation is split into nine logical, English-only commits. Tests and live fixtures are not tied to Georgia, a named venue, a named item, store hours, or a particular time of day.

Audit acceptance coverage

  1. delivery and fee work as advertised sort aliases; invalid values return the allowed set.
  2. Venue detail and hours accept slug, ID, and URL and avoid the retired API path.
  3. Closed and scheduled-only behavior is driven by upstream state and covered by deterministic fixtures plus dynamic live discovery.
  4. Partial grocery catalogs no longer appear as successful complete-empty menus.
  5. Search documentation recommends the Wolt profile language.
  6. Names and translations are passed through only when Wolt supplies them.
  7. Item search/detail consistently preserve known venue and currency context.
  8. Unit, weight-step, and purchasable_balance semantics, including zero and null, are documented and tested.
  9. Checkout accepts every venue reference form declared by its schema; cart listing retains the all-carts default and adds an optional venue filter.
  10. A requested delivery mode is selected or a typed WOLT_DELIVERY_MODE_UNAVAILABLE result lists valid alternatives.
  11. Checkout returns structured unavailable items without discarding the basket.
  12. Expired auth, rate limiting, temporary upstream failures, refresh failures, unsupported endpoints, and HTTP 410 are classified separately.
  13. Full JSON duplication is no longer unconditional; legacy content-only behavior is an explicit opt-in.
  14. The primary image remains present in the normal item response. A separate field-selection mode remains an optional future enhancement rather than being required for the response-contract fix.

Safety and compatibility

Catalog and availability checks fail closed when upstream state cannot be verified. Any present non-null disabled_info disables an item, including unfamiliar payload shapes. Current option definitions win equal-quality merges, while richer fallback data is retained when current data is incomplete.

The live smoke test discovers a currently usable venue/category/item dynamically, adds exactly one unit that was absent from the basket, validates checkout behavior, removes only that unit, and verifies restoration. It never calls cart clear.

Content-only MCP clients can explicitly request the legacy full JSON duplicate with _meta["wolt/duplicate_content"] = true; the default keeps the full typed payload only in structuredContent.

Validation

Validated on exact commit fc3792819481086d090d0e92d2b6ea3899e9f7c8:

  • go build ./...
  • go vet ./...
  • go test -count=1 ./...
  • go test -race -count=1 ./...
  • go test -race -count=10 ./internal/cli ./internal/config ./internal/mcpserver ./scripts/mcp-checkout-smoke ./scripts/mcp-venue-smoke
  • golangci-lint v2.4.0 run — 0 issues
  • config tests cross-compiled for Plan 9/amd64, AIX/ppc64, and JS/Wasm
  • bash -n, ShellCheck 0.9.0, workflow YAML parsing, and git diff --check
  • scripts/build-local.ps1; both binaries report v2.2.1-10-gfc37928-local
  • official dynamic live smoke: 21 passed, 0 failed, 0 skipped, including MCP cart add/checkout/remove and exact restoration
  • dynamic URL resolution probe across ten candidates and two slug/ID/URL currency/identity rounds
  • duplicate scan: unchanged from upstream at threshold 100 and improved from four groups to three at threshold 150
  • independent final review: clean for correctness, security, concurrency, portability, duplication/bloat, test fragility, English-only content, and PR artifacts

The race detector initially exposed a real cancellation race between Gorilla WebSocket deadline mutation and an in-flight JSON write. Cancellation now uses Conn.Close, which Gorilla permits concurrently with reads and writes; focused repeated tests and the full race suite pass.

Known boundaries

Wolt venue hours, items, dynamic catalog payloads, and delivery modes are live upstream data, so regression tests discover fixtures dynamically and deterministic contract tests use upstream-shaped data. Cross-process atomicity against a simultaneous write from another application cannot be guaranteed without an upstream basket revision or conditional-write API; operations are serialized within this process and preserve unrelated baskets.

Windows validation required MinGW-w64 for Go's native race detector and an ASCII temporary drive mapping for golangci-lint because the local checkout path contains non-ASCII characters. These are local environment workarounds and are not included in this PR.

@saneruby

saneruby commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

The audit fixes are consolidated into nine logical commits. The PR description contains the complete acceptance mapping, validation results, and known boundaries.

@saneruby

saneruby commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Update complete: the branch is rebased onto v2.2.1 (47715f1) and validated at fc37928. The full unit, race, lint, build, portable cross-compile, and dynamic live-smoke suites pass; the PR remains a draft for maintainer review.

@saneruby
saneruby force-pushed the agent/mcp-audit-hardening-clean branch from 273f166 to fc37928 Compare July 26, 2026 14:17
@saneruby
saneruby marked this pull request as ready for review July 26, 2026 14:24
@mekedron

Copy link
Copy Markdown
Owner

Thank you — this is excellent work. Reviewed and merging with all commits preserved.

@mekedron
mekedron merged commit d1c19ea into mekedron:main Jul 26, 2026
4 checks passed
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