SchCP review brief

This brief is for a reviewer who has not seen the project before. It explains what exists, how it was built, which claims need checking, and where we already know there are weaknesses.

What this is

SchCP (Scheduling Context Protocol) is a proposed protocol that lets AI agents arrange time between people on their behalf. That means booking a business's appointments (barbers, auto shops, clinics), or setting up a meeting between several people whose own agents keep their calendars. It is meant as a first step toward agents coordinating people's time. Since v0.4 it never moves money. A host may state a price, which is disclosed in the signed offer and paid to the host directly, but many meetings have none. It covers:

Since v0.14 of the paper it is positioned as its own protocol for scheduling people, at a business and between people's own calendars. It borrows from Stripe's UCP Local Services, Wix's USP and Google's AP2 where they fit, and keeps its envelope close to theirs so one endpoint could speak both, but it is not offered as a profile of them (the author's decision, design/v0.4-notes.md §8.19). Payment, where a host wants it, belongs to the payment protocols or to the host's own checkout, never to SchCP. Meta and Sierra's PAP is covered as a platform risk.

The repo is /home/businessdesk/dev/schcp, also kept in a private GitHub repository; v0.4 is on the branch v0.4-hosts. It contains:

Path What it is
whitepaper/index.html The whitepaper, "Arranging People's Time: delegated scheduling authority for AI agents" (v0.15, which reports the live testing in §10.21, the decided open questions in §10.22 and the second implementation in §10.23). It is an HTML fragment with SVG figures. It is also published privately at https://claude.ai/artifact/H7kv3Qvkgm9uTYK3cjjX4y
whitepaper/schcp-whitepaper-v*.pdf PDF render of the paper
spec/SPEC.md, spec/schemas/schcp.schema.json Normative spec v0.4 and its JSON Schema
design/v0.4-notes.md Why v0.4 took the money out and left cancellations and no-shows to hosts, what it removed and added, what changed on the wire, and the decisions made in live testing (§8)
src/ Reference implementation in TypeScript. host/ holds the engine, the SQLite store and the HTTP server; agent/ holds the client, the planner (book an appointment; arrange, finish and revise a meeting of several people, before and after it is confirmed), the key app and webhooks; protocol/ holds JOSE, mandates, meeting contracts, acceptances and rules (meeting.ts), attestations, stated prices and durations; sim/ is a simulated internet of five fictional businesses (one a café that holds tables for meetings), a booking platform, and a personal-calendar service (Daybook) with three people on it
test/ 330 tests: 329 pass, 1 skipped, 0 fail, 0 todo. test/scenarios/ holds the business-viewpoint scenarios; test/people.test.ts and test/open-end.test.ts cover people as hosts and open-ended bookings; test/meetings.test.ts covers meetings of several people, rules and counters; test/meeting-changes.test.ts covers changing a confirmed meeting and businesses as members
conformance/ Wire-level test vectors (233) and a black-box runner (npm run conformance)
implementations/python/ A second, independent implementation in Python written from the spec, schema and vectors only: signatures, mandates, offers, receipts, moves, meetings and a live client (python3 -m schcp.vectors). Its README lists the 24 places the spec text was not enough, all since settled (design notes §8.20)
src/mcp/ An MCP server, so an existing assistant can book through SchCP. 16 tools: discover_host, find_slots (with with and daily), hold_slot (with with), approve_and_book, release_hold, get_booking, cancel_booking, change_booking, respond_to_change, join_waitlist, waitlist_status, leave_waitlist, arrange_meeting (with answers and in_person), meeting_status, revise_meeting, cancel_meeting
examples/site Sample booking site and playground (npm run site, port 4600), including a playdate between two parents and a stranger refused by a contacts-only calendar. npm run site:check runs its scenarios headless: 22 runs, 0 BAD
docs/diagrams/ Five SVG diagrams: booking states, the meeting flow, who says yes, a guest's move, and how the live testing was run
video/ Remotion source for a 131-second explainer (1080p). npm run render there writes out/schcp-explainer.mp4; the output is not committed
research/ Four research reports: AdCP, scheduling standards, agent-commerce delegation (AP2/ACP/UCP and others), booking platforms
reviews/ Ten business-viewpoint reviews. reviews/README.md is the ranked synthesis
LICENSE, LICENSE-docs, IPR_POLICY.md, NOTICE Licensing modelled on AdCP: Apache-2.0 for code, CC-BY-4.0 for docs, and a royalty-free licence under Necessary Claims

Run it with Node ≥ 22.18; there is no build step, because Node strips the TypeScript types itself:

npm install
npm test             # 330 tests (329 pass, 1 skipped)
npm run site:check   # 22 headless scenario runs
npx tsc -p .         # typecheck
npm run site         # playground on http://localhost:4600
npm run demo         # oil-change walkthrough

How it was built (in passes)

  1. The user's draft (v0.1). A problem statement and a sketch inspired by AdCP.

  2. Research. Parallel research agents produced the four reports in research/. They surveyed AdCP, iCalendar/iTIP/CalDAV/JSCalendar, AP2/ACP/UCP/PAP/Web Bot Auth/UCAN/Biscuit, and booking platforms (Square, Mindbody, Booksy, Fresha and others), with market numbers.

  3. First build (spec v0.1). The reference merchant endpoint, the agent SDK, signed offers on holds, booking-scoped management mandates, and 77 scenario tests. This produced whitepaper v0.3, which has a build report.

  4. Adversarial review. An independent review found 18 defects. All were fixed, with regression tests R1–R18 in test/review-regressions.test.ts, bringing the suite to 97 tests.

  5. Sample site. Added the booking site, no-show fees, alternatives offered on decline, and screenshots in the paper.

  6. Second pass (spec v0.2). We built three items the paper had listed as open questions, bringing the suite to 108 tests:

    • Closed mandates. The human signs the hash (offer_sha256) of the exact offer the merchant issued. The merchant policy closed_mandate: never | if_charged | always controls when this is required, and a mismatch returns OFFER_APPROVAL_REQUIRED. The consent text is generated from the signed offer.
    • Attestations. Third-party attesters (for example, a phone check) vouch that a principal key belongs to one person. Attested newcomers get their own pool of holds (max_active_per_attester), so a flood of throwaway identities can't crowd them out. An invalid attestation is ignored rather than rejected.
    • Reputation decay. Old hold expiries count less over time (half-life of 7 days by default). A suspended agent gets 60 s holds back after about 1.1 days and the full TTL after about 7 days. The "has booked here before" signal never decays.
  7. Third pass: ten business seats. Ten reviewers each took one viewpoint: small merchant, booking platform, healthcare, AI-assistant vendor, payments risk, multi-location chain, home services, consumer-protection regulator, scarce slots, and investor/standards. They wrote 47 scenario tests: passing tests show what already works, and todo tests mark gaps.

  8. Fixes from the third pass (whitepaper v0.5). The reviews found four real defects, all now fixed:

    1. A deposit pre-authorization was not bound to the payer. It now carries principal, and a mismatch returns PAYMENT_DECLINED.
    2. Refund failures were swallowed. Refund state (pending / refunded / failed) is now recorded on the booking, retried with backoff, and visible to the agent. Every retry uses the same idempotency key, so a lost response can't cause a double refund. This is a MUST in spec §5.5.
    3. A no-show was silent. It now bumps the sequence, issues a signed receipt and sends a booking.no_show webhook.
    4. The consent text showed raw ISO timestamps and an internal ID. It now gives local time in words and the business name. The key app shows the domain it verified during discovery, never a domain the offer merely claims.

    Also in this pass:

    • Schema migration for older databases (removed in v0.4: nothing has been released, so there is nothing to migrate).
    • The licensing above.
    • Paper changes: abstract rewritten around the gap, the money and the ask; market numbers in §1; new §10.13 "Third pass: ten seats at the table"; expanded §11 open questions; new §12 "Conclusion and ask".
  9. Deep review (whitepaper v0.6). An adversarial review reproduced 20 defects with 21 probes (reviews/deep-review.md); each was fixed with a regression test (test/money-and-leases.test.ts, removed with money in v0.4, authorization-boundaries, accepted-terms, calendar-and-wire, standing).

  10. Protocol v0.3 (whitepaper v0.7). Built from design/v0.3-notes.md: every acceptance names the SHA-256 of a merchant-signed offer; offers state an all-in total; late cancellations, late changes and no-shows are recorded rather than charged, and an enforcing merchant adds them to the next offer, which only the human's own signature accepts; a 60-second undo; a waitlist; two-party confirmation and reconfirmation; one refusal shape with registered reasons.

  11. Two v0.3 reviews. reviews/v0.3-review.md (V01–V10) and reviews/follow-up-review.md (R01–R12) reproduced 22 distinct defects between them, mostly where the new waitlist and two-party paths met the older controls. Each is fixed with a regression test in test/v03-review.test.ts, and every fix was checked by reverting it.

  12. Closing the gaps. Every todo scenario the ten seats wrote now passes (the 1 skipped test is arrival windows, a deliberate "won't do"). New in the engine and spec: resource capacity and combinable resources for parties, per-person prices, release schedules, a walk-in reserve, customer eligibility, a notes refusal for clinics, service locations and areas, surcharges, a deposit after a no-show, merchant extensions, displacement when a resource goes out, deposit waitlists, two-party bookings at approval merchants, pre-approved no-show ceilings, mandates for several merchants with a shared booking limit kept by the key app, operator-wide rate limits and reputation, brands, and calendar cold start. Each rule was mutation-checked (27 mutations, all caught). Built in parallel: crash tests (which found a stranded-capture race in the payment journal's release, now fixed), a conformance runner with test vectors, an MCP server, an AP2 profile (SD-JWT VC mandates with ES256 keys), and a sample site showing the v0.3 features.

  13. v0.4: arranging time, never money (design/v0.4-notes.md). SchCP no longer moves money. Removed: deposits, the payment provider, captures, refunds, the payment journal, the confirm and approval leases, adjustment lines, and the crash and payment tests. Confirming a booking is now one database transaction. Prices are optional disclosures. "Merchant" became host (a business or a person) and "customer" became guest. New:

    • open-ended bookings, which have a start and no agreed end; the host sets time aside and ends the booking when it is over;
    • places: at the guest's address, or a place the guest names;
    • people as hosts, with contacts-only calendars whose contact list is never published;
    • arrangeMeeting, which books one meeting on two people's calendars with one consent.

    The engine, tests, MCP server, conformance runner, sample site and spec were updated in parallel; every new rule was mutation-checked.

  14. Meetings of several people, rules and counters (design/v0.4-notes.md §4.5–4.7, spec §5.11, §5.12, §7.9). One organizer-signed contract names every member, their calendar and that calendar's signed offer; each calendar checks only its own entry; every member accepts the contract's hash, with their own key or through their agent under a rule they signed; nothing is confirmed anywhere until the organizer commits every acceptance to every calendar. A member's counter (another place) leads to a revised contract that everyone answers again. Bookings say whether the host, a person or a rule said yes, and a guest can ask for the person. 13 mutations, all caught.

  15. Cancellations and no-shows left to hosts (design/v0.4-notes.md §5, spec §5.6, §7.6). With no money moving, recording late cancellations and no-shows made no sense. Removed: fee terms in offers and policies (cancellation tiers, late-change treatment, no-show fees), outcome records, max_no_show_fee, the if_no_show_fee closed-mandate policy, the cancel quote and the 60-second undo. Cancelling is always free and records nothing. Kept: a host may mark a past booking completed or no_show as a status only, because a kept appointment is what earns standing under the anti-sybil cap (max_active_unproven) and for "returning guest" eligibility. The conformance checks cancel.quote and cancel.undo became cancel.free.

  16. Live testing with AI assistants on both sides (design/v0.4-notes.md §8). Claude Haiku assistants used the MCP tools through a local gateway, as guests (Noor, Sam, Kim, Alex) and as hosts (the front desk of Rosie's Nails, and the agents keeping Ellis's and Priya's calendars). Each round ran three scenarios. After each round the defects and misread wording were fixed, until three clean rounds in a row: 25 rounds, clean ones 17, 19, 21, 23, 24 and 25. The commits from "Fixes from the first live run" onward record each fix. What changed since the last brief:

    • Guests can move a booking (change_booking). At a host_approval host the move waits for the host's yes and the booking keeps its time meanwhile (proposal.awaiting_host, consent); a rule the person signed may say yes. New webhooks booking.change_approved and booking.change_declined (with a refusal: host_declined:*, or host_did_not_answer). A declined move stays on the booking as declined_change for guests who poll (spec §5.7, §5.8, §6).
    • Per-member answers in a meeting contract: the organizer can ask one member to answer in person and let a rule answer for another; the consent text names each group (spec §5.12, §7.9; MCP in_person).
    • Taking back a yes, and backing out. Until a meeting is final, a member may decline a booking they or their rule already said yes to; a decline is always recorded as the person's own answer, never a rule's. A member's calendar that cancels its part, before or after the meeting is final, calls the meeting off, and the other calendars hear why in that person's words. cancel_meeting calls a meeting off on every calendar at once (spec §5.12).
    • A business's own answer is by: "host". A front desk's yes, no or counter used to be recorded as answer.by: "person", which assistants read as a private individual. "person" now means the person whose own calendar it is, and "rule" a rule they signed (spec §7.9).
    • A confirmed meeting is not revised, and the refusal now comes before anything is held or asked: asking for a change offer on a confirmed meeting booking is meeting_invalid. (Superseded in pass 17: a confirmed meeting can now change with every member's yes.)
    • Choosing a person. discover_host lists choose_from; find_slots and hold_slot take with; offers carry resource_name next to resource (spec §5.2).
    • A waitlist window must hold the whole appointment (window_too_short, spec §5.9).
    • MCP wording. approve_and_book says user_signed ("this exact offer" or "only the limits: service, time window, price ceiling"); find_slots takes daily hours; discover_host lists taxes and says when a host refuses notes; prices say "list price"; deadlines come with relative times (expires_in, held_for); meeting_status gives answer_by; outcomes say requested rather than booked: false.
    • Times on the wire are whole seconds; consent texts name attendees and who answers how.
  17. Deciding the open questions (paper §10.22, design/v0.4-notes.md §8.10–§8.19). The author answered fourteen open questions, each put as a plain choice, and this pass built the answers:

    • Changing a confirmed meeting. The organizer revises it; every calendar keeps it confirmed at its time with the revision in meeting.pending_revision; once every member has said yes, commit moves each calendar atomically (booking.change_approved); a no, a yes taken back or the deadline (never after the start) drops it, and the organizer withdraws it elsewhere (POST /bookings/{id}/meeting/withdraw, reason organizer_withdrew).
    • Businesses as meeting members (business: true): the business's signed offer is its yes, or the host's yes where it approves each booking. Bellwoods Café joined the sample world.
    • The meetings capability in discovery; a calendar without it refuses a meeting contract.
    • Members shown by your own contact names, or as "someone you don't know, named …" (a SHOULD).
    • Records retention stated by the host: policies.retention and retention in every offer and receipt (required), and in the consent text. SchCP sets no number and checks nothing.
    • Receipts as evidence: consent: {approved: "offer" | "contract" | "limits", mandate, mode?} replaces approved_by; the SDK refuses a receipt whose retention or mandate disagrees.
    • Consent guidance (spec §5.3 SHOULDs) and differs_from_request in the MCP's hold_slot.
    • A cancel gives a booking back to a shared booking limit, retried while the key app is down.
    • Renames: "wallet" became the key app and "budget" a shared booking limit (constraints.shared_limit: true, reason shared_limit_reached), since neither holds money.
    • Decided without building: recurring meetings are not in v0.4; finding a place is out of scope; a cross-host reputation is left out; a host's silence still counts as attended; accessibility requests stay open. SchCP stands on its own rather than as a profile.
    • Also: closed schemas for holds, waitlist entries and booking responses; the v0.2/v0.3 review probes retired; npm run check-docs, which fails when a count in the docs disagrees with the code; and the R2 cap test timed in CPU time, the likely cause of live testing's one unexplained failure.

What we'd most like reviewed

In priority order:

  1. Do the paper, spec and code agree? The paper makes many specific claims, such as recovery timelines, error codes, field names, test counts and the decay maths. Spot-check them against spec/SPEC.md and src/. Inconsistencies between these three have been our most common bug.

  2. Are the security and trust mechanisms sound? Look at mandate verification and chaining (src/protocol/mandate.ts), closed-mandate enforcement (only the ROOT mandate's offer_sha256 counts, not an agent-signed child), attestation handling (src/protocol/attestation.ts), hold caps and reputation (src/host/engine.ts), HTTP message signatures (src/protocol/httpsig.ts), and contacts-only access (checkAccess, and whether a stranger can learn anything about a person's calendar). Try to break them. New in this pass, and worth the most attention:

    • Meeting contract checks (checkMeeting, verifyMeeting in src/protocol/meeting.ts): each calendar checks only its own entry (signer is the booker, member is its owner, offer hash, time and place). Can a contract say something to one member that another member's offer does not?
    • Acceptances and rules (verifyAcceptance, verifyRule, ruleCovers): another calendar verifies a rule acceptance it did not see given. Can a rule be stretched past what the person signed, or used by an agent it does not name?
    • Commit (commitMeeting): nothing is confirmed without every other member's acceptance of the current hash. Atomicity across calendars is the organizer agent's job (finishMeeting cancels the rest if one commit fails); hosts never talk to each other.
    • Revision (reviseMeeting): same meeting, same organizer, higher revision, and an offer the calendar actually made. Can an old contract or acceptance be replayed?

    New in pass 17, and worth the most attention now:

    • Changing a confirmed meeting (reviseMeeting, commitMeeting, withdrawRevision in src/host/engine.ts; finishMeeting in src/agent/planner.ts; spec §5.12). Can a waiting revision move one calendar without every yes, outlive a cancellation or the start, or leave calendars disagreeing without the organizer's agent noticing?
    • Business members: can a business member's booking be committed before its host said yes, or a meeting be final with the business still waiting?
    • Receipt consent and retention: can a receipt name a mandate the person never signed, or a retention that differs from the offer, and still pass the SDK?
    • Shared booking limit release on cancel (src/host/server.ts, limit_claims in the store): can a unit be given back twice, or given back for a booking that went ahead?

    New in pass 16, from live testing:

    • Host-approved guest moves (acceptChange, approveChange, declineChange and the sweeper in src/host/engine.ts; spec §5.7 step 3). The booking keeps its old time, contract and sequence while the new time is held until the approval deadline. Can a waiting move be applied twice, outlive a cancellation, or move a booking without the host's or a rule's yes? Does a move by the owner, or one a rule covers, skip the wait as it should, and does an unanswered move stay out of the guest's reputation?
    • declined_change: recorded on a decline or a lapse, cleared by the next change request. Is it ever left stale, or set for a move that did happen?
    • Per-member answers (memberAnswers in src/protocol/meeting.ts, and where the engine uses it): a member's own answers overrides the contract's. Can a rule answer for a member the organizer asked to answer in person?
    • Retraction (hostDecline on a tentative meeting booking): allowed only until the meeting is final. Can a confirmed meeting booking be declined, or a retraction race a commit?
    • Backing out (hostCancel on a meeting booking, and finishMeeting in src/agent/planner.ts): the meeting goes off, the cause is a no or a lapse before any cancellation, and the others are told in the member's words. Is the right person ever blamed wrongly, or a calendar left showing a meeting that is off?
  3. Are the external facts true? Dates and claims about other efforts came from research agents and have not been independently verified. Check these in particular:

    • the UCP Local Services charter (filed 2026-10-09);
    • Wix USP;
    • AP2 v0.2;
    • PAP (Meta + Sierra, 2026-10-06);
    • AdCP's licensing and IPR (research/adcp.md §2.4);
    • the market numbers in §1, which come from research/booking-platforms.md §5: 84k employer and 838k non-employer US salons, ~176k US auto shops, ~23% healthcare no-shows, a 56% phone-booking share, Booksy's 30% and Fresha's 20% fees, and Treatwell's £1.6bn.
  4. Is the paper persuasive? Does the abstract land with an executive in two minutes? Is the ask in §12 clear? reviews/investor-standards.md made suggestions we did not take: retitling the paper, adding an author byline, a "who pays" section, and a unit-economics example.

  5. Are the tests real? We mutation-checked each feature: each was broken on purpose to confirm a test fails. The third pass showed that 108 tests written only from the protocol designer's point of view missed four bugs. What other viewpoints or attacks are still missing?

Known gaps and limitations (already acknowledged)

Suggested output from the reviewer

A ranked list of findings. For each one, give the file and line, what is wrong, a concrete failure scenario or counter-evidence, and a suggested fix. Separate confirmed defects from opinions, and flag anything in the paper that overstates what the code does.