Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 57 additions & 28 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# Contributing

Thanks for looking at Agent Commerce Gateway. This is an early alpha; the
architecture is deliberate and the scope is deliberately narrow.
Thanks for looking at Agent Commerce Gateway. The architecture is deliberate and
the scope is deliberately narrow; both of those are load-bearing, and most of
the rules below exist to keep them that way.

## Ground rules

1. **Scope discipline is a release requirement.** This release is MCP + x402 only.
New protocols and rails land after the adapter model survives real use.
Classify every proposal as `BLOCKER` / `QUALITY` / `NICE-TO-HAVE` /
`POST-ALPHA` — the default answer to a new capability is `POST-ALPHA`.
1. **Scope discipline is a release requirement.** Supported today: MCP, HTTP and
x402. Experimental and off by default: A2A, ACP, AP2 and OpenAPI import. New
protocols and rails land after the adapter model survives real use. Classify
every proposal as `BLOCKER` / `QUALITY` / `NICE-TO-HAVE` / `POST-1.0` - the
default answer to a new capability is `POST-1.0`.
2. **Never make the gateway custodial.** No PR may introduce storage of a
merchant or buyer private key, seed phrase or fund custody. See
[`SECURITY.md`](SECURITY.md).
Expand All @@ -32,27 +34,30 @@ npm run demo:agent
```

Requirements: Node >= 22, npm 10, Docker, and [Foundry](https://getfoundry.sh)
(`anvil`, `forge`, `cast`) for the chain work.
(`anvil`, `forge`, `cast`) for the chain work. On Linux, if your user is not
UID/GID 1000, see the note in the [README](README.md#quickstart) before
`docker compose up`.

## The loop

```bash
npm run verify # lint + typecheck + test — run this before opening a PR
npm run verify # contract + lint + typecheck + test - run this before opening a PR
npm run test:e2e # deterministic end-to-end (boots its own chain)
npm run lint:fix
```

Scope a run to one area with `npx vitest run tests/unit/<area>`.

A change is not mergeable if TypeScript fails, lint fails, required tests fail,
or the deterministic E2E fails.
A change is not mergeable if the contract surface changed unannounced,
TypeScript fails, lint fails, required tests fail, or the deterministic E2E
fails.

## Architecture you need to know before writing code

Read, in order:

1. [`docs/architecture.md`](docs/architecture.md) — the shape of the system.
2. [`docs/contracts.md`](docs/contracts.md) — the frozen cross-package contract.
1. [`docs/architecture.md`](docs/architecture.md) - the shape of the system.
2. [`docs/contracts.md`](docs/contracts.md) - the frozen cross-package contract.

The one rule that surprises people: **every protocol adapter converges on
`ExecutionPipeline.execute`**. An adapter never calls a merchant backend and
Expand All @@ -79,51 +84,75 @@ Implement `PaymentProvider`. Required before review:
- [ ] a `replayKey` derived only from the payment authorisation
- [ ] negative tests: no payment, malformed, wrong amount, wrong recipient,
wrong network, wrong asset, replay, provider unavailable
- [ ] a deterministic settlement proof — real state change, not a mocked success
- [ ] a deterministic settlement proof - real state change, not a mocked success
- [ ] no private key held by the gateway

## Adding an authorization method

Implement `AuthorizationProvider`. Authorization gates settlement; it never
moves money and never unlocks a resource on its own. Required before review:

- [ ] verification and reservation are one atomic step, before settlement
- [ ] a replay identity that survives re-presentation of the same proof
- [ ] binding to the resolved resource, input and price, not just to a signature
- [ ] `consume` / `release` / `markUncertain` finalizers, with release reserved
for failures that provably moved no money
- [ ] static trust only: no key discovery, no outbound request from a proof
- [ ] `AUTHORIZATION_*` error codes, never a payment code, and a coarse
rejection reason that is not an oracle for trust policy
- [ ] nothing of the proof itself in a receipt, an event or a log

AP2 is the worked example: [`docs/ap2.md`](docs/ap2.md).

## Publishing

The repository *is* the package: one `package.json`, published as
**`@devlab.group/agent-commerce`**. It ships the `agent-commerce` binary and three
library paths — `.`, `./mcp`, `./x402` — built from `src/` into `dist/`. The
MCP SDK, x402 and viem are **optional peer dependencies**: neither the main
entry nor the CLI may import them, or a default install breaks. Architectural
boundaries live in directories under `src/`, not in package manifests, so a
reappearing `pnpm-workspace.yaml` or `packages/` directory means the two models
are being run at once; the packaging tests fail on either.
**`@devlab.group/agent-commerce`**. It ships the `agent-commerce` binary and four
library paths - `.`, `./ap2`, `./mcp`, `./x402` - built from `src/` into
`dist/`.

Two constraints a PR must not break. **The heavy rails are optional peer
dependencies** (`@modelcontextprotocol/sdk`, `@x402/core`, `@x402/evm`, `viem`,
`jose`, `@sd-jwt/core`, `canonicalize`, `@coinbase/x402`), and neither the main
entry nor the CLI may import one, or a default install breaks. **Architectural
boundaries live in directories under `src/`**, not in package manifests, so a
reappearing `pnpm-workspace.yaml` or `packages/` directory means two models are
being run at once. The packaging tests fail on either.

```bash
npm run build # bundle -> dist/index.js + dist/cli/index.js
npm run test:cli:dist # run the built binary under plain node
npm run pack:dry # inspect what would be published
```

Only `dist/`, `README.md` and `LICENSE` are published. Everything else —
`src/`, `tests/`, `demo/`, `scripts/`, `docs/` — stays in the repository. Note
Only `dist/`, `README.md` and `LICENSE` are published. Everything else -
`src/`, `tests/`, `demo/`, `scripts/`, `docs/` - stays in the repository. Note
that `dist/` ships sourcemaps that embed the TypeScript they were built from;
that is intended (public source, real stack traces), not an oversight.

Never run `npm publish` without explicit maintainer approval.

## Commits and PRs

Prefix commits by area: `core:` `protocol:` `payment:` `cli:` `test:` `docs:`
`chore:`. Keep commits small and coherent.
Conventional commits: `type(scope): subject`, lower case, no trailing full stop.
Types in use are `feat`, `fix`, `docs`, `test`, `chore` and `refactor`; the
scope is the area you touched (`core`, `config`, `pipeline`, `gateway`,
`runtime`, `ap2`, `acp`, `openapi`, `cli`, `payment`), and repo-wide changes
drop it. Keep commits small and coherent.

```text
payment: bind x402 verification to a replay key
protocol: expose configured resources as MCP tools
feat(pipeline): enforce authorization before payment settlement
test(acp): add stable checkout conformance coverage
```

A PR should say what changed, why, how you tested it, and what it does **not**
cover.

## Reporting bugs

Include the version/commit, your `agent-commerce doctor --json` output (it
Include the version or commit, your `agent-commerce doctor --json` output (it
contains no secrets), what you expected, and what happened. For security issues,
follow [`SECURITY.md`](SECURITY.md) instead — do not open a public issue.
follow [`SECURITY.md`](SECURITY.md) instead - do not open a public issue.

## Code of conduct

Expand Down
Loading
Loading