@allorc/a423-sdk 0.5.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.
@@ -0,0 +1,106 @@
1
+ # Cancellation subflow — 0.6.0
2
+
3
+ [Start here: installation, arguments and lifecycle maps](./README.md) · [All examples](./examples/README.md)
4
+
5
+ A job has one parent lifecycle and an optional cancellation subflow. Cancellation can negotiate compensation for completed work and agreed expenses, then close the job. It does not permit continuing work after a partial release.
6
+
7
+ The flow is:
8
+
9
+ 1. Publish `propose_settlement`, or alternate `counter_settlement`, with the worker's proposed amount, reason, claims, original deadline and expected revision. The payer receives the remainder. Funds remain in the escrow contract, never the worker's wallet. Work pauses.
10
+ 2. Both parties review the exact current proposal and sign the `ClosingSettlement` typed data returned by `settlementConsent`. Signature authorization is bound to chain, contract, agreement, job, revision, amounts, proposal hash and expiry.
11
+ 3. Accept using `workflow.acceptCancellation`. The adapter verifies both EOA/ERC-1271 signatures and current on-chain terms/proposal. The workflow atomically saves an immutable JSON instruction and marks the child flow `accepted` before any closing transaction is signed or broadcast. The instruction says `funds_status: "not_executed"`.
12
+ 4. Forward the saved instruction. The adapter validates the JSON against the job, encodes `settleByConsent`, applies the signing/broadcast policy and journals the transaction. The contract verifies signatures and distributes the exact split atomically. It does not parse or trust JSON.
13
+ 5. After the configured confirmations and matching settlement evidence, save the final cancellation receipt. It records actual worker/payer allocations, zero remaining locked amount, transaction/block evidence and the accepted instruction ID.
14
+
15
+ An acceptance record is an authorization receipt, not proof of payment. Pending, rejected, expired or unavailable execution must not display a completed payment receipt.
16
+
17
+ ## Server API
18
+
19
+ ```js
20
+ const proposed = await workflow.act(id, 'propose_settlement', {
21
+ worker_amount: '30', // raw token units; payer gets total minus 30
22
+ expected_revision: 0,
23
+ expires_at: proposalExpiry, // no later than original resolution deadline
24
+ reason: 'Research completed; remaining work cancelled',
25
+ claims: [{ kind: 'work', description: 'Checked source list', evidence: ['artifact:sources'] }]
26
+ }, { actor: workerId, idempotencyKey: 'cancel-proposal-1' });
27
+
28
+ const job = await workflow.acceptCancellation(id, {
29
+ expected_revision: proposed.negotiation.current.escrow_version,
30
+ proposal_hash: proposed.negotiation.current.proposal_hash,
31
+ payer_signature: payerSignature,
32
+ worker_signature: workerSignature
33
+ }, { actor: payerId, idempotencyKey: 'cancel-accept-1' });
34
+ ```
35
+
36
+ The default forwards immediately AFTER saving acceptance. To separate acceptance and execution, pass `forward: false`, then:
37
+
38
+ ```js
39
+ const instruction = await workflow.cancellationInstruction(id, payerId);
40
+ await workflow.act(id, 'execute_cancellation', {
41
+ instruction_id: instruction.id
42
+ }, { actor: payerId, idempotencyKey: 'cancel-execute-1' });
43
+ ```
44
+
45
+ `createEscrowApplication({forwardCancellations:false,...})` stages HTTP acceptance. An explicitly enabled keeper (`allowExecute:true` / factory `keeperEnabled:true`) forwards accepted instructions. Observe-only reconciliation never creates a new payment transaction.
46
+
47
+ | Route, relative to `/api/a423/jobs/:id` | Meaning |
48
+ |---|---|
49
+ | `GET /subflows` | Child records and active child ID |
50
+ | `GET /cancellation` | Cancellation child or null |
51
+ | `POST /cancellation/accept` | Body: revision/hash and both signatures; save then optionally forward |
52
+ | `POST /cancellation/execute` | Body: `instruction_id`, optionally external `transaction_hash` |
53
+ | `GET /cancellation/instruction` | Accepted instruction; unavailable until acceptance |
54
+ | `GET /cancellation/receipt` | Final receipt; HTTP 409 before confirmation |
55
+
56
+ POST requests require the usual credential and `idempotency-key`. The typed workflow client exposes the corresponding methods. The application handler freshly revalidates final receipt evidence; unavailable/changed evidence returns 503. The lower-level workflow getter validates record integrity only; use `freshCancellationReceipt` with an evidence revalidator for current chain proof.
57
+
58
+ ## State and recovery
59
+
60
+ Child statuses are `proposal_pending`, `negotiating`, `acceptance_pending`, `accepted`, `execution_pending`, `execution_failed`, `disputed`, `proposal_rejected`, and `completed`. Its ID and `parent_job_id` link it to the parent job; events retain proposal revisions and receipt/instruction IDs. The job remains `settlement_negotiation` while acceptance/execution is pending. The separate escrow ledger changes only with confirmed chain evidence.
61
+
62
+ Keep the same actor, request and idempotency key on retries. Accepted JSON survives storage restarts. Recovery observes the durable transaction journal; it must not send a duplicate payout. If approval is saved but execution was never started, call acceptance again or use an authorized keeper. A rejected execution preserves the accepted instruction and locked ledger; retry deliberately using a new execution key after resolving its cause. Pending execution must be reconciled/resumed first.
63
+
64
+ The SDK blocks counters after instruction acceptance. External transactions can still change the chain state; forwarding checks fresh state and the contract rejects stale signatures. Original expiry refunds, arbitration and timeout allocations remain available under their original conditions. Those exits create a final receipt with their actual outcome and `instruction_id:null`; `prior_instruction_id` preserves the unexecuted acceptance reference. A failed acceptance or proposal produces no payment receipt.
65
+
66
+ ## Compatibility and limits
67
+
68
+ 0.6 reuses the 0.5 `A423Escrow` contract. No new deployment is needed for a verified 0.5 deployment. Existing 0.4/older contracts do not support these methods; retain their original adapter until custody is closed. Existing records without subflows remain readable; a cancellation child is added when negotiation starts.
69
+
70
+ The legacy `accept_settlement` action remains available for a direct counterparty transaction or dual-signature relay. It can produce a final receipt without a separately saved accepted instruction. Use the new staged flow when the UI needs acceptance before execution.
71
+
72
+ Amounts use raw integer token units, never floating point. Signatures and claims are public authorization material, not private keys. Persist records securely and avoid exposing participant-specific endpoints without authentication. Pure JSON constructors validate structure/binding; they do not independently prove cryptographic consent or chain finality. The reference adapters perform those checks. No live-chain deployment, external app migration or independent security audit is implied.
73
+
74
+ ## Pre-funding decline and reopening — 0.7
75
+
76
+ Before funding, the requester can `cancel` and the worker can `decline` from `draft`, `quoted` or `accepted`. Both close the parent job as `cancelled`; neither deletes it. A quote sent afterward is rejected. No token refund or payment receipt is issued because this path has not taken custody of funds. The normal job event and `pre_funding_cancellations` record document the cancellation.
77
+
78
+ Each cancellation record has a unique `id`, cancelling actor/action/reason, prior state/agreement/acceptances, generation (`epoch`), reopening approvals and reopening timestamp. Participants can read it through the existing job/client GET API. A cancelled job ID cannot be created again or removed through ordinary protocol actions.
79
+
80
+ ```js
81
+ let job = await workflow.act(id, 'decline', {
82
+ reason: 'Cannot meet the proposed budget',
83
+ expected_epoch: currentJob.epoch || 0,
84
+ }, {actor: workerId, idempotencyKey: 'decline-1'});
85
+
86
+ const cancellation_id = job.pre_funding_cancellations.at(-1).id;
87
+ job = await workflow.act(id, 'reopen', {cancellation_id}, {
88
+ actor: payerId, idempotencyKey: 'reopen-payer-1'
89
+ });
90
+ // If worker declined, the worker must also explicitly approve reopening.
91
+ job = await workflow.act(id, 'reopen', {cancellation_id}, {
92
+ actor: workerId, idempotencyKey: 'reopen-worker-1'
93
+ });
94
+ ```
95
+
96
+ Required reopening approvers are the requester and the cancelling party. When both had already accepted the agreement, both must approve regardless of who cancelled. Until all required approvals are present, the job remains `cancelled`. An observer/verifier cannot reopen it. A requester cancellation before bilateral acceptance can therefore be reopened by the requester alone; subsequent participation still requires fresh agreement. Application authorization policies apply to every approval.
97
+
98
+ When reopened, the same job returns to `draft`, the active agreement is cleared, `epoch` increments, and history/idempotency records remain. No old acceptance is restored. The next quote uses a higher agreement revision even when the textual terms are unchanged; its canonical terms include the reserved `_a423_reopen: {job_id, epoch}` field, changing the agreement hash and wallet-signature payload. Hash and sign the returned agreement. Validators must tolerate this SDK-owned top-level field.
99
+
100
+ After reopening, `quote`, `accept`, `fund`, `cancel` and `decline` require `input.expected_epoch === job.epoch`. This rejects delayed requests from prior negotiations. Existing idempotency keys remain bound to their original requests: an old completed request may return the current record but will not execute again. Use fresh keys for a new negotiation generation. Reopening requires the exact current `cancellation_id`; an old cancellation cannot reopen a later one.
101
+
102
+ The generic HTTP handler and typed client's `act` method support both actions. Chat tools expose `decline_job` and `reopen_job`; the reference dashboard exposes the same actions and history. Existing cancelled records without a pre-funding cancellation record remain terminal; no historical approval is fabricated.
103
+
104
+ Any funding attempt (`fund.requested`), pending operation, funding evidence or escrow ledger blocks this reopening path, even if a funding attempt was rejected. This is deliberately conservative because a signed/broadcast transaction may exist outside a cached state. Use reconciliation and escrow exits instead. Paid, refunded and settled jobs remain terminal; repeating that work needs a new job ID linked by application metadata.
105
+
106
+ This is off-chain workflow cancellation, not on-chain revocation of a previously issued signature. Already shared worker-consent signatures retain their original contract-defined validity; an external payer could still try to fund the old agreement. Reopening changes the new agreement commitment and prevents using an old signature for the new agreement, but cannot erase an old signature. Any external old-agreement funding must be reconciled and handled under its original on-chain terms; do not display it as new-generation custody. Hard revocation before external funding would require an additional on-chain nonce/revocation protocol and deployment, which this release does not add.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased documentation
4
+
5
+ - Rewrite the README around installation/version checks, initialization argument definitions, typed job fields, end-to-end action tables and Mermaid process maps.
6
+ - Add a runnable no-chain quickstart and an examples index; include both in the npm package.
7
+ - Explain published versus source versions, private economics, cancellation/reopening boundaries and actual receipt confirmation.
8
+
9
+ ## 0.8.1
10
+
11
+ - Accept direct `settlementPolicy.thresholds` initialization arguments alongside the existing policy-builder form (mutually exclusive).
12
+ - Validate threshold names, types and ordering at startup; capture the configuration so later caller mutations cannot change it.
13
+ - Expose the read-only effective configuration and document per-instance setup.
14
+
15
+ ## 0.8.0
16
+
17
+ - Add optional `/policy` primitives for integer gross-margin evaluation, configurable 50% target/10% minimum/50% maximum defaults, capped recommendations and private profit projections.
18
+ - Wire durable cost/budget freezing, owner-approved exceptions, worker/requester proposal defaults and acceptance guards into the application factory.
19
+ - Add role-aware policy inspection/prompts, state/receipt projections and reference dashboard economics.
20
+ - Add audit-logged malformed-pending rejection with adapter proof; the reference adapter supports only impossible submit encodings with no journal/external/on-chain submission.
21
+ - Retain existing contract, signatures and public settlement formats. Remote app metering/UI adoption remains separate.
22
+
23
+ ## 0.7.0
24
+
25
+ - Add worker `decline` before funding, with retained cancellation IDs, reasons, prior agreements and actor history.
26
+ - Add explicit `reopen` approvals: requester plus cancelling party, and both parties after bilateral acceptance.
27
+ - Preserve the job ID, clear the active agreement, monotonically advance quote revisions, and bind new agreements to a reopening generation.
28
+ - Reject stale-generation quote/accept/fund/cancel/decline requests and any pre-funding cancellation after funding has been attempted.
29
+ - Add chat actions, dashboard controls and regression coverage. No contract changes or external signature-revocation claim.
30
+
31
+ ## 0.6.0
32
+
33
+ - Add a persisted cancellation child flow with proposal, acceptance, escrow execution and confirmed receipt stages.
34
+ - Verify both wallet signatures before saving the immutable JSON instruction; encode it into the existing `settleByConsent` call.
35
+ - Add typed client/HTTP/chat actions, accepted-instruction and fresh final-receipt routes, keeper forwarding and dashboard controls.
36
+ - Preserve locked balances through rejected/expired execution; retain arbitration and original deadline exits.
37
+ - Reuse the 0.5 contract without Solidity changes. Earlier contracts still require the 0.5 deployment upgrade.
38
+
3
39
  ## 0.5.0
