@allorc/a423-sdk 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,101 +1,631 @@
1
- # A423 SDK — 0.6.0
1
+ # A423 SDK
2
2
 
3
- Version 0.6 adds a durable **cancellation subflow**: accepted signed JSON → escrow execution → confirmed cancellation receipt. See [CANCELLATION.md](./CANCELLATION.md). Acceptance records never claim funds have moved. It reuses the 0.5 reference contract.
3
+ **Build agreements between agents, lock payment in escrow, and produce evidence of what actually happened.**
4
4
 
5
- Version 0.5 adds **negotiated closing settlements**: partial-work/expense claims, counterproposals, exact acceptance, on-chain release blocking during negotiation, arbitration and unchanged deadline fallbacks. See [SETTLEMENT.md](./SETTLEMENT.md) for the complete flow, signatures, ledger and upgrade requirements. It closes the job; it does not add milestone payments.
5
+ A423 is a backend SDK for agent-to-agent, human-to-agent and agent-to-human jobs. You supply the task, identities, wallets, execution and validation. The SDK coordinates the agreement, permissions, durable workflow, on-chain settlement and receipts.
6
6
 
7
- Experimental Node.js/Next.js middleware for agent tasks, agreements and escrow. Version 0.4 adds working persistence, authority credentials and operational services around the configurable workflow and reference contract. Installing the SDK never publishes endpoints or deploys a contract automatically.
7
+ [npm package](https://www.npmjs.com/package/@allorc/a423-sdk) · [GitHub source](https://github.com/Allorc-official/A423-SDK) · [Changelog](./CHANGELOG.md) · [Security](./SECURITY.md) · [MIT license](./LICENSE)
8
+
9
+ **These docs describe source version 0.8.1.** Check the published npm version before installing; a source commit is not an npm release. If 0.8.1 is not published yet, use the source/tarball instructions below. Installing an older version will not provide newer APIs.
10
+
11
+ > Experimental, independently unaudited reference implementation. Acceptance is not payment. A transaction hash is not confirmation. Funds live in the deployed escrow contract, never inside the SDK or the worker's wallet while locked.
12
+
13
+ ## Contents
14
+
15
+ - [What runs where](#what-runs-where)
16
+ - [Install the library](#install-the-library)
17
+ - [Try it without a blockchain](#try-it-without-a-blockchain)
18
+ - [Configure an escrow application](#configure-an-escrow-application)
19
+ - [Configuration arguments](#configuration-arguments)
20
+ - [Understand target, minimum and maximum](#understand-target-minimum-and-maximum)
21
+ - [Define a job agreement](#define-a-job-agreement)
22
+ - [End-to-end job lifecycle](#end-to-end-job-lifecycle)
23
+ - [Cancellation and partial-work settlement](#cancellation-and-partial-work-settlement)
24
+ - [Decline, cancel and reopen before funding](#decline-cancel-and-reopen-before-funding)
25
+ - [Use it from Next.js or another agent](#use-it-from-nextjs-or-another-agent)
26
+ - [Retries, receipts and recovery](#retries-receipts-and-recovery)
27
+ - [Supported scenarios and boundaries](#supported-scenarios-and-boundaries)
28
+ - [Modules and detailed guides](#modules-and-detailed-guides)
29
+ - [Develop and publish](#develop-and-publish)
30
+
31
+ ## What runs where
32
+
33
+ ```mermaid
34
+ flowchart TD
35
+ Caller["Your UI, human or AI agent"] --> Backend["Your backend with A423 SDK"]
36
+ Backend <--> Store["Private durable storage"]
37
+ Backend --> RPC["RPC connection to chosen network"]
38
+ RPC <--> Escrow["Deployed A423Escrow contract"]
39
+ Escrow --> Wallets["Worker payout and payer refund"]
40
+ Wallets -->|"Wallet approvals and signatures"| Backend
41
+ ```
42
+
43
+ | Component | Responsibility |
44
+ |---|---|
45
+ | Your application | Task execution, artifacts, real cost measurement, wallet custody/delegation and business approvals |
46
+ | SDK backend | Agreements, role checks, workflow records, transaction journal, policy, reconciliation and receipts |
47
+ | Authority | Organization-approved enrollment and authenticated agent credentials |
48
+ | Blockchain contract | Holds the agreed ERC-20 token and enforces the supported signed settlement rules |
49
+ | RPC provider | Reads chain state and sends signed transactions to the selected network |
50
+
51
+ Installing the package does not deploy a contract, open API endpoints, create wallets or start an AI worker. Mount the handler, configure your services and run your operational worker explicitly. Public identity derivation, including `@allorc/identity`, is not authentication or permission to spend.
52
+
53
+ ## Install the library
54
+
55
+ Use **Node.js 22.13+**; Node 24 is the tested runtime. SQLite uses persistent local disk. Run the SDK on the backend, not inside a browser bundle.
56
+
57
+ ### Published package
8
58
 
9
59
  ```sh
10
- npm install @allorc/a423-sdk@0.6.0 ethers@^6.15.0 pdf-lib@^1.17.1
60
+ npm view @allorc/a423-sdk version
61
+ npm view @allorc/a423-sdk@0.8.1 version
11
62
  ```
12
63
 
13
- Node.js 22.13+ is required for the SQLite module. Node 24 is the tested runtime. `ethers` is optional unless you import `/escrow`; `pdf-lib` is optional unless you render PDFs. Core/workflow remain framework-independent.
64
+ If the required version is published:
14
65
 
15
- ## Modules
66
+ ```sh
67
+ npm install @allorc/a423-sdk@0.8.1 ethers@^6.15.0
68
+ # Optional: only if you need PDF receipts
69
+ npm install pdf-lib@^1.17.1
70
+ ```
16
71
 
17
- | Import | Implemented behaviour |
18
- |---|---|
19
- | `/workflow` | Versioned terms, bilateral acceptance, role guards, CAS state/events, pending operations, explicit observe/resume and safety holds |
20
- | `/escrow` | Reference ERC-20 contract, full custody lifecycle, EOA/ERC-1271 worker consent, strict chain evidence, managed signing/delegation, allowlisted deployment registry, bounded fee replacements and incremental log indexing |
21
- | `/storage` | Single-host SQLite workflow store, transaction/replacement journal, atomic nonce reservations, leased queue/checkpoints, spending reservations and authority/event storage |
22
- | `/authority` | Operator-approved organization registry, organization-signed agent enrollment, single-use challenges, signed audience-bound bearer credentials, revocation, public-key verification and renewal |
23
- | `/operations` | Typed task/evidence validation, spending authorizer, maintenance/keeper ticks, durable execution dispatcher, fresh receipts/PDF, reorg quarantine, SSE, webhook outbox, safe migration planning and app/chat integration handler |
24
- | Root | Preserved original simple offer/accept/submit/approve API; [legacy documentation](./LEGACY.md) |
72
+ `ethers` is needed for escrow/chain functionality. It is optional for the core workflow and pure policy calculator. `pdf-lib` is optional for PDF rendering. See the [npm package and available versions](https://www.npmjs.com/package/@allorc/a423-sdk?activeTab=versions).
25
73
 
26
- ## Start with the actual lifecycle
74
+ ### Before publication: build an installable tarball
27
75
 
28
- Create a draft → propose immutable terms → both parties accept the exact revision/hash → worker signs consent → payer authorizes funding → contract holds tokens → worker passes fresh eligibility gate → deliver/commit artifact → verify → release. Expired unsubmitted jobs can refund; submitted disagreements can dispute/resolve; the agreed timeout allocation remains available if trusted roles disappear.
76
+ From a checkout containing the required version, or a source archive supplied by the maintainer:
29
77
 
30
- **Acceptance, token approval and confirmed custody are different states.** Only matching confirmed chain evidence establishes that money is locked. Neither a public identity hash nor a caller-supplied agent ID authenticates the caller.
78
+ ```sh
79
+ git clone https://github.com/Allorc-official/A423-SDK.git
80
+ cd A423-SDK
81
+ node -p "require('./package.json').version"
82
+ npm ci --ignore-scripts
83
+ npm pack
84
+ ```
31
85
 
32
- Read [ESCROW.md](./ESCROW.md) for exact fields, inputs, roles and contract limits. Read [OPERATIONS.md](./OPERATIONS.md) for configuration, authority enrollment, storage, worker scheduling, spending limits, fresh receipts, monitoring, UI/chat wiring and migration.
86
+ For this version, `npm pack` creates `allorc-a423-sdk-0.8.1.tgz`. Install that file in your application:
33
87
 
34
- ## Minimal server setup
88
+ ```sh
89
+ npm install /absolute/path/allorc-a423-sdk-0.8.1.tgz ethers@^6.15.0
90
+ ```
35
91
 
36
- ```ts
37
- import { createSqliteStorage } from '@allorc/a423-sdk/storage';
38
- import { createWorkflow } from '@allorc/a423-sdk/workflow';
39
- import { createEscrowAdapters, escrowTransitions } from '@allorc/a423-sdk/escrow';
40
- import { createSpendingAuthorizer, createApplicationHandler } from '@allorc/a423-sdk/operations';
41
-
42
- const storage = createSqliteStorage({ filename: '/your-protected-local-disk/a423.sqlite' });
43
- const authorizeTransaction = createSpendingAuthorizer({
44
- spending: storage.spending,
45
- limitsFor: loadApprovedRawTokenLimits,
46
- authorize: checkSignerDelegationAndJobSafety,
92
+ Keep the tarball or pin a source commit for reproducible evaluation. If the GitHub branch has not received the latest commit, cloning it cannot provide that commit's features. Never assume an npm update, GitHub push and contract deployment are the same operation.
93
+
94
+ ## Try it without a blockchain
95
+
96
+ This example uses **in-memory storage** and moves **no money**. It demonstrates bilateral agreement and the optional pure policy calculator. Reference escrow jobs use the richer agreement schema shown later.
97
+
98
+ ```js
99
+ import {createMemoryStore} from '@allorc/a423-sdk';
100
+ import {createWorkflow} from '@allorc/a423-sdk/workflow';
101
+ import {createSettlementPolicy} from '@allorc/a423-sdk/policy';
102
+
103
+ const workflow = createWorkflow({store: createMemoryStore()});
104
+ const id = 'research-001';
105
+ await workflow.create({
106
+ id,
107
+ participants: {requester: 'buyer', worker: 'researcher', verifier: 'reviewer'},
108
+ }, 'buyer');
109
+
110
+ let job = await workflow.act(id, 'quote', {
111
+ terms: {description: 'Prepare a research brief', amount_raw: '200'},
112
+ }, {actor: 'researcher', idempotencyKey: 'quote-1'});
113
+
114
+ const acceptance = {
115
+ revision: job.agreement.revision,
116
+ agreement_hash: job.agreement.hash,
117
+ };
118
+ await workflow.act(id, 'accept', acceptance, {
119
+ actor: 'buyer', idempotencyKey: 'buyer-accept-1',
47
120
  });
48
- const adapters = createEscrowAdapters({
49
- provider, chainId, contractAddress, tokenAddress, confirmations,
50
- transactions: storage.transactions, signerFor, addressFor,
51
- authorizeTransaction, validateArtifact,
121
+ job = await workflow.act(id, 'accept', acceptance, {
122
+ actor: 'researcher', idempotencyKey: 'worker-accept-1',
52
123
  });
53
- const workflow = createWorkflow({
54
- store: storage.jobs, adapters, transitions: escrowTransitions,
55
- validateTerms, authorize: authorizeWorkflowAction,
124
+
125
+ const plan = createSettlementPolicy().evaluate({
126
+ cost_basis_raw: '100', budget_raw: '300', escrow_raw: '300',
56
127
  });
57
- const handler = createApplicationHandler({
58
- workflow, store: storage.jobs, queue: storage.queue,
59
- authenticate, revalidate, consentFor,
128
+ console.log(job.state); // accepted; no funding has occurred
129
+ console.log(plan.target_payout); // 200: 50% gross-margin target
130
+ console.log(plan.recommended_payout);// 200: fits budget and escrow caps
131
+ ```
132
+
133
+ Save as `demo.mjs` and run `node demo.mjs` after installing the required SDK version. In the source repository, the same example is available as:
134
+
135
+ ```sh
136
+ node examples/quickstart.mjs
137
+ ```
138
+
139
+ Calling `fund` on this minimal workflow fails because no funding adapter is configured. Use the application factory below for the reference escrow implementation.
140
+
141
+ ## Configure an escrow application
142
+
143
+ First deploy a compatible reference contract on your chosen EVM network with its ERC-20 token address. Then configure the SDK against that deployment. The 0.8.1 SDK reuses the **0.5 reference contract**; installing a new npm version does not upgrade an older immutable contract.
144
+
145
+ The following is a backend wiring example. `authority`, `walletRegistry`, `approvals`, `artifacts`, `workMeter` and `budgets` are **your application's services**, not additional SDK exports. See [OPERATIONS.md](./OPERATIONS.md) for authority enrollment, protected signing and execution setup.
146
+
147
+ ```js
148
+ import {JsonRpcProvider} from 'ethers';
149
+ import {createSqliteStorage} from '@allorc/a423-sdk/storage';
150
+ import {createEscrowApplication} from '@allorc/a423-sdk/operations';
151
+
152
+ const storage = createSqliteStorage({filename: './private/a423.sqlite'});
153
+ const provider = new JsonRpcProvider(process.env.A423_RPC_URL);
154
+
155
+ const app = createEscrowApplication({
156
+ storage,
157
+ authority, // Your configured createAuthority(...) instance
158
+ escrows: [{
159
+ provider,
160
+ chainId: Number(process.env.A423_CHAIN_ID),
161
+ contractAddress: process.env.A423_ESCROW_ADDRESS,
162
+ tokenAddress: process.env.A423_TOKEN_ADDRESS,
163
+ confirmations: 2,
164
+ }],
165
+ signerFor: async actor => walletRegistry.signerFor(actor),
166
+ addressFor: async actor => walletRegistry.addressFor(actor),
167
+ limitsFor: async () => ({
168
+ perTransaction: '1000000', daily: '5000000', total: '20000000',
169
+ }),
170
+ approveSigning: async context => approvals.allowSigning(context),
171
+ validateArtifact: async context => artifacts.validate(context),
172
+
173
+ // Optional: remove this block to leave profit policy disabled.
174
+ settlementPolicy: {
175
+ thresholds: {
176
+ target_margin_bps: 5000, // Aim for 50% gross margin
177
+ min_margin_bps: 1000, // Below 10% needs an owner exception
178
+ max_margin_bps: 5000, // Do not ask above the 50% threshold
179
+ require_counter: true,
180
+ },
181
+ costBasisFor: async job => workMeter.costBasisRaw(job.id),
182
+ budgetFor: async job => budgets.maximumRaw(job.id),
183
+ approveException: async context => approvals.ownerException(context),
184
+ },
185
+ keeperEnabled: false,
186
+ forwardCancellations: true,
187
+ requireIndependentArbitrator: true,
60
188
  });
61
- // Import this configured handler in the appropriate Next.js catch-all route:
189
+
190
+ export const {workflow, handler, applicationOptions, workerOptions} = app;
191
+ ```
192
+
193
+ All money values above are **raw integer token units**, not dollars or decimal token amounts. Choose limits for your own token's decimals and spending policy. Parent directories and the SQLite/WAL files must be on protected persistent local storage; the driver is not a distributed database or an ephemeral serverless disk substitute.
194
+
195
+ You can register multiple network/contract/token deployments through `escrows`. Each job is bound to its agreed deployment. Switching RPC URLs never moves an existing job's funds to another chain. The reference adapters check the RPC chain ID, escrow token and agreement fields before acting. Connection/eligibility checks occur when used; initialization is not proof that a contract was deployed or funded.
196
+
197
+ ## Configuration arguments
198
+
199
+ ### Application factory: `createEscrowApplication(options)`
200
+
201
+ | Argument | Required / default | Meaning |
202
+ |---|---|---|
203
+ | `storage` | Required | Durable jobs, transaction journal, nonce reservations, queue, spending and private KV |
204
+ | `authority` | Required | Configured authority with registered, active agents and credential verification |
205
+ | `escrows` | Required; nonempty | Allowlist of supported chain deployments; fields below |
206
+ | `signerFor(actor)` | Required callback | Returns the authorized wallet/signer for an actor; reject unknown actors |
207
+ | `addressFor(actor)` | Required callback | Returns that actor's trusted wallet address |
208
+ | `limitsFor(context)` | Required callback | Raw-token `perTransaction`, `daily`, `total` funding caps |
209
+ | `approveSigning(context)` | Required callback | Explicitly permits or denies a concrete signing/broadcast intent |
210
+ | `validateArtifact(context)` | Required for verification | Checks the submitted artifact commitment against accepted criteria |
211
+ | `execute(context)` | Optional | Your actual work runner; returns a real `result_hash` and honors its abort signal |
212
+ | `settlementPolicy` | Disabled when omitted | Enables the optional cost/profit policy described below |
213
+ | `keeperEnabled` | `false` | Allows the operational worker to execute eligible maintenance actions when run |
214
+ | `forwardCancellations` | `true` | Saves accepted cancellation JSON, then attempts escrow execution; `false` stages it only |
215
+ | `requireIndependentArbitrator` | `true` | Requires arbiter wallet to differ from payer, worker and verifier |
216
+ | `allowAuthorityRequest(request)` | Deny by default | Explicit request policy for optional authority challenge/exchange routes |
217
+
218
+ The factory returns `workflow`, `handler`, `applicationOptions`, `workerOptions`, `chatTools(actor)` and an optional `profit` service. No worker is scheduled just because `keeperEnabled` is set. Run [scripts/operations-worker.mjs](./scripts/operations-worker.mjs) with your exported configuration.
219
+
220
+ ### Each entry in `escrows`
221
+
222
+ | Argument | Meaning |
223
+ |---|---|
224
+ | `provider` | An ethers provider connected to the chosen network's RPC |
225
+ | `chainId` | Positive numeric network ID; testnet and mainnet have different IDs |
226
+ | `contractAddress` | Address of the compatible deployed escrow contract |
227
+ | `tokenAddress` | ERC-20 address configured in that contract |
228
+ | `confirmations` | Positive number of blocks required before the adapter confirms chain evidence |
229
+
230
+ Environment variable names in the example are an application convention. The SDK receives explicit values; it does not automatically discover a testnet, mainnet, RPC URL or token.
231
+
232
+ ### Profit policy initialization
233
+
234
+ | Argument | Default / meaning |
235
+ |---|---|
236
+ | `thresholds.target_margin_bps` | `5000`: desired gross margin of 50% |
237
+ | `thresholds.min_margin_bps` | `1000`: minimum normal gross margin of 10% |
238
+ | `thresholds.max_margin_bps` | `5000`: highest permitted payout threshold, corresponding to 50% margin |
239
+ | `thresholds.require_counter` | `true`: worker must counter a first requester offer before accepting it; requester can accept the worker's own ask |
240
+ | `costBasisFor(job)` | Required when enabled; returns completed-work cost plus agreed expenses as a raw-unit string |
241
+ | `budgetFor(job)` | Required when enabled; returns the original requester cap as a raw-unit string |
242
+ | `approveException(context)` | Optional trusted callback returning `{approved: true, approval_id: 'decision-id'}`; absent means no below-minimum exception |
243
+ | `canViewPrivate({job, actor})` | Optional; allows an already authorized operator to see worker economics; default denies extra viewers |
244
+ | `policy` | Alternative `createSettlementPolicy(...)` object; do not supply it together with `thresholds` |
245
+
246
+ **100 basis points = 1%.** Settings must satisfy `0 <= minimum <= target <= maximum < 10000`. For a 30% target, 5% minimum and 40% maximum, pass `3000`, `500` and `4000`. Percentages apply to **revenue**, not cost. Unknown option names, invalid types and inconsistent thresholds fail at initialization.
247
+
248
+ Each instance captures its effective settings. Inspect `app.profit.configuration`; mutating the original options object afterward does not change the policy. Already frozen negotiations retain their recorded cost/budget/configuration snapshot. Owner exceptions never override budget, escrow or maximum payout caps, and an agent-supplied approval flag is not owner authorization.
249
+
250
+ ## Understand target, minimum and maximum
251
+
252
+ With the default **50% target / 10% minimum / 50% maximum**:
253
+
254
+ ```text
255
+ profit = worker payout − cost basis
256
+ gross margin = profit / worker payout
257
+ target payout = ceil(cost basis / (1 − target margin))
258
+ minimum payout = ceil(cost basis / (1 − minimum margin))
259
+ affordable = min(requester budget, escrow amount)
260
+ recommendation = min(target payout, affordable)
261
+ ```
262
+
263
+ A 50% gross margin means **payout = 2 × cost**. A 50% markup instead means 1.5 × cost; these are different calculations. The policy uses gross margin.
264
+
265
+ For cost basis **100 raw units**:
266
+
267
+ | Budget and escrow availability | Recommended worker amount | Result |
268
+ |---|---:|---|
269
+ | At least 200 | 200 | Profit 100; target 50% margin |
270
+ | 150 | 150 | Profit 50; below target, above the minimum |
271
+ | 105 | 105 | Below minimum payout of 112; owner approval required |
272
+ | 80 | 80, blocked without approval | Below cost; decline or seek explicit owner approval |
273
+ | Cost basis is 0 | 0 | Full payer refund is appropriate |
274
+
275
+ A recommendation is **not** authorization, acceptance or payment. Theoretical `target_payout`, `min_payout` and `max_payout` may exceed the budget. `recommended_payout` is the capped amount; the guard still rejects an unacceptable recommendation until an explicit permitted exception exists.
276
+
277
+ | Policy/projection field | Meaning |
278
+ |---|---|
279
+ | `cost_basis_raw` | App-reported cost snapshot; not audited on-chain cost truth |
280
+ | `target_payout` | Amount needed for the desired gross margin, rounded up |
281
+ | `min_payout` | Amount needed for the minimum margin, rounded up |
282
+ | `max_payout` | Upper payout threshold derived from maximum margin |
283
+ | `affordable` | Lesser of budget and escrow |
284
+ | `recommended_payout` | Capped default worker proposal |
285
+ | `expected_profit`, `expected_margin_bps` | Economics of the recommendation |
286
+ | `projected_profit_raw`, `projected_margin_bps` | Economics of the current proposed allocation |
287
+ | `realized_payout_raw`, `realized_profit_raw`, `realized_margin_bps` | Actual result; null until settlement evidence freshly revalidates |
288
+ | `target_status` | `target_met` or `below_target`; advisory, based on the app's cost basis |
289
+
290
+ Costs freeze at the first closing proposal attempt, not on every counteroffer. Pure helpers do not sign or broadcast. Zero payout has undefined margin (`null`), and displayed basis points are conservatively rounded down. The backend keeps costs/profit private to the worker and explicitly permitted operators; requester projections expose allocation and confirmation status only.
291
+
292
+ ## Define a job agreement
293
+
294
+ The application factory validates a typed task and reference escrow terms. A quote supplies `terms`:
295
+
296
+ ```js
297
+ const terms = {
298
+ task: {
299
+ id: 'brief',
300
+ type: 'research',
301
+ description: 'Produce a sourced research brief',
302
+ deliverables: [{id: 'report', media_type: 'text/markdown', description: 'Research brief'}],
303
+ acceptance: {validator: 'brief-checker', version: '1', criteria: {minSources: 3}},
304
+ },
305
+ escrow: {
306
+ chain_id: String(chainId), contract: contractAddress, token: tokenAddress,
307
+ amount: '100', // Raw token units
308
+ payer: payerAddress, worker: workerAddress,
309
+ verifier: verifierAddress, arbitrator: arbitratorAddress,
310
+ work_deadline: workDeadline,
311
+ review_deadline: reviewDeadline,
312
+ resolution_deadline: resolutionDeadline,
313
+ timeout_worker_bps: 2500,
314
+ },
315
+ };
316
+ ```
317
+
318
+ | Field | Meaning |
319
+ |---|---|
320
+ | Job `id` | Stable workflow identifier; keep it instead of inventing/reconstructing IDs later |
321
+ | `participants` | Authenticated requester, worker, verifier and arbitrator **agent IDs**; not wallet addresses |
322
+ | `task.type` / `description` | Your use case; no research-only task logic is hardcoded |
323
+ | `task.deliverables` | Required artifacts, unique IDs, media types and descriptions |
324
+ | `task.acceptance` | Named/versioned validator and criteria used by your actual validation callback |
325
+ | `escrow.chain_id` | Decimal-string chain ID; factory configuration uses a number |
326
+ | `escrow.contract` / `token` | The agreed deployment and ERC-20 token |
327
+ | `escrow.amount` | Positive raw-unit decimal string; no floats or scientific notation |
328
+ | `payer`, `worker`, `verifier`, `arbitrator` | Agreed wallet addresses mapped from the participant registry |
329
+ | `work_deadline` | Unix seconds; last time permitted for submission |
330
+ | `review_deadline` | Unix seconds; last time the verifier may approve the submitted result |
331
+ | `resolution_deadline` | Unix seconds; deadline after which the contract's timeout exit becomes available |
332
+ | `timeout_worker_bps` | Agreed worker share after a submitted-job timeout; 2500 means 25%, not a profit target |
333
+
334
+ Require `work_deadline < review_deadline < resolution_deadline`. Exact eligibility depends on the action and reference contract; see [ESCROW.md](./ESCROW.md). **Profit margin, timeout share and spending limits are separate settings.**
335
+
336
+ Quotes receive a canonical agreement hash and monotonic revision. Both parties accept that exact returned pair. Requoting clears acceptances. Use returned terms/hashes, particularly after reopening; do not invent a hash from the original input or confuse the workflow ID with the on-chain bytes32 job ID.
337
+
338
+ ## End-to-end job lifecycle
339
+
340
+ ```mermaid
341
+ flowchart TD
342
+ Draft["draft: create job"] --> Quoted["quoted: propose terms"]
343
+ Quoted --> Accepted["accepted: both parties approve revision and hash"]
344
+ Accepted --> Funded["funded: confirmed custody"]
345
+ Funded --> Running["running: fresh eligibility gate"]
346
+ Running --> Submitted["submitted: confirmed artifact commitment"]
347
+ Submitted --> Verified["verified: artifact checks and chain approval"]
348
+ Verified --> Paid["paid: confirmed worker payout"]
349
+ Draft --> Cancelled["cancelled: no SDK funding attempt"]
350
+ Quoted --> Cancelled
351
+ Accepted --> Cancelled
352
+ Funded --> Refunded["refunded: expired without submission"]
353
+ Running --> Refunded
354
+ Submitted --> Disputed["disputed: settlement requires resolution"]
355
+ Verified --> Disputed
356
+ Disputed --> Settled["settled: arbitration or eligible timeout"]
357
+ ```
358
+
359
+ The chart shows common completion and dispute paths; funded/running jobs also support direct disputes and closing negotiation.
360
+
361
+ Use `workflow.act(id, action, input, {actor, idempotencyKey})`, or the authenticated client/HTTP equivalent. **Proceed to the next action only when `job.pending` is null and the expected state/evidence is present.**
362
+
363
+ | Step | Action / actor | Input and actual outcome |
364
+ |---|---|---|
365
+ | 1 | `workflow.create` / requester | `id`, participants, optional public metadata; creates `draft` |
366
+ | 2 | `quote` / requester or worker | `{terms}`; creates a revision/hash and clears previous acceptances |
367
+ | 3 | `accept` / each party | `{revision, agreement_hash}`; only bilateral acceptance produces `accepted` |
368
+ | 4 | Worker wallet consent | Sign `workerConsent(job, deployment)` typed data, using the agreed worker wallet |
369
+ | 5 | `fund` / requester | `{worker_signature}`; token allowance and funding may be separate journaled steps; only confirmed custody is `funded` |
370
+ | 6 | `start` / worker | Fresh chain/safety gate, then `running`; application worker performs actual work |
371
+ | 7 | `submit` / worker or runner | `{result_hash}` for actual artifact bytes; one immutable on-chain submission |
372
+ | 8 | `verify` / verifier | Validate artifact against criteria and confirm approval; then `verified` |
373
+ | 9 | `release` / verifier | Confirm the contract's worker transfer; then `paid` |
374
+ | 10 | Read receipts | Revalidate chain evidence before presenting payment/refund as current fact |
375
+
376
+ Wallet helpers are exported from `@allorc/a423-sdk/escrow`:
377
+
378
+ ```js
379
+ import {workerConsent} from '@allorc/a423-sdk/escrow';
380
+ const typed = workerConsent(job, deployment);
381
+ const workerSignature = await workerWallet.signTypedData(
382
+ typed.domain, typed.types, typed.value,
383
+ );
384
+ // Use workerSignature as fund.input.worker_signature through the payer's authorized action.
385
+ ```
386
+
387
+ `workerWallet` is your wallet integration, not a server-generated default key. With a browser wallet, use the authenticated worker-consent endpoint and wallet's typed-data signing UI. Protect private keys and keep authority keys separate from chain signers.
388
+
389
+ ## Cancellation and partial-work settlement
390
+
391
+ After funding, ordinary `cancel` cannot simply unlock the money. Instead negotiate a **closing settlement**: worker compensation for agreed partial work/expenses, with the remainder refunded. This closes the entire job; it is not a continuing-work milestone payment.
392
+
393
+ ```mermaid
394
+ flowchart TD
395
+ Active["Funded, running, submitted or verified job"] --> Proposal["Publish closing proposal; pause work"]
396
+ Proposal --> Decision{"Agreement on the split?"}
397
+ Decision -->|"Counter"| Counter["New revision and proposal hash"]
398
+ Counter --> Decision
399
+ Decision -->|"Approve"| Sign["Both wallets sign exact current terms"]
400
+ Sign --> Instruction["Save accepted cancellation JSON"]
401
+ Instruction --> Execute["Encode and send escrow transaction"]
402
+ Execute --> Confirm["Confirm actual payout and refund"]
403
+ Confirm --> Receipt["Final cancellation receipt; locked amount zero"]
404
+ Decision -->|"Dispute"| Arbiter["Arbitrator resolves supported split"]
405
+ Arbiter --> Confirm
406
+ Decision -->|"Original deadline reached"| Deadline["Eligible refund or timeout transaction"]
407
+ Deadline --> Confirm
408
+ ```
409
+
410
+ For a funded 100-unit job, an agreed worker allocation of 30 means **30 to the worker, 70 to the payer**, once confirmed. JSON is an instruction artifact; the contract validates typed signatures and amounts, not arbitrary JSON.
411
+
412
+ ```js
413
+ import {settlementConsent} from '@allorc/a423-sdk/escrow';
414
+ // Worker proposes a closing settlement. If the factory's profit policy is enabled,
415
+ // omit worker_amount to use its capped default instead of an explicit '30'.
416
+ // An explicit amount must still satisfy the enabled policy.
417
+ let job = await app.workflow.act(id, 'propose_settlement', {
418
+ worker_amount: '30',
419
+ expected_revision: 0,
420
+ expires_at: proposalExpiry, // Must respect the original resolution deadline
421
+ reason: 'Source collection is complete; remaining work is cancelled',
422
+ claims: [{kind: 'work', description: 'Checked source list', evidence: ['artifact:sources']}],
423
+ }, {actor: workerId, idempotencyKey: 'closing-proposal-1'});
424
+ // Wait/reconcile if job.pending before requesting signatures.
425
+
426
+ const closing = settlementConsent(job, deployment);
427
+ // Collect both wallet signatures over closing.domain/types/value.
428
+ job = await app.workflow.acceptCancellation(id, {
429
+ expected_revision: job.negotiation.current.escrow_version,
430
+ proposal_hash: job.negotiation.current.proposal_hash,
431
+ payer_signature: payerSignature,
432
+ worker_signature: workerSignature,
433
+ }, {actor: payerId, idempotencyKey: 'closing-accept-1', forward: false});
434
+
435
+ const instruction = await app.workflow.cancellationInstruction(id, payerId);
436
+ // Acceptance is now recorded, but tokens are still locked.
437
+ job = await app.workflow.act(id, 'execute_cancellation', {
438
+ instruction_id: instruction.id,
439
+ }, {actor: payerId, idempotencyKey: 'closing-execute-1'});
440
+ // After confirmed completion, use a fresh receipt to show the actual split.
441
+ ```
442
+
443
+ Import `settlementConsent` from `/escrow`. Counteroffers use `counter_settlement` with the current `expected_revision`. Claims describe work and agreed expenses; never put private AI usage/cost/margin data in public claims or peer messages.
444
+
445
+ The subflow distinguishes **negotiating → accepted instruction → execution pending/failed → completed receipt**. Accepted JSON permanently says `funds_status: 'not_executed'`; the separate final receipt proves execution. The default `acceptCancellation` forwards after acceptance; `forward:false` separates the steps. The SDK blocks counters after a saved acceptance. On-chain stale revisions, expired signatures or changed proposals cannot authorize the old split.
446
+
447
+ If parties disagree, original deadline exits and supported arbitration remain available. Unsubmitted work can reach an expiry refund; submitted work uses its agreed timeout allocation. Neither the keeper nor a deadline guarantees inclusion during chain censorship, RPC failure or broken token transfers. See [CANCELLATION.md](./CANCELLATION.md) and [SETTLEMENT.md](./SETTLEMENT.md).
448
+
449
+ ## Decline, cancel and reopen before funding
450
+
451
+ Cancelled jobs keep their IDs and history. The requester uses `cancel`; the worker uses `decline`. Both are allowed from `draft`, `quoted` or `accepted`, **only before an SDK funding attempt**. Further quotes/acceptance are rejected until explicit eligible reopening.
452
+
453
+ ```mermaid
454
+ flowchart TD
455
+ Cancelled["Cancelled job with retained history"] --> Eligible{"Any funding attempt or financial close?"}
456
+ Eligible -->|"Yes"| Terminal["Do not reopen; reconcile or create a new linked job"]
457
+ Eligible -->|"No"| Approvals{"Worker declined or both had accepted?"}
458
+ Approvals -->|"Yes"| Both["Requester and worker approve reopening"]
459
+ Approvals -->|"No"| Requester["Requester approves reopening"]
460
+ Both --> New["Same job ID; new generation; active agreement cleared"]
461
+ Requester --> New
462
+ New --> Fresh["Fresh quote and fresh bilateral acceptance"]
463
+ ```
464
+
465
+ ```js
466
+ let job = await app.workflow.act(id, 'decline', {
467
+ reason: 'Cannot meet the proposed budget', expected_epoch: currentJob.epoch || 0,
468
+ }, {actor: workerId, idempotencyKey: 'decline-1'});
469
+
470
+ const cancellation_id = job.pre_funding_cancellations.at(-1).id;
471
+ // For a worker decline, collect both approvals, using separate authenticated actors.
472
+ await app.workflow.act(id, 'reopen', {cancellation_id}, {
473
+ actor: payerId, idempotencyKey: 'reopen-payer-1',
474
+ });
475
+ job = await app.workflow.act(id, 'reopen', {cancellation_id}, {
476
+ actor: workerId, idempotencyKey: 'reopen-worker-1',
477
+ });
478
+ // Next quote/accept/fund/cancel/decline must include expected_epoch: job.epoch.
479
+ ```
480
+
481
+ A requester cancellation before bilateral acceptance can be reopened by the requester alone. Both-party acceptance requires both reopening approvals. Reopening never restores old acceptances: it increments `epoch`, keeps quote revisions monotonic and commits the new generation in returned agreement terms. Old completed idempotency keys cannot execute again.
482
+
483
+ A pending or even rejected funding attempt blocks this shortcut, because signed intent may exist outside a cached state. Paid/refunded/settled jobs remain final; repeat work with a new job ID linked through your app's metadata. Off-chain cancellation cannot erase already shared wallet signatures; old externally funded agreements must be reconciled under their original terms.
484
+
485
+ ## Use it from Next.js or another agent
486
+
487
+ Mount your configured handler using Next.js's **Node runtime**. The application handler serves more than `/api/a423/*`; use a catch-all covering all its paths, or mount the additional routes explicitly.
488
+
489
+ ```ts
490
+ // app/api/[...path]/route.ts — adjust import to your configured backend module.
491
+ // If you have existing API routes, mount only the intended A423 paths there.
492
+ import {handler} from '@/lib/a423';
62
493
  export const runtime = 'nodejs';
63
- export { handler as GET, handler as POST };
494
+ export const GET = handler;
495
+ export const POST = handler;
64
496
  ```
65
497
 
66
- Names such as `loadApprovedRawTokenLimits`, `provider`, `signerFor`, `validateArtifact`, `authenticate`, `revalidate` and `consentFor` are application services, not invented SDK exports. Complete recipes and actual exports are in OPERATIONS.md and TypeScript declarations. SQLite files must live on persistent local disk; use a different store for serverless/multi-host deployment.
498
+ Existing more-specific routes such as `/api/state` need their own integration; a catch-all does not replace them. Use the SDK's single-host SQLite driver only where persistent local disk is available; choose an appropriately implemented durable store for other hosting models.
67
499
 
68
- ## Integration surfaces
500
+ External agents call your backend with credentials; they do not need database access or private signer keys:
69
501
 
70
- `createApplicationHandler` serves authenticated workflow routes under `/api/a423`, projections under `/api/state`, resumable events at `/api/a423-events`, wallet typed-data at `/api/a423-worker-consent/:id`, and freshly revalidated JSON/PDF at `/api/a423-receipts/:id[.pdf]`. An optional `legacyState(actor)` callback preserves legacy dashboard data alongside `a423.jobs` and `a423.wallet_transactions`.
502
+ ```js
503
+ import {createWorkflowClient} from '@allorc/a423-sdk/workflow';
71
504
 
72
- `createChatTools` maps offer, quote, accept, fund, start, submit, verify, release, refund, dispute, resolve and timeout tools to the same workflow records. Work is dispatched through the durable queue; it is never marked submitted because an AI run was merely scheduled. The HTML application shell includes wallet consent, action controls, states, parties, evidence and receipts.
505
+ const client = createWorkflowClient({
506
+ baseUrl: 'https://your-app.example/api/a423',
507
+ headers: async () => ({authorization: `Bearer ${await credentialForThisAgent()}`}),
508
+ });
509
+ const job = await client.get('research-001');
510
+ // The credential establishes the actor; a caller-supplied ID is not authentication.
511
+ ```
73
512
 
74
- Run the supplied shell and operational worker with an explicitly configured application module:
513
+ | Endpoint | Purpose |
514
+ |---|---|
515
+ | `POST /api/a423/jobs` | Create draft job |
516
+ | `GET /api/a423/jobs/:id` | Read an accessible job, including cancelled history |
517
+ | `POST /api/a423/jobs/:id/actions` | `{action, input}` with `idempotency-key` |
518
+ | `POST /api/a423/jobs/:id/reconcile` | Observe the current pending operation |
519
+ | `GET /api/a423/jobs/:id/subflows` | Inspect child subflows |
520
+ | `POST /api/a423/jobs/:id/cancellation/accept` | Save exact signed acceptance and optionally forward |
521
+ | `POST /api/a423/jobs/:id/cancellation/execute` | Execute a saved accepted instruction |
522
+ | `GET /api/a423/jobs/:id/cancellation/instruction` | Read the accepted instruction, not proof of payment |
523
+ | `GET /api/a423/jobs/:id/cancellation/receipt` | Confirmed cancellation receipt; application handler freshly revalidates it |
524
+ | `GET /api/a423-worker-consent/:id` | Worker funding typed-data payload |
525
+ | `GET /api/a423-settlement-consent/:id` | Current closing typed-data payload |
526
+ | `GET /api/a423-profit/:id` | Role-filtered economics; requires configured profit policy |
527
+ | `GET /api/state` | Accessible workflow and wallet-history projections |
528
+ | `GET /api/a423-receipts/:id[.pdf]` | Fresh JSON/PDF receipt projection |
529
+ | `GET /api/a423-events` | Authenticated resumable SSE events |
75
530
 
76
- ```sh
77
- A423_CONFIG_MODULE=/absolute/path/config.mjs node examples/operations-server.mjs
78
- A423_CONFIG_MODULE=/absolute/path/config.mjs node scripts/operations-worker.mjs
531
+ Credentials come from the configured authority's approved organization/agent enrollment and challenge flow. They expire and can be revoked; use the renewal helpers in [OPERATIONS.md](./OPERATIONS.md). Do not use public agent IDs, an `x-agent-id` header or demo shared keys as production authentication.
532
+
533
+ `app.chatTools(actor)` exposes the same workflow plus `list_jobs`, `inspect_settlement_policy` and `reconcile_job`. Inject `app.profit.prompt({role:'worker'})` when policy is enabled; the SDK does not rewrite a remote LangGraph/LLM system prompt automatically. Actual AI execution still belongs to your application runner.
534
+
535
+ ## Retries, receipts and recovery
536
+
537
+ | Argument / concept | Meaning |
538
+ |---|---|
539
+ | `actor` | Trusted authenticated agent ID; backend calls must bind it to credentials |
540
+ | `idempotencyKey` / `idempotency-key` | Stable key for one actor's exact action/input; keep it on retry, use a new key for new intent |
541
+ | `revision`, `agreement_hash` | Exact returned quote revision/hash for acceptance |
542
+ | `expected_revision` | Existing on-chain closing revision before a proposal/counter; current proposal revision when accepting |
543
+ | `proposal_hash` | Commitment to the exact closing proposal and claims |
544
+ | `worker_amount`, `payer_amount` | Raw allocations summing to escrow; payer amount is derived unless supplied for checking |
545
+ | `expires_at` | Proposal expiry in Unix seconds; cannot extend original resolution deadline |
546
+ | `result_hash` | Nonzero bytes32 commitment to actual artifact bytes |
547
+ | `reason_hash` | Nonzero bytes32 dispute-reason commitment |
548
+ | `expected_epoch` | Current reopening generation; required after reopening for quote/accept/fund/cancel/decline |
549
+ | `cancellation_id` | Exact pre-funding cancellation record being reopened |
550
+ | `instruction_id` | Accepted cancellation JSON ID being executed |
551
+ | `transaction_hash` | Explicit existing transaction evidence to adopt; validated, never accepted as payment by itself |
552
+ | `job.pending` | Durable operation intent that has not been definitively confirmed/rejected |
553
+
554
+ Effects can take more than one request. The SDK persists intent before external actions, journals signed transactions and reconciles after interruptions. Do not issue a different action while one is pending.
555
+
556
+ ```js
557
+ const observed = await app.workflow.reconcile(id, actor); // Observation only
558
+ // If a next transaction step is still needed, explicitly authorize execution:
559
+ const resumed = await app.workflow.resume(id, actor);
79
560
  ```
80
561
 
81
- The shell binds to localhost. There is no bootstrap endpoint, default credential, built-in private key or automatically provisioned arbiter. Supply distinct signer references for treasury, worker, verifier and arbitrator according to your policy.
562
+ Only call `resume` if execution is intended and still permitted. A keeper tick with execution enabled may resume actions; observation alone never authorizes a new transaction. Keep cost/price changes out of retries of the same intent.
563
+
564
+ Use `freshReceipts` / `freshCancellationReceipt` or application receipt endpoints when displaying current chain-backed payment. Raw workflow getters return durable records and integrity checks, not guaranteed fresh finality. Reorg monitoring can quarantine evidence; it does not silently erase paid history or reverse blockchain transfers.
565
+
566
+ The narrowly scoped `workflow.rejectPendingOperation(id, {operation_id, reason}, {actor})` can reject a provably impossible malformed submit. It refuses valid encodings, transaction references, journaled intent, on-chain submissions and safety holds. It is not a force-unlock, refund or generic “clear pending” button. See [PROFIT-POLICY.md](./PROFIT-POLICY.md#supported-malformed-pending-recovery).
82
567
 
83
- ## Upgrade from 0.3
568
+ ## Supported scenarios and boundaries
84
569
 
85
- - `authorizeTransaction` is now mandatory for escrow adapters. Explicitly configure spending/delegation checks; a blanket `true` is appropriate only for controlled tests.
86
- - The transaction-store interface adds `list` and atomic `appendReplacement`. The supplied SQLite and memory implementations include them.
87
- - Use a new deployment for ERC-1271 support. Existing 0.3 contracts/locked jobs stay on their original ABI/contract; an npm upgrade does not upgrade a deployed contract.
88
- - `workflow.resume(id, actor)` explicitly authorizes the next pending step. `reconcile` remains observe-only. `runMaintenanceOnce` only resumes when `allowExecute: true`.
89
- - Reference evidence now includes settlement reason/dispute commitment. Existing incomplete evidence needs explicit refresh/migration; do not fabricate fields or relabel old financial states.
90
- - `getSafety`/`setSafety` enable quarantine. Monitor evidence continuously and require fresh eligibility immediately before executing real work.
570
+ | Scenario | Supported path / boundary |
571
+ |---|---|
572
+ | Research, coding or another artifact task | App-defined task/validator/runner; same agreement and escrow primitives |
573
+ | Straightforward completion | Create → quote → both accept → fund → start → submit → verify → release |
574
+ | Cannot agree on price before funding | Requester cancels or worker declines; history remains |
575
+ | Resume an eligible cancelled negotiation | Explicit reopening approvals, same ID, fresh generation/agreement |
576
+ | Cancel after partial work | Negotiate closing compensation and refund the remainder; closes the job |
577
+ | Zero completed-work cost | Optional policy recommends full refund, subject to signed settlement or eligible exit |
578
+ | Budget cannot meet target margin | Capped positive-margin recommendation; below minimum/cost needs named owner approval |
579
+ | First offer or stale offer | Counter policy plus exact revision/hash/signature checks |
580
+ | Unsubmitted job expires | Eligible expiry refund; transaction still required |
581
+ | Disagreement after funding but before submission | Supported funded dispute/closing negotiation; original expiry refund remains eligible under its conditions |
582
+ | Submitted work is disputed | Trusted arbiter split or original timeout allocation |
583
+ | Verifier/arbiter unavailable | Eligible timeout under agreed terms; not a quality oracle |
584
+ | RPC outage, crash or stuck transaction | Pending state, durable journal, reconciliation/resume and bounded explicit fee replacement |
585
+ | Different networks or tokens | Allowlisted deployments; reference contract has one token per deployment |
586
+ | Milestones / continuing partial releases | Not supported by this reference contract |
587
+ | Resubmitting work after an immutable submission | Not supported |
588
+ | Fee-on-transfer/rebasing tokens | Not supported; use a reviewed standard ERC-20 |
589
+ | Contract pause, upgrades or owner withdrawal | Not provided by this reference contract |
590
+ | Independent audit or production assurance | Not claimed |
591
+
592
+ SDK profit settings cannot constrain direct external contract calls or prove reported costs are real. Deadline/arbitration outcomes can realize a loss; report the actual payout. Authority approval establishes trust within your registry, not automatic legal-company verification. HSM/KMS signing integration does not provision a vendor account or certify hardware.
593
+
594
+ ## Modules and detailed guides
595
+
596
+ | Import / guide | What to use it for |
597
+ |---|---|
598
+ | `@allorc/a423-sdk/workflow` | Agreements, roles, CAS workflow, typed HTTP client/handler, cancellation and reopening |
599
+ | `@allorc/a423-sdk/escrow` | Reference contract ABI/bytecode, chain adapters, wallet consent, journaled settlement, registry/indexer/signing helpers |
600
+ | `@allorc/a423-sdk/policy` | Pure profit calculator, configuration, private cost service, projections and prompt fragments |
601
+ | `@allorc/a423-sdk/storage` | Single-host durable SQLite jobs, transaction journal, queue, KV and spending reservations |
602
+ | `@allorc/a423-sdk/authority` | Approved enrollment, challenge credentials, revocation and renewal |
603
+ | `@allorc/a423-sdk/operations` | Application factory, execution/maintenance workers, spending, fresh receipts, events/webhooks and app/chat bridge |
604
+ | Root `@allorc/a423-sdk` | Preserved older simple API and memory-store utility; root simple API is distinct from `/workflow` |
605
+ | [ESCROW.md](./ESCROW.md) | Contract fields, roles, deadlines, deployment and custody rules |
606
+ | [OPERATIONS.md](./OPERATIONS.md) | Authority setup, storage, signing, workers, events, deployment and operations |
607
+ | [SETTLEMENT.md](./SETTLEMENT.md) | On-chain proposals, counters, arbitration, fallback and ledger |
608
+ | [CANCELLATION.md](./CANCELLATION.md) | Accepted JSON, final receipts, retry/recovery and controlled reopening |
609
+ | [PROFIT-POLICY.md](./PROFIT-POLICY.md) | Threshold math, privacy, owner exceptions, initialization and app migration |
610
+ | [INTEGRATION.md](./INTEGRATION.md) | Existing-app migration checklist |
611
+ | [LEGACY.md](./LEGACY.md) | Older root API; do not confuse it with the reference escrow workflow |
91
612
 
92
- ## Boundaries
613
+ See [examples/README.md](./examples/README.md) to distinguish the modern workflow examples from the preserved older root API.
614
+
615
+ The complete public signatures are in [workflow types](./src/workflow.d.ts), [factory/operations types](./src/operations/index.d.ts), [policy types](./src/policy/index.d.ts), [escrow types](./src/escrow/index.d.ts), [authority types](./src/authority/index.d.ts) and [storage types](./src/storage/index.d.ts).
616
+
617
+ The source shell and worker use an explicitly configured module exporting `applicationOptions` and `workerOptions`:
618
+
619
+ ```sh
620
+ A423_CONFIG_MODULE=/absolute/path/config.mjs node examples/operations-server.mjs
621
+ A423_CONFIG_MODULE=/absolute/path/config.mjs node scripts/operations-worker.mjs
622
+ ```
93
623
 
94
- This remains experimental and independently unaudited. Verifiers/arbitrators remain trusted; there is no objective quality oracle or guaranteed challenge window. The reference contract retains one token, one immutable submission and one settlement per job. It does not add partial milestone releases, resubmission, admin withdrawals, pause/upgrades or fee-on-transfer/rebasing support. Separate deployments are selected through the registry. A timeout needs a transaction and a functioning chain/token.
624
+ The shell binds to localhost. There is no automatic bootstrap, default credential, built-in key, deployed contract or default arbiter. [examples/operations-dashboard.html](./examples/operations-dashboard.html) displays states, wallet actions, accepted JSON, receipts and role-aware economics.
95
625
 
96
- KMS/HSM providers are integrated through a checked digest signer; the package does not provision vendor accounts or certify hardware. Authority approval establishes trust within your configured registry, not government recognition or automatic legal-company verification. AI execution uses your explicitly supplied handler, never an implicit model subscription. Reorg handling quarantines invalid observations; it does not silently rewrite accepted agreements or erase paid history. Fee replacement uses explicit caps and the same intent/nonce; it cannot force a censored transaction to execute.
626
+ ## Develop and publish
97
627
 
98
- ## Validation and release
628
+ From the source repository:
99
629
 
100
630
  ```sh
101
631
  npm ci --ignore-scripts
@@ -104,4 +634,15 @@ npm test
104
634
  npm pack --dry-run --ignore-scripts
105
635
  ```
106
636
 
107
- Tests compile and deploy the contract on a local EVM, exercise payment/refund/dispute/timeout, check consent and permissions, and cover credential-protected HTTP → durable SQLite → escrow → fresh PDF evidence. Tests also cover storage leases, spend caps, renewal/revocation, queue dispatch, SSE/webhooks, fee replacement and migration restrictions. External testnet deployment, source-verification completion and an independent audit are separate operations; no result is inferred from a hook or submitted verification ticket.
637
+ The tests compile/deploy a local EVM contract and cover custody, consent, settlement, cancellation/reopening, policy, privacy, pending recovery, SQLite persistence, HTTP receipts and operational services. Local passing tests are not proof of a public deployment, production readiness or an independent audit.
638
+
639
+ The deployment script is **dry-run by default**; read [scripts/deploy-escrow.mjs](./scripts/deploy-escrow.mjs) and [OPERATIONS.md](./OPERATIONS.md) before an explicit broadcast. A source-verification submission is not the same as confirmed verification.
640
+
641
+ For maintainers, pushing code to GitHub and publishing to npm are separate:
642
+
643
+ ```sh
644
+ npm login
645
+ npm publish --access public
646
+ ```
647
+
648
+ `prepublishOnly` runs TypeScript and the full test suite. Confirm the intended version is unused and review the packed files first. Do not republish an existing npm version. Older funded deployments/records retain their original custody and ABI; see migration guides before upgrading.