Skip to content

Third corrections: exposure to a price means being SHOWN that price, and one DID is one subject - #33

Open
github-actions[bot] wants to merge 2 commits into
mainfrom
ship/index-fixes3
Open

Third corrections: exposure to a price means being SHOWN that price, and one DID is one subject#33
github-actions[bot] wants to merge 2 commits into
mainfrom
ship/index-fixes3

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Machine-operated ship branch ship/index-fixes3.

The ship workflow dispatches the full ci matrix, waits for it, merges only the certified combined state with main, then runs the deployment-aware release gate against the merged SHA — with automatic certified revert on a red gate. See .github/workflows/ship.yml.

AgentTanuki added 2 commits July 31, 2026 13:30
…istaken for money, experiments were promoted by unrelated revenue

Five system-level defects found by independent review of 79265a9. Every one
passed its own unit tests; each was only visible end to end.

1. A2A DEEP-PREFLIGHT PAYMENT WAS BROKEN END TO END. _preq_from_task always
   rebuilt payments.check_request(capability) and handle_payment_submission
   always returned store.check(...), so a deep-preflight challenge settled
   the WRONG canonical operation and returned a capability shortlist to
   someone who had paid for an endpoint check. The task now persists the
   exact operation and its canonical params at quote time; reconstruction is
   table-driven through payments.request_from_stored, reads ONLY our own
   stored fields (a submission cannot steer settlement onto another
   operation) and refuses an unknown operation instead of defaulting.
   Settlement dispatches to the product actually bought and records a
   deep_preflight_run with full settlement metadata and the actor quoted.
   Covered by a payment-required -> submitted -> completed test, not just
   challenge/card assertions.

2. settlement_mode == "x402" IS NOT PROOF OF MONEY. The same rail defaults to
   Base Sepolia, where a successful settlement is a successful payment of
   nothing, and a facilitator claiming success is not a chain receipt.
   HTTP/MCP/A2A now record confirmed, mainnet, network, amount and tx from
   auth.settled.record, and revenue requires ALL of: x402 + confirmed +
   mainnet + genuine-external attribution. Testnet and unconfirmed
   settlements are reported separately as supporting and can never promote.

3. EXPERIMENTS WERE NOT INDEPENDENTLY ATTRIBUTABLE. Any global primary metric
   moving could promote any experiment, so unrelated escrow revenue - or a
   watch sold for a different offer - would "prove" a deep_preflight price
   change worked. Each experiment is now bound to its operation; exposure,
   revenue, payers and repeat use are computed from that operation's events
   only, and revenue is summed from settled amounts rather than a global
   total. Also enforced ONE applied change per cycle GLOBALLY, not per
   experiment: two prices moving at once makes both results uninterpretable,
   which is the whole point of "one independently measurable change at a
   time".

4. THE A2A CARD STILL OPENED WITH PASSPORT ISSUANCE. It now leads with the
   endpoint decision and the measured numbers; the passport follows as
   supporting, with its register snippet intact so the offer funnel stage is
   not silently dropped. The existing copy-contract test was updated to the
   new ordering rather than deleted.

5. REMOTE INGEST WAS STILL DORMANT. A bounded local-only cycle is green in
   production, so the documented, capped MCP Registry adapter is now cleared
   and on by default, with GUILD_INDEX_INGEST=0 retained as a
   one-config-change kill switch. Sources we will NOT ingest are named with
   their exact gate rather than silently omitted: a2a_registry (the A2A
   discovery spec states it prescribes no registry API, so any endpoint we
   used would be undocumented - enabling it would mean scraping) and
   x402_bazaar (bulk automated reading is not covered by a documented public
   API contract, and a trade-listing surface is exactly where an unclear
   permission should be resolved before reading at machine speed).
   Published at /admin/index/cycle -> bounds and indexsources.active_sources.

Also re-applies the index autorun default and POST /admin/index/cycle, which
were lost when the previous correction branch was re-cut from main.

Tests: 1113 passed, 9 skipped (15 new).
…and one DID is one subject

Two more defects where a docstring promised something the code did not do,
and the gap flattered us both times.

1. FREE USE WAS COUNTED AS EXPOSURE TO A PAID PRICE.
   qualified_exposure(store, "deep_preflight") counted free preflight_run
   events. A free-preflight caller has never been quoted the deep-preflight
   price, so the engine could reach its denominator on people who were never
   offered anything and then halve or kill that price. "They used the free
   thing" is not evidence about a price.

   There is now an explicit impression boundary. Every 402 /
   payment-required moment records a canonical `paid_offer_challenged` event
   carrying `challenged_operation`, recorded identically on HTTP, MCP and
   A2A through one shared helper per transport so the three cannot drift into
   measuring different things. Exposure to a paid operation is exactly two
   things: being shown that operation's price, or completing a PAID call of
   it (a free-tier call of the same shape is not an impression of the price).
   Adjacent free products never count. Evidence-bundle and watch experiments
   get the same boundary. The portfolio view (no operation) is retained for
   the commercial report and labelled NOT VALID FOR PRICING.

2. THE INDEX PROMISED IDENTITY DEDUPE AND PERFORMED ENDPOINT DEDUPE ONLY.
   The module documented "endpoint fingerprint FIRST, then declared identity"
   while ingest keyed solely on endpoint, so one subject at several addresses
   was several entries - inflating the one number the index is judged on,
   in a product whose entire claim is not overstating.

   Declared-DID coalescing now happens at ingest: the canonical entry keeps
   its endpoint, additional endpoints declaring the same did:key become
   `alias_endpoints` retaining their own provenance and last observation, and
   every alias source is also recorded on the canonical entry. Summaries
   report subjects and endpoints SEPARATELY (total_entries,
   alias_endpoints_folded, distinct_endpoints_known) so neither can be quoted
   as the other.

   reconcile_identities() migrates duplicates that already exist - oldest
   entry wins as canonical, deterministic and idempotent, run at the start of
   every cycle and a no-op when there is nothing to merge.

   OPERATOR equivalence is explicitly NOT inferred. Two endpoints sharing a
   name, a domain or a contact string are not evidence of one operator, and
   guessing would launder one party's evidence into another's - a worse error
   than the one being fixed. Operator stays `unknown` unless a deterministic
   declared identifier says otherwise, and the public view says so.

Also corrects the stale indexops module comment: remote ingest is bounded
default-ON for CLEARED_SOURCES only, with GUILD_INDEX_INGEST=0 retained as
the one-config-change kill switch.

Tests: 1130 passed, 9 skipped (17 new, including "free preflight alone cannot
make deep pricing decisive" and "a genuine external deep challenge can").
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.

0 participants