# Portable Result and commerce records

This implementation adds three bounded exports to existing delivery and commerce
workflows. It does not create another identity system, reputation score, payment
system, or general-purpose transaction graph.

| Export | Supported profile | What is verified |
| --- | --- | --- |
| `prooftask.result-passport/v1` | Selected public results for one organization subject | Platform-signed manifest, each original signed public summary, exact subject/result/publication/digest bindings, separately supplied disclosure status |
| `prooftask.commerce-record/v1` | Existing single-recipient, fixed-price Result Order commerce | Prooftask's immutable observations of checked provider readbacks and their captured sequence |
| `prooftask.transaction-closure-package/v2` | Ordinary custom delivery and one selected commerce payment attempt | Original Result Package v2, signed commercial order, Commerce Record, and exact relationships between them |

`prooftask.commerce-status/v1` is a separate signed current-status statement for a
Commerce Record. Historical content is immutable. Refreshing or replacing a
status does not modify that content.

## Result Passport v1

The Passport is a transport containing `schema`, a signed `manifest`, original
`summaries`, and replaceable `statuses`. Its manifest reuses the established
`provider_UUID` subject. Each selected entry contains `resultId`, `publicationId`,
`publicationDigest`, `completionTime`, and `summaryDigest` of the entire enclosed
summary. The coverage is always `SELECTED_RESULTS_NOT_COMPLETE_HISTORY`.

The provider page `/providers/{subjectId}` lets a visitor select results and
download `passport.json` or a ZIP containing that JSON and verification instructions.
V1 permits 1–20 distinct results from one subject; each
selection must name an exact currently public v2 publication and digest. A
changed, withdrawn, foreign-subject, duplicate, or unknown selection fails. Large
exports fail explicitly under the existing 2 MiB canonicalization limit; no
entries are silently removed. ZIP uses the existing bounded archive writer with
two fixed paths, `passport.json` and `README.txt`. The complete signed objects are
preserved. The instructions are not another signed factual record. No private
originals, issuer keys or executable verifier are added. Extract `passport.json`
and use a separately obtained trusted checker and independently trusted keys;
the checker does not import ZIP files.

The export endpoint is public and can be used anonymously:

```http
POST /api/public/providers/{subjectId}/passport
Content-Type: application/json

{"selections":[{"publicationId":"pub_<uuid>","publicationDigest":"<64 lowercase hexadecimal characters>"}]}
```

Append `?format=zip` for `application/zip`; omitted format or `?format=json`
returns JSON. Both use the same current-publication, selection and origin checks
and `Cache-Control: no-store`. Unknown formats fail explicitly.

**The collection is selected by its requester.** The service signs the exact
collection and verifies the underlying approved public projections; it does not
assert that the provider curated, endorsed, or signed this particular selection.
An observer can assemble a collection of already public material without gaining
any private-project access or additional publishing authority.

Each item continues to use its independent publication-status statement. Fresh
`AVAILABLE` means that the exact projection is currently disclosed; it is not a
new statement about delivery quality or original-result completion. `UNAVAILABLE`
does not erase historical signatures. Missing, ambiguous, invalid, or expired
status becomes `UNKNOWN`. The five-minute native publication-status window is
unchanged. A separate status input for the Passport is an array of current
publication statements; it replaces the embedded status array, including when
the replacement is empty.

Passport verification is
`SIGNED_PUBLIC_SUMMARIES_ONLY_NOT_ORIGINAL_RESULTS`. It does not include private
agreements, original files, full Result Packages, financial details, organization
mandates, or a provider's complete history. Existing history-reference creation
remains the way to use a selected result in a new agreement. New cooperation
does not inherit earlier acceptance, authorization, or payment.

## Commerce Record and current status

Only the existing `RESULT_ORDER` commerce path is covered. Template sales and
participation payouts are outside this profile. One binding identifies the
commerce order, Result Order, accepted-source digest, provider and environment,
currency and fixed amount, buyer scope, recipient scope, recipient account, and
item. The amount is the existing inclusive fixed total.

