Your Market architecture — current
Status: current as of 2026-09-04. Companion to
Identity architecture — current.
Audience: Foundation engineers, security reviewers, integrators.
Source of truth: functions/lib/market/, functions/market.js. When this
doc disagrees with the code, trust the code.
This is the bulk-buy commerce-agent slice of Pillar 3 (Your Market). It sits
beside the older vote-on-bids marketplace (product_requests /
product_votes / pillar3-market), it does not replace it. Partner-facing
copy (whitepaper, deck, yc-site) still describes the reverse-marketplace
story; this file describes what the agent harness actually enforces.
1. What this is
Foundation has three pillars on one verified-human identity layer:
| Pillar | Public host | Job | This slice |
|---|---|---|---|
| Your Voice | voice.foundation-global.com |
One human, one vote | Not implemented here. Catalog/pool policy that needs a vote is a hand-off, not a Voice subagent. |
| Your Share | share.foundation-global.com |
Cooperative funds | Escrow may come from Share later. The Market agent must not move the community fund. |
| Your Market | market.foundation-global.com |
Aggregate buying power | This. Sybil-resistant collective purchasing. |
Chrome name is Foundation. Pillar color for Market is #0891b2. Mobile/web
never hold a Solana keypair; chain writes (when any) go through Cloud Functions,
same rule as anchorCommitment.
The product is not Amazon-shaped. A verified member commits qty + max
unit price. That is demand, not a charge. Gathering continues until fill ≥ 80%
or the target is met; then RFQ, award, per-member checkout, ship / return.
Fake demand must not move supplier prices: every demand line goes through
requireAuth + requireVerifiedMember (functions/lib/verified-member.js),
the same sybil gate Voice writes use.
2. Architecture (decided once)
The pattern is Anthropic's anatomy of effective commerce agents: one model in a standard agent loop, skills not subagents, no intent router.
Two agents only because they face different principals:
| Agent | Principal | Prompt holds | Skills (functions/lib/market/skills.js) |
|---|---|---|---|
| Pool | Buyer, Market surface | Search, commitment semantics, presentation | demand-intent, purchase-research, customer-care, memory-personalization |
| Source | Ops | Pooling / RFQ / award / fulfillment | demand-pooling, supplier-negotiation, order-fulfillment, returns-exceptions |
Tools call Foundation systems (catalog, pools, RFQ, orders, host payments).
Search results arrive already ranked; the model chooses what to show
(searchCatalog in functions/lib/market/engine.js).
UI components are tools: present_pools, present_checkout, present_quotes,
present_review_queue. The server fills records from session-issued IDs
(functions/lib/market/session.js). Layout in the tool args is the on-screen
order.
Skills load as tool results so they cache with the session prefix
(load_skill in functions/lib/market/harness.js). High-frequency
instructions stay in the system prompt (functions/lib/market/prompts.js).
Safety, legal, brand, and key user facts stay in the prompt.
Prompt-cache order, prefix-based (buildCacheLayers):
- Global — prompt + tool defs (byte-identical across sessions)
- Session — memory facts + history
- Volatile — current time, current page (last, so it cannot bust the prefix)
Target hit rate is 90–99%. @plantagoai/ai's claudeWithCache is the shared
caching wrapper used elsewhere in Foundation; the Market harness is built so a
tool-capable loop can sit on the same global → session → volatile split. The
eval suite grades a scripted model (functions/__tests__/market-evals.test.js)
so quality does not depend on a live API key.
At 1K–1M members, Source sees aggregates only: committedQty,
customerCount, p50Max, fillRatio. sourceAggregate() never returns a
member list.
3. Lifecycle (Voice-shaped)
Implemented in functions/lib/market/engine.js.
- Commit. Verified member writes one demand line:
qty+maxUnitPrice. Not a charge. One line per(poolId, uid). Per-member qty cap is enforced on the resulting state (PER_MEMBER_QTY_CAP = 20), including retries. - Gather. Continues until
committedQty / targetQty ≥ 0.8orcommittedQty ≥ targetQty. - RFQ. Known suppliers: policy auto-applies
open_rfqin-band (ops is not clicking every join). Thin pool or emptyknownSupplierIds: human review queue (kind: "open_rfq"). - Award. Quotes and supplier notes are untrusted: sanitized and wrapped
in
<untrusted-commerce-data>(functions/lib/market/fence.js). Auto-award ifunitPrice ≤ unitCeilingand supplierrating ≥ 4.0. Else review (kind: "award"). - Allocate. Per-member checkout orders, status
awaiting_checkout. The model has nocharge/capture_payment/place_ordertool (POOL_TOOLS/SOURCE_TOOLS). - Place order. Host callable
placeMarketOrder. Requires a paymentnonce. The model cannot call this. - Returns. Auto-return under value/window constants
(
AUTO_RETURN_MAX_VALUE,AUTO_RETURN_WINDOW_DAYS); disputes to review.
The model stages. A policy or a person applies. Guardrails re-check at
apply time (applyStaged), not at stage time — a ceiling that moved after
staging still blocks the award. Writes serialize per pool
(withPoolLock in functions/market.js).
4. Harness (enforced in code)
Every rule below is in functions/lib/market/, not only in the prompt.
| Rule | Where |
|---|---|
| Fence third-party content | fenceUntrusted() — strips bidi/control chars, fence-marker imitations, fake tool_use / Assistant: turns; caps size; wraps <untrusted-commerce-data> |
| Writes/renders accept only server-issued IDs this session | createSession().issue / .accept; presentation tools drop unknown IDs |
| No charge tool | POOL_TOOLS ∪ SOURCE_TOOLS contain neither capture_payment nor charge nor place_order |
| Clients read own commitments/orders; Functions write | Firestore rules for market_* (server allow write: if false) |
| Source never sees a member dump | sourceAggregate() |
| Memory from user/assistant text only | extractFacts() ignores toolResultText; MARKET_MEMORY_ENABLED switch |
| Evals are snapshots | Construct store + user message, grade final store + last UI, not the tool path (functions/__tests__/market-evals.test.js) |
Eval coverage (a negative for every positive): core join, unverified refusal,
messy history, abandoned-intent, user-authored injection, data-plane injection
in quote notes, multi-capability join+checkout, checkout without a commit,
present_pools layout, dropped un-issued IDs.
5. Memory
Typed facts in Firestore (market_memory), not in the model:
- Always-on keys in the session prefix (
qty_cap,delivery_preference,default_max) - Per-turn prefetch from the same signals that pre-load a skill
- Everything else behind
lookup_memory - See / correct / delete callables; 365-day retention;
MARKET_MEMORY_ENABLED=falsedisables writes and extraction
GDPR: market_memory and market_agent_sessions delete with the account;
market_commitments and market_orders anonymize uid and keep amounts
(financial-record retention, same posture as votes).
6. Demo
demoNearThreshold() (functions/lib/market/demo.js) seeds pool
olive-oil-5l at 7/10 fill, known supplier Northwind (rating 4.5, quote
$4.20, ceiling $5). One verified join trips RFQ → eligible auto-award → host
checkout.
Seed into the emulator with seedMarketDemo (ring ≤ 1) or the emulator
seeder once that path is wired.
7. What this is not
- Not the
pillar3-marketAnchor program. That program still mirrors supplier-selection votes onproduct_requests. Seedocs/superpowers/specs/2026-06-01-pillar3-market-design.md. - Not a Voice vote. Do not embed a Voice subagent.
- Not a Share disbursement. No tool moves
funds/allocations. - Not a new brand. No "Foundation Mobile", no "Collective".
8. File map
| Path | Responsibility |
|---|---|
functions/lib/market/engine.js |
Lifecycle, policy, caps, ranked search, tool defs |
functions/lib/market/harness.js |
Agent loop, session IDs, presentation tools |
functions/lib/market/fence.js |
Untrusted-content sanitizer |
functions/lib/market/session.js |
Per-session issued-ID registry |
functions/lib/market/skills.js |
Skill bodies (loaded as tool results) |
functions/lib/market/prompts.js |
Pool / Source system prompts + cache layers |
functions/lib/market/memory.js |
Typed facts, extract / remember / forget |
functions/lib/market/demo.js |
Near-threshold olive-oil fixture |
functions/market.js |
Callables + Firestore persist / per-pool lock |
functions/__tests__/market-*.test.js |
Unit + snapshot evals |
| Market API guide | Client-facing callables |
| Cloud Functions reference | Exhaustive CF catalog (includes this slice) |
| Design spec | docs/superpowers/specs/2026-09-04-your-market-commerce-agents-design.md |