4
40
 
5
41
  - Add reference-contract closing proposals, counterproposals, exact counterpart acceptance and dual-signature permissionless settlement.
package/ESCROW.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Reference on-chain escrow (0.5.0)
2
2
 
3
+ [Start here: installation, arguments and lifecycle maps](./README.md) · [All examples](./examples/README.md)
4
+
3
5
  For negotiated cancellation and partial compensation, see [SETTLEMENT.md](./SETTLEMENT.md). The 0.5 contract adds proposal/counter/accept operations and a Negotiating state; a new deployment is required.
4
6
 
5
7
  This opt-in module implements a real ERC-20 escrow path. The generic workflow remains provider-independent. It includes Solidity source, reproducibly compiled ABI/bytecode, an ethers v6 adapter and local-EVM tests. It is experimental and has not received an independent security audit; deploy to a local chain/testnet for evaluation. No contract is deployed automatically and no funds move merely by installing the package.
package/INTEGRATION.md CHANGED
@@ -1,3 +1,17 @@
1
+ # Profit-driven settlement in 0.8
2
+
3
+ [Start here: installation, arguments and lifecycle maps](./README.md) · [All examples](./examples/README.md)
4
+
5
+ Configure the application factory with `settlementPolicy: {costBasisFor, budgetFor, policy}` to opt in. Read [PROFIT-POLICY.md](./PROFIT-POLICY.md) for cost freezing, privacy, prompts, owner exceptions, malformed-pending recovery and remote app migration. Existing 0.5 contracts and 0.6 cancellation receipts remain compatible.
6
+
7
+ # Pre-funding decline and reopening in 0.7
8
+
9
+ New actions: `decline` (worker) and `reopen` (required participants). Preserve the job ID, inspect `pre_funding_cancellations`, and supply `cancellation_id` when reopening. After reopening, supply `expected_epoch` for quote/accept/fund/cancel/decline. A fresh quote includes a reserved `_a423_reopen` commitment in its terms; sign/hash the returned agreement, not the original input. See [CANCELLATION.md](./CANCELLATION.md#pre-funding-decline-and-reopening--07).
10
+
11
+ # Cancellation subflows in 0.6
12
+
13
+ Use `workflow.acceptCancellation` or `POST /api/a423/jobs/:id/cancellation/accept` with both wallet signatures. Acceptance is persisted before forwarding; show the subflow status and issue the final receipt only after confirmation. See [CANCELLATION.md](./CANCELLATION.md) for routes, retry behavior and migration. The actual remote application is not modified by installing this package.
14
+
1
15
  # Closing settlements in 0.5
2
16
 
3
17
  Read SETTLEMENT.md. Use a new local/testnet reference deployment, preserve older funded contracts with their original adapters, wire proposal/counter/accept actions and the settlement-consent endpoint, and display work status/escrow ledger/negotiation separately. Test partial compensation, stale and expired approvals, arbitration, original deadlines and crash recovery. No real-chain deployment is implicit.
@@ -13,7 +27,7 @@ The remote MVP was not available in the SDK workspace; these utilities do not im
13
27
  Install after the maintainer publishes:
14
28
 
15
29
  ```sh
16
- npm install @allorc/a423-sdk@0.5.0
30
+ npm install @allorc/a423-sdk@0.8.1
17
31
  ```
18
32
 
19
33
  For testing before publication, install the maintainer's `npm pack` tarball instead. Do not silently install 0.1 and assume the workflow entry point exists.
package/OPERATIONS.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # A423 operational integration
2
2
 
3
+ [Start here: installation, arguments and lifecycle maps](./README.md) · [All examples](./examples/README.md)
4
+
5
+ For the 0.6 accepted cancellation JSON → escrow execution → confirmed receipt subflow, see [CANCELLATION.md](./CANCELLATION.md). The 0.5 contract is unchanged; an accepted instruction is not proof of payment.
6
+
3
7
  Version 0.5 adds closing negotiation, a paused-work state, a separate escrow ledger and proposal history. See SETTLEMENT.md before deploying the new contract. Execution callbacks receive an AbortSignal and must honor it for cooperative cancellation.
4
8
 
5
9
  This guide covers concrete SDK modules and their deployment boundaries. The remote MVP's source was unavailable during this SDK change, so its chat tools, dashboard, environment, reset route and testnet deployment have not been modified. Use the supplied bridge in that repository, then run the actual app's integration tests. Existing contract-level constraints remain explicit.
@@ -0,0 +1,154 @@
1
+ # Profit-driven settlement policy — 0.8.1
2
+
3
+ [Start here: installation, arguments and lifecycle maps](./README.md) · [All examples](./examples/README.md)
4
+
5
+ Implements the SDK portion of the supplied **A423 Profit-Driven Settlement Policy — Recommendation**. It is an opt-in off-chain negotiation policy. Existing users keep their current settlement behavior until configured. The 0.5 escrow contract, `a423.closing.v1`, signatures and cancellation receipt formats are unchanged.
6
+
7
+ ## Configure the backend
8
+
9
+ ```ts
10
+ import {createEscrowApplication} from '@allorc/a423-sdk/operations';
11
+
12
+ const app = createEscrowApplication({
13
+ // Existing storage, authority, escrows, signerFor, addressFor,
14
+ // limitsFor, approveSigning and validateArtifact callbacks...
15
+ ...existingConfiguration,
16
+ settlementPolicy: {
17
+ costBasisFor: async job => workMeter.costBasisRaw(job.id),
18
+ budgetFor: async job => (job.metadata as {budget_raw: string}).budget_raw,
19
+ thresholds: {
20
+ target_margin_bps: 3000, // 30% target
21
+ min_margin_bps: 500, // 5% minimum
22
+ max_margin_bps: 4000, // 40% maximum
23
+ require_counter: true,
24
+ },
25
+ // Optional: consult a durable, authenticated owner-approval system.
26
+ // Never return approval solely because an agent supplied a flag.
27
+ approveException: async context => ownerApprovals.check(context),
28
+ // Optional: only your explicitly authorized operator identities.
29
+ canViewPrivate: async ({actor, job}) => operators.canInspect(actor, job.id),
30
+ },
31
+ });
32
+ ```
33
+
34
+ The cost provider returns a canonical nonnegative raw-token-unit integer string including agreed costs/expenses. The app owns token prices, currency conversion, decimals, invoices and mock meters. Budget is also a canonical raw-unit string; it is never inferred from USD or free-form text. Inputs are bounded by uint256. No mock usage or private key is generated by the policy.
35
+
36
+ ### Initialization arguments (0.8.1)
37
+
38
+ Set `settlementPolicy.thresholds` before calling `createEscrowApplication`. Each instance has its own captured, immutable effective configuration, readable through `app.profit.configuration`; mutating the original options afterward does not change it. All configuration is checked before any cost lookup or chain transaction. Unknown/misspelled threshold names, invalid types and inconsistent bounds fail at startup.
39
+
40
+ | Argument | Default | Meaning |
41
+ |---|---:|---|
42
+ | `target_margin_bps` | 5000 | Desired gross margin; 3000 means 30% |
43
+ | `min_margin_bps` | 1000 | Minimum without owner exception |
44
+ | `max_margin_bps` | 5000 | Maximum permitted margin threshold |
45
+ | `require_counter` | true | Require a worker counter before accepting the requester's first offer |
46
+
47
+ Require `0 <= minimum <= target <= maximum < 10000`; 100 basis points equals 1%. Supply either `thresholds` or the existing `policy: createSettlementPolicy(...)` form, not both. Omitting both preserves the defaults. Cost and budget providers, explicit owner-approval checks, wallet signing/spending limits, network and confirmation depth remain separate initialization callbacks/settings; they are not client-controlled trade arguments.
48
+
49
+ Changing defaults does not rewrite already frozen negotiation snapshots. For new jobs, create an SDK instance with the desired configuration. Do not silently change policy thresholds during an accepted negotiation.
50
+
51
+ The defaults are 50% target gross margin, 10% minimum and 50% maximum. They are configurable SDK primitives; the app chooses its business policy. Profit is payout minus cost, and gross margin is profit divided by payout. A 50% target means twice the cost, not 1.5 times the cost.
52
+
53
+ The service freezes cost, budget, escrow total and configuration in private durable KV at the **first closing proposal attempt**, including requester proposals. Freezing before the transaction prevents retries/counters from sampling growing costs; a rejected proposal does not unfreeze it. Live worker previews before that attempt do not freeze the meter. Concurrent first attempts reuse the atomic winning snapshot. A new agreement/generation receives a new snapshot. Do not place internal costs in public metadata, claims, mail or proposal terms.
54
+
55
+ ## Pure calculation
56
+
57
+ ```ts
58
+ import {createSettlementPolicy} from '@allorc/a423-sdk/policy';
59
+ const policy = createSettlementPolicy();
60
+ const plan = policy.evaluate({
61
+ cost_basis_raw: '600505',
62
+ budget_raw: '800000',
63
+ escrow_raw: '5000000',
64
+ offer_raw: '800000',
65
+ negotiation_round: 1,
66
+ });
67
+ // target_payout: 1201010; min_payout: 667228
68
+ // recommended_payout: 800000; expected_profit: 199495
69
+ // expected_margin_bps: 2493; viability: below_target
70
+ ```
71
+
72
+ Amounts are raw integers. Payout thresholds use ceiling division, margins use signed floor basis points, and no floating-point token arithmetic is used. `2493` means at least 24.93%, a conservatively truncated display. `null` margin means zero payout; it is not a zero-percent profit claim. For configurable non-50% thresholds, upward rounding can exceed the requested margin by the unavoidable smallest token unit.
73
+
74
+ `target_payout`, `min_payout` and `max_payout` are theoretical thresholds; they may exceed budget/escrow. **Only `recommended_payout` is a capped proposed amount**, equal to the lesser of target, budget and escrow. A theoretical threshold larger than uint256 is still returned exactly as a string, but the recommended allocation cannot exceed its uint256 input caps.
75
+
76
+ | Condition | Behavior |
77
+ |---|---|
78
+ | Zero cost | Recommend worker 0, full payer refund |
79
+ | Affordable amount below cost | Owner approval or decline; never silently accept a loss |
80
+ | Affordable amount below minimum payout | Owner approval required |
81
+ | Budget below target but above minimum | Recommend the highest affordable payout |
82
+ | Target affordable | Recommend target; do not exploit excess budget |
83
+ | Offer above budget, escrow or maximum payout | Reject through the SDK; counter within limits |
84
+ | First requester offer | Counter before accepting when `require_counter` is enabled |
85
+
86
+ `decision` is advisory; it does not sign, broadcast, promise acceptance or automatically cancel a job. `requires_owner_approval` marks economic exceptions. Profit projection is based on the app's cost estimate, not an on-chain audit of real expenditure.
87
+
88
+ ## Proposal and acceptance integration
89
+
90
+ When enabled through the factory, the same workflow preparation and authorization apply to generic HTTP actions, chat tools and direct workflow calls:
91
+
92
+ - An omitted worker `worker_amount` in `propose_settlement` / `counter_settlement` defaults to the capped recommendation. Below-minimum defaults are still blocked until approved.
93
+ - An omitted requester amount defaults to 0. The requester may propose a full refund, but cannot force the worker to accept it.
94
+ - Explicit worker proposals must satisfy minimum/cost and maximum/budget caps, or have a named owner approval for the lower bound.
95
+ - `accept_settlement`, `approve_cancellation` and `execute_cancellation` apply the policy regardless of which actor relays both signatures.
96
+ - A confirmed worker response after the first requester offer satisfies the counter requirement. The requester can accept the worker's own ask directly; the worker cannot accept a first requester offer without countering. An initial worker ask followed by the first requester counter is still a first requester offer. After negotiation, an affordable offer at or above minimum may be accepted even below target.
97
+ - Owner exceptions require a trusted server callback returning `{approved:true, approval_id:'durable-owner-decision-id'}`. The callback receives the exact action, amount, current proposal hash and evaluation. An agent's `owner_approved` flag has no effect. The approval ID is privately recorded. Recheck the callback on subsequent actions; approve only the intended amount/revision/action.
98
+ - Owner approval **cannot** override the budget, escrow or maximum payout. Arbitrator/timeout/refund exits remain available and are not obstructed by profit targets.
99
+
100
+ Keep revision/hash, signature and role validation. A policy decision cannot replace counterparty consent. The SDK does not autonomously fabricate work claims, renegotiate repeatedly or guarantee profit. Direct external contract calls are not governed by this off-chain business policy. Explicit external transaction evidence may be adopted without the economic guard, because the adapter only validates an already executed transaction and cannot broadcast through that path. Observe-only reconciliation also bypasses profit approval checks, so a withdrawn owner exception cannot hide an already executed result; fresh spending/resume still requires authorization. Actual arbitration or timeout payouts can realize a loss; projections report the actual result.
101
+
102
+ For custom workflow construction, wire `service.prepare` as `prepareInput` and compose `service.authorize` with your existing authorization. For custom signing/approval flows outside this workflow, call the same service explicitly. Merely importing `createSettlementPolicy` does not enforce it.
103
+
104
+ ## Agent tools and prompts
105
+
106
+ The factory's chat tools add `list_jobs`, `reconcile_job` and `inspect_settlement_policy({job_id})`. Inspection returns a role-aware profit projection and a prompt fragment. Add that fragment to your agent's system/tool instructions. The factory does not rewrite a remote LangGraph prompt automatically.
107
+
108
+ ```ts
109
+ const prompt = app.profit.prompt({role:'worker'});
110
+ // Or: settlementPolicyPrompt({role:'worker', policy, override: customPrompt})
111
+ ```
112
+
113
+ The worker fragment prioritizes profit, caps, explicit loss approval, counters, confidentiality, correct job IDs and confirmed receipts. The requester fragment prioritizes completed work, budget and receipts without exposing worker economics. Quote pricing and real execution/usage metering remain application responsibilities; use the pure evaluator with estimated costs before quoting. Closing policy does not silently rewrite an already funded agreement price.
114
+
115
+ ## Status, receipts and privacy
116
+
117
+ The factory supplies `profitFor` to the application handler. It adds `profit` to `/api/state` jobs and fresh JSON/PDF receipt projections, and exposes `GET /api/a423-profit/:id`. All require participant credentials. `canViewPrivate` does not grant access to an otherwise inaccessible job; an operator must already have participant access. The pure protocol job records, public event stream and on-chain receipts contain no new profit/cost fields.
118
+
119
+ - Worker and explicitly permitted operator: cost basis, target/minimum/maximum, affordability, current allocation, projected profit/margin, target status and whether the basis is frozen.
120
+ - Requester/other participants: proposed amount, confirmed actual amount and evidence status only. Internal cost, budget snapshot, profit and margins are omitted. Public reads never invoke the cost provider.
121
+ - `realized_*` fields remain null until a settlement event freshly revalidates on the configured chain. Reorged, missing or unavailable evidence does not produce realized profit. Acceptance and a cached terminal state are insufficient.
122
+ - The reference dashboard displays these role-specific fields using raw units and basis points. The application should format token decimals and distinguish simulated versus invoiced costs.
123
+
124
+ `profitProjection({evaluation,payout_raw,confirmed})` is a pure helper: its caller must prove confirmation before setting `confirmed:true`. Prefer the factory/service projector with a real evidence revalidator.
125
+
126
+ ## Supported malformed-pending recovery
127
+
128
+ Use the server-side SDK API instead of mutating workflow rows with raw CAS:
129
+
130
+ ```ts
131
+ const job = await app.workflow.get(jobId, workerId);
132
+ await app.workflow.rejectPendingOperation(jobId, {
133
+ operation_id: job.pending.id,
134
+ reason: 'Runner emitted a missing or malformed result_hash; will retry with a real artifact',
135
+ }, {actor: workerId});
136
+ ```
137
+
138
+ The engine requires the current operation ID, the original operation's role/authorization, input-integrity verification and an adapter's `never_executable` proof. It atomically records a rejected operation and recovery audit event without advancing the job or moving funds. A conflicting write fails CAS.
139
+
140
+ The reference adapter supports **only a syntactically invalid `submit.result_hash`** with no external transaction reference, no journaled operation or replacement, and no on-chain submission. Such input cannot encode a valid submission transaction. Valid inputs, unknown transaction intents, RPC outages, journal entries and other actions cannot be discarded. Reconcile/resume those instead. A custom adapter's recovery hook is trusted security code and must prove the same impossibility, including concurrent execution safety; it is not an unconditional rejection callback. No recovery HTTP/chat endpoint is exposed by default.
141
+
142
+ ## Application migration checklist
143
+
144
+ The remote app described in the Markdown is separate from this SDK repository. To adopt this release there:
145
+
146
+ 1. Install 0.8.1 after publication; configure the factory's `settlementPolicy` callbacks against `lib/a423/work-meter.ts` and the original budget.
147
+ 2. Expose `cost_basis_raw` from the simulated meter, clearly label it simulated, and let the SDK freeze the closing snapshot.
148
+ 3. Remove the cost-recovery-only default from `lib/a423/chat04.ts`; omit worker amount to use the policy default, or use the evaluator explicitly. Keep work/progress claims public and costs private.
149
+ 4. Inject the role-specific prompt; use inspection for private worker reasoning and list/reconcile tools for recovery.
150
+ 5. Display `profit` projection fields in the dashboard and summaries. Requester views must not copy worker-private fields into mail, shared caches or logs.
151
+ 6. Replace the malformed-submit raw CAS repair with `rejectPendingOperation` and preserve existing transaction journals.
152
+ 7. Run the funded cancellation journey with an actual cost provider; verify target, budget-limited, loss-approval, zero-work, privacy and receipt cases.
153
+
154
+ No independent audit, external deployment, publication or remote application migration is performed by installing the package.