New or changed normalized provider facts are appended to an immutable journal.
Every entry has a sequence number, previous hash, observation time, component,
provider entity reference, facts, and its own hash. Payment readbacks include
payment intent/charge references, refunds, and disputes. Transfer observations
include reversals. Refund and payout observations retain their separate meanings.
An unchanged successful readback updates a separate current-status observation
time for exactly the same component, entity, sequence, and facts. It does not add
another immutable history entry. Failed provider calls do not become successful
observations or refresh that time. The journal prevents update/delete/replacement and
checks immutable provider references and sequence continuity.

A failed payment attempt may have its own latest Charge. Its observation remains
in history, but only a successful payment, transfer source or refund establishes
the fixed successful Charge reference. A later successful attempt on the same
Checkout and PaymentIntent must agree with that reference thereafter. Existing
captured failures are replayed into a separate derived successful-reference index;
the old reference rows, immutable observations and signed records are not rewritten.

The record is signed and stored by journal head. Re-exporting the same captured
history returns the same signed Commerce Record, even after unchanged provider
readbacks. The 1,000-entry export limit therefore counts captured changes rather
than routine polling; it cannot be exhausted merely by checking unchanged facts
every half hour for 21 days. Exceeding the bounded export limit fails explicitly
without deleting captured history. Existing orders acquire no
invented past: coverage remains `CAPTURED_PROVIDER_OBSERVATIONS_ONLY`, with
`priorHistory: NOT_ASSERTED` and `firstCapturedAt`. An export can have zero
observations; this proves no payment. Observation time means when Prooftask
successfully checked a provider response, not an independently timestamped moment
when the economic event occurred.

| Observed component | Permitted meaning |
| --- | --- |
| Open or expired checkout | `PAYMENT_NOT_CONFIRMED` |
| Checked paid payment | `PROVIDER_VERIFIED_PAYMENT` |
| Checked transfer | `PROVIDER_VERIFIED_TRANSFER`, with its separately recorded reversed amount |
| Succeeded refund | `PROVIDER_VERIFIED_REFUND` |
| Provider dispute flag | `DISPUTED` |
| Provider payout marked paid | `PROVIDER_OBSERVED_PAYOUT` |
| All of the above | `bankReceiptVerified: false` |

These labels are scoped by
`PROOFTASK_ATTESTATION_OF_PROVIDER_READBACK`. An independent verifier checks
Prooftask's signature and internal consistency. It does not obtain a Stripe
digital signature over the export, re-query Stripe, or establish bank receipt.
Payment, transfer, provider payout, and bank receipt remain separate concepts.

The current status transports the latest captured observation per component and
entity. Each component expires one hour after its last successful observation;
signing or downloading a status again does not extend that observation's window.
An actual successful readback with unchanged facts may advance the separate
current observation time. The verifier labels this
`statusObservation: RECORDER_ATTESTATION_NOT_REPLAYED` when that time differs
from the included immutable historical entry.
Overall freshness requires every included component to be fresh. This is bounded
knowledge of captured components, not a guarantee that all possible provider
activity has been observed.

To update an older saved record, request current status with its exact canonical
record digest. The status binds that saved record's history head and the latest
journal head. Verification checks the target digest, unchanged transaction
binding, observation/reference continuity, and freshness. A different payment,
account, provider environment, or unrelated digest cannot be substituted. Later
refunds and disputes update current observations while old signed history remains
valid. The status does not include the entire intervening journal. When its head
advances, the verifier reports
`historyExtension: RECORDER_ATTESTATION_NOT_REPLAYED`; it verifies the recorder's
statement and visible continuity, not every missing intervening entry. Download
the latest full Commerce Record to replay that captured history.

## Transaction Closure Package v2

This profile is `CUSTOM_DELIVERY_RESULT_ORDER`, with coverage
`SINGLE_TRANSACTION_SELECTED_PAYMENT_ATTEMPT`. The signed data contains typed
references, original artifacts, and limited assurance. The original artifacts are:

1. A Result Package v2 containing the exact historical **issued** Result Record
   corresponding to the accepted delivery used by this payment.
2. The native signed Result Order record, replayed under its own v1/v2 rules.
3. The native signed Commerce Record for the selected payment attempt.

The verifier independently checks the Result Package manifest, outer signature,
original delivery/history signatures and policies, core/source correspondence,
and recorded authority chain. It also verifies and replays the Result Order and
Commerce Record. The association checks then bind agreement ID/version/digest,
parties, accepted review and delivery ID/version/digest, issued Result ID, agreed
commercial terms, recipient account, environment, currency, and amount.

The existing payment source digest must equal:

```text
SHA256(JCS([
  resultOrder.termsDigest,
  resultOrder.financialHead,
  acceptedReview.id,
  acceptedSubmission.id,
  agreement.digest
]))
```

Re-signing an outer package cannot repair a forged inner signature or a
cross-transaction association. Missing mandatory artifacts, an unissued Result,
or wrong references fail. Optional status failure does not invalidate valid
historical content.

Export does not issue a paid Result Record automatically, debit credits, initiate
payment, or transfer money. It resolves an already issued historical snapshot.
Later concerns, additional reviews, refunds, and reversals cannot cause a newer
acceptance to replace the one originally bound to the payment. A changed live
state is reported through its separate status.

The base package does not require a Passport, subsequent agreement, artificial TSTO
object, AP acknowledgement, or finance-reliance event. Existing JEP and recorded
authority material stay inside the native Result Package. The original API/AP
`transaction-closure-package/v1` implementation and meaning are unchanged, with
`externalSettlement: NOT_ASSERTED` preserved.

### Optional reference to later cooperation

The order page offers **Include a later agreement**. The requester selects a saved
Passport JSON and another agreement they can access. This is an explicit private
export containing materials from both agreements; a public provider page does
not expose it or discover later customers.

`POST /api/commerce/result-orders/{resultOrderId}/closure` accepts exactly:

```json
{"passport": {"schema": "prooftask.result-passport/v1", "...": "complete saved Passport"}, "nextTaskId": "delivery_<uuid>"}
```

The Passport shown above is a placeholder, not a valid signed example. Submit
the complete saved object. The server independently checks financial and private
project access to the original transaction, then private-copy access to the
specified next agreement, before reading its recorded history link. It does not
search all customers or infer a relationship from names, dates or payment amounts.

The signed optional `lineage` carries the original Passport, native
`HISTORY_REFERENCE`, `AGREEMENT_LINK`, and a native signed next-agreement snapshot.
The updated verifier checks each native signature and binds the selected public
result, publication ID/digest, original delivery and acceptance, recorded history
reference and exact next-agreement version. The source association checks the
same Result, agreement version, submitted delivery, acceptance, completion time
and native event-history head. Independently signed wrappers may have different
export times; their bytes and `recordDigest` are not asserted identical. A public
projection from a different history head is not silently treated as the same
snapshot. Only one explicit `COOPERATE`
reference is included, not a complete cooperation history or general graph.

The precise relation is **the Passport contains a result referenced by the next
agreement**. The existing historical reference does not record a Passport digest,
so this export does not assert that this particular Passport was used to create
the agreement. A referenced public subject also does not independently establish
the next supplier's workspace identity. Earlier authority, acceptance and payment
never carry into the next agreement.

The next agreement may be awaiting supplier acceptance. It is a historical
snapshot, not a completed Result or fresh status statement. An extended report
therefore keeps overall current status `UNKNOWN`; separate base-transaction
status can still be checked and shown. A supplied invalid or unrelated lineage
fails the extended export/verification rather than being silently dropped.

Omitting the extension retains the original GET and historical v2 behavior. The
current verifier accepts both old and extended v2 packages and the existing v1
profile. Pre-extension strict v2 verifiers do not understand `lineage`; recipients
need the updated independent checker for those files. The canonical 2 MiB bound
also applies to the complete extended package: use a smaller Passport selection
when necessary; no referenced object is silently omitted.

The legacy API/AP v1 profile has a distinct verification scope. The unified checker
verifies its outer signature and the original v1 reference checks, not the v2
Result/Commerce policies. Embedded AP, finance-reliance and receipt facts do not
become independently verified inner records merely because the outer signature
passes. Current status stays unknown and external settlement remains unasserted.
V1 does not record an export signing time: `closedAt` is a business event time,
not a substitute for it. The report explicitly leaves historical key-validity
timing unestablished and rejects revoked issuer keys.

## Private export APIs

All commerce and closure exports require an authenticated, currently authorized
party and financial `READ`. A Closure additionally requires the existing buyer
account/project record gate or named supplier record gate. A finance-only
workspace member cannot obtain private delivery material through a Closure.
Public Passport permission grants no access to these endpoints.

| Method and path | Download |
| --- | --- |
| `GET /api/commerce/orders/{commerceOrderId}/record` | `commerce-record.json` |
| `GET /api/commerce/orders/{commerceOrderId}/status` | `commerce-status.json` for the latest export |
| `GET /api/commerce/orders/{commerceOrderId}/status?recordDigest={digest}` | Current commerce status bound to the specified saved record |
| `GET /api/commerce/result-orders/{resultOrderId}/closure` | `transaction-records.json` |
| `POST /api/commerce/result-orders/{resultOrderId}/closure` with `{passport,nextTaskId}` | `transaction-records-with-history.json` |
| `GET /api/commerce/result-orders/{resultOrderId}/closure/status` | `transaction-status.json` containing `{result, commerce}` |
| `GET /api/commerce/result-orders/{resultOrderId}/closure/status?recordDigest={digest}` | Status targeting the enclosed Commerce Record digest of an older Closure |

For a Closure, the query digest is
`closure.body.data.references.commerce.digest`, not the outer Closure digest.
Status retrieval reports captured observations; it is not itself a provider
refresh command. Use the existing explicit commerce refresh workflow when new
provider observation is required.

## Verification, schemas, and trust

Use `/verify` or the standalone `/developers/verify-result.mjs`. The native
dispatcher, browser verifier, and generated CLI share the same artifact policies.
The native entry point is `verifyResultArtifact(artifact, trust, checkedAt, status)`;
the byte-level entry point is `verifyResultFiles`. Verify untrusted file bytes
through the latter so duplicate JSON keys and invalid encodings are rejected.

With Node 24 or later:

```sh
node verify-result.mjs passport.json independently-trusted-keys.json 2026-09-30T00:00:00.000Z
node verify-result.mjs commerce-record.json independently-trusted-keys.json 2026-09-30T00:00:00.000Z --status=commerce-status.json
node verify-result.mjs transaction-records.json independently-trusted-keys.json 2026-09-30T00:00:00.000Z --status=transaction-status.json --text
```

The timestamp is the relying party's verification time and uses canonical UTC
RFC 3339 milliseconds. `--text` changes presentation only. Optional
`--authority=approved-authority-policy.json` supplies separately approved
organization authority anchors/revocations for native Result Package checks.
Issuer-key bundles do not implicitly approve embedded company mandates. The
verification engines make no network requests or economic actions.

The supplied issuer keys and applicable release/key validity policies must be
trusted independently. The signature of a sender-supplied bundle cannot make its
own key trustworthy. A successful report is `VERIFIED_WITH_LIMITATIONS`, with
historical validity, per-object checks, current status, recorded authority, and
limits reported separately. Cryptography does not independently prove delivery
quality, truth of original files, legal company authority, a provider signature,
bank arrival, absence of omitted events, or legal liability.

The fixed schemas are published at:

- `/.well-known/prooftask-result-passport-v1.schema.json`
- `/.well-known/prooftask-commerce-record-v1.schema.json`
- `/.well-known/prooftask-commerce-status-v1.schema.json`
- `/.well-known/prooftask-transaction-closure-package-v2.schema.json`

JSON Schema validation does not replace native signature, history, reference,
authority, or freshness verification. Existing canonicalization, nesting, node,
and size limits still apply; no silent truncation or custom extension loader is
introduced.

## Continuing with saved records

A provider-page selection can span pages and searches for the same organization.
The page shows the selected references and lets the visitor remove them. Selection
does not reserve publication permission: export still checks every exact selected
publication and digest. Changed or withdrawn entries are not silently replaced.

Use `/passport/status` to obtain current disclosure statements for a saved
Passport, including when all its public results have been withdrawn. The page
reads the JSON locally and sends only the organization subject and selected
publication IDs/digests to
`POST /api/public/providers/{subjectId}/passport/status`, with the same bounded
`{selections:[{publicationId,publicationDigest}]}` request as Passport selection.
The response is an independent status array. It does not validate or replace the
old Passport: verify the original file, independently trusted keys, and this new
status array together. This online retrieval page is separate from the offline
checker, which makes no network requests.

The economic-record and transaction-record download sections also accept a saved
JSON for a targeted status update. They compute its Commerce Record digest on the
device and send only that digest to the existing authorized status endpoint.
The server checks the exact saved-record index and current access. For a Closure,
the target is the enclosed Commerce Record digest, never the outer package hash.
If a checkout expires and the same Result Order starts another payment attempt,
the saved digest still selects the original attempt after the existing project
and financial access checks; it is never redirected to the replacement payment.
Choosing a file does not establish signature validity; independently verify the
old artifact and the returned status together. A status download does not itself
refresh the payment provider or initiate an economic action.

The browser checker updates expiring component statuses while a result remains
open and before printing or saving a report. A fresh component cannot keep an
expired component current. Later-agreement status remains unknown, and historical
signatures remain verifiable after any current-status window expires.

The targeted tests include `result-passport.test.ts`,
`commerce-record-runtime.test.ts`, `commerce-record-verifier.test.ts`,
`transaction-closure-v2.test.ts`, and `portable-records-routes.test.ts`. Saved-file
continuity is covered by `saved-status.test.ts`, `result-passport-continuity.test.ts`
and `result-passport-status-routes.test.ts`. `verification-freshness.test.ts` covers
independent expiration, and `legacy-closure-verification.test.ts` compares native,
generated browser and standalone CLI results. Existing
`runtime-supplier-closure.test.ts` protects the v1 API/AP path.

`tests/fixtures/portable-records-v1.json` contains four frozen exports generated
through the synthetic ordinary-delivery workflow, with public test keys only.
`portable-record-vectors.test.ts` applies the native policies to this corpus.
The existing CI gate `python scripts/check-result-core-vectors.py` independently
validates all four JSON Schemas, JCS/Ed25519 signatures, object digests, and the
commerce hash chain using Python and locally resolved schemas. It does not claim
to reimplement all native business or authority policies. To regenerate reviewable
synthetic vectors, run:

```sh
node scripts/generate-portable-record-vectors.ts test-artifacts/portable/schema-fixtures.json
```

`tests/fixtures/transaction-lineage-v2.json` separately covers the optional
extension, retaining the original four artifacts as compatibility evidence.
The Python gate validates its schema, signatures and cross-object digests; native
tests additionally exercise chronological, party and authorization policies.
Regenerate this synthetic public-key-only fixture with
`node scripts/generate-transaction-lineage-vector.ts <output.json>`.

`tests/devices/portable-records.spec.mjs` walks the complete supported business
story through browser controls: provider proposal, buyer agreement, supplier
acceptance, actual local-file delivery, buyer file matching and acceptance,
record issuance, both disclosure approvals, JSON/ZIP Passport downloads,
independent offline verification, a new buyer's history-reference agreement,
second delivery/acceptance, both commercial confirmations, explicit checkout,
provider readback, transfer, Commerce/Closure downloads and second-result
accumulation. A third agreement supplies the optional lineage export while still
awaiting supplier acceptance. Browser, native and CLI reports are compared.

The browser fixture bootstraps identities, scoped finance grants, legal test
configuration, test record funding and a local simulated payment provider. It
does not create the agreements, submissions, decisions, commercial orders or
publication approvals through service shortcuts. A checkout click changes the
simulated external provider state; no real Stripe request or bank movement is
performed. This is not a production onboarding, mailbox-delivery or live-payment
acceptance claim.
