@ixo/decision-oracle 0.2.28 → 0.2.30

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.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/dist/db/schema.d.ts +17 -0
  3. package/dist/db/schema.d.ts.map +1 -1
  4. package/dist/db/schema.js +10 -0
  5. package/dist/db/schema.js.map +1 -1
  6. package/dist/lib/api-lane/accept.d.ts.map +1 -1
  7. package/dist/lib/api-lane/accept.js +27 -10
  8. package/dist/lib/api-lane/accept.js.map +1 -1
  9. package/dist/lib/api-lane/notify.d.ts +9 -0
  10. package/dist/lib/api-lane/notify.d.ts.map +1 -1
  11. package/dist/lib/api-lane/notify.js +10 -3
  12. package/dist/lib/api-lane/notify.js.map +1 -1
  13. package/dist/lib/api-lane/record.d.ts.map +1 -1
  14. package/dist/lib/api-lane/record.js +6 -3
  15. package/dist/lib/api-lane/record.js.map +1 -1
  16. package/dist/lib/engine/client.d.ts +14 -2
  17. package/dist/lib/engine/client.d.ts.map +1 -1
  18. package/dist/lib/engine/client.js +90 -11
  19. package/dist/lib/engine/client.js.map +1 -1
  20. package/dist/lib/ixo/grant-chain.js +6 -6
  21. package/dist/lib/ixo/grant-chain.js.map +1 -1
  22. package/dist/lib/openhands/why.d.ts.map +1 -1
  23. package/dist/lib/openhands/why.js +7 -2
  24. package/dist/lib/openhands/why.js.map +1 -1
  25. package/dist/lib/work-order/index.d.ts +4 -2
  26. package/dist/lib/work-order/index.d.ts.map +1 -1
  27. package/dist/lib/work-order/index.js +19 -4
  28. package/dist/lib/work-order/index.js.map +1 -1
  29. package/dist/middleware/api/keys.d.ts.map +1 -1
  30. package/dist/middleware/api/keys.js +10 -2
  31. package/dist/middleware/api/keys.js.map +1 -1
  32. package/dist/middleware/api/rate-limit.js +3 -4
  33. package/dist/middleware/api/rate-limit.js.map +1 -1
  34. package/dist/middleware/api/wait.d.ts +17 -2
  35. package/dist/middleware/api/wait.d.ts.map +1 -1
  36. package/dist/middleware/api/wait.js +80 -19
  37. package/dist/middleware/api/wait.js.map +1 -1
  38. package/dist/middleware/ucan-auth.d.ts.map +1 -1
  39. package/dist/middleware/ucan-auth.js +7 -2
  40. package/dist/middleware/ucan-auth.js.map +1 -1
  41. package/dist/routes/v1/api/claims.d.ts +15 -3
  42. package/dist/routes/v1/api/claims.d.ts.map +1 -1
  43. package/dist/routes/v1/api/claims.js +85 -14
  44. package/dist/routes/v1/api/claims.js.map +1 -1
  45. package/dist/workflows/claim-ingest.d.ts +19 -125
  46. package/dist/workflows/claim-ingest.d.ts.map +1 -1
  47. package/dist/workflows/claim-ingest.js +49 -765
  48. package/dist/workflows/claim-ingest.js.map +1 -1
  49. package/dist/workflows/evaluate.d.ts +193 -0
  50. package/dist/workflows/evaluate.d.ts.map +1 -0
  51. package/dist/workflows/evaluate.js +980 -0
  52. package/dist/workflows/evaluate.js.map +1 -0
  53. package/docs/api.md +24 -4
  54. package/docs/guide.md +19 -1
  55. package/migrations/0029_inline_lease.sql +1 -0
  56. package/migrations/meta/0029_snapshot.json +3021 -0
  57. package/migrations/meta/_journal.json +7 -0
  58. package/package.json +4 -4
@@ -4,10 +4,16 @@
4
4
  * each step checkpointed and auto-retried, so a crash/redeploy/transient failure never re-does
5
5
  * work or drops a claim.
6
6
  *
7
- * THE WORKFLOW OWNS ITS LEDGER ROW — one writer, whole lifecycle:
7
+ * THE WORKFLOW OWNS ITS LEDGER ROW for its whole lifecycle — the one other writer is the API
8
+ * lane's inline run (`evaluateInline`, workflows/evaluate.ts), the same `evaluate` body run by the
9
+ * accepting request for a caller that waits; it takes the row only from `queued` (writing its lease,
10
+ * `inlineUntil`, in the same statement), and the steps below find what it wrote:
8
11
  * step "record-queued" → upsert the `claim_ingests` row as `queued` (durable: the row exists
9
- * iff the instance started, so nothing can ever strand a row)
10
- * step "evaluate" → mark the row `processing` (so a later attempt knows one ran), then
12
+ * iff the instance started, so nothing can ever strand a row) — never
13
+ * over a row already `processing` / `processed`, which an inline run
14
+ * (or an earlier run of this instance) wrote and a rerun must not undo
15
+ * step "evaluate" → (workflows/evaluate.ts) a recorded decision is returned as is; else
16
+ * mark the row `processing` (so a later attempt knows one ran), then
11
17
  * ONE pull (chain metadata + body + attachment descriptors), the facts
12
18
  * lane over the same in-memory claim (`buildFacts`: read the engine's
13
19
  * rubric manifest, run the producer, freeze the FactSet), and `POST /v1/decisions` on
@@ -28,7 +34,11 @@
28
34
  * ingest — the decision is recorded, and an operator rerun re-enters
29
35
  * delivery idempotently. A `pending_review` decision waits for the
30
36
  * review lane (routes/v1/reviews.ts → ReviewComplete) to act instead
31
- * step "mark-processed" → row → `processed`
37
+ * step "mark-processed" → row → `processed` — right after the decision (and the act), so the
38
+ * ledger reads `processed` beside it without waiting on a receiver;
39
+ * AFTER the notify for a `pending_review`, whose record reads its
40
+ * tasks off the `review_required` event the notify writes
41
+ * step "notify-review" → the source's notify lane (fail-soft, its own delivery ledger)
32
42
  * terminal catch → row → `failed` (+ the message), instance shows Errored in the dashboard
33
43
  * The cron (cron/) only discovers claims and creates instances; it never writes this table.
34
44
  *
@@ -85,6 +95,7 @@
85
95
  import { WorkflowEntrypoint, type WorkflowEvent, type WorkflowStep } from "cloudflare:workers";
86
96
  import type { ClaimLaneKind } from "../contracts/index.js";
87
97
  import type { OracleEnv } from "../env.js";
98
+ export { type EvaluateResult, GRANT_RETRY_DELAY, PAYER_BILLING } from "./evaluate.js";
88
99
  export interface ClaimIngestParams {
89
100
  claimId: string;
90
101
  /** Known at cron discovery time; absent on manual runs (resolved on-chain in `record-queued`). */
@@ -104,28 +115,6 @@ export interface ClaimIngestParams {
104
115
  /** The accepting request's id, API lane only. */
105
116
  requestId?: string;
106
117
  }
107
- /** What leaves the evaluate step: small, log-safe, enough to reason about the run — never the facts. */
108
- export interface EvaluateResult {
109
- status: string;
110
- verdict: string | null;
111
- resultDigest: string | null;
112
- receiptCid: string | null;
113
- }
114
- /**
115
- * How a review-billing trouble is told apart in the ledger's `error` (a run that gave up while its
116
- * collection was paused for payment): the resume cron restarts exactly those.
117
- */
118
- export declare const PAYER_BILLING = "payer-billing:";
119
- /**
120
- * `403 NO_EVALUATE_GRANT` is retry-ONCE (docs/ENGINE-CONTRACT.md): the grant may have landed on
121
- * chain seconds ago — the portal grants and registers in one flow — so wait about a block and ask
122
- * again before calling it a real "not hired". In-step on purpose: the FactSet may not leave the
123
- * step, so a step-level sleep would cost a full rebuild (connectors, AI charges). An object so a
124
- * test can shorten it.
125
- */
126
- export declare const GRANT_RETRY_DELAY: {
127
- ms: number;
128
- };
129
118
  export declare class ClaimIngest extends WorkflowEntrypoint<OracleEnv, ClaimIngestParams> {
130
119
  run(event: WorkflowEvent<ClaimIngestParams>, step: WorkflowStep): Promise<{
131
120
  status: string;
@@ -136,104 +125,6 @@ export declare class ClaimIngest extends WorkflowEntrypoint<OracleEnv, ClaimInge
136
125
  ok: boolean;
137
126
  }>;
138
127
  private ingest;
139
- /**
140
- * The evaluate step's body: pull → facts → decide → the `decisions` row. Reads the DB, not step
141
- * results, so an operator rerun converges — and a retry that finds the row is done.
142
- */
143
- private evaluate;
144
- /**
145
- * Who pays for this chain claim's review (header): null when its collection pays by its
146
- * evaluation leg (or the oracle no longer bills per review); `reserved` when a charge already
147
- * holds it (a retry); else the payer, resolved fresh — never a guess: the engine's or the chain's
148
- * trouble retries the step, and no usable delegation pauses the collection `payer-delegation`.
149
- */
150
- private billingFor;
151
- /**
152
- * A billing pause. The payer's two: the owner's notice, never a page (a customer's condition).
153
- * `oracle-plan`: IXO's to fix — paged, no notice to the owner or the payer.
154
- */
155
- private pausePayer;
156
- /** The normal path: pull → build → (reserve) → decide → the `decisions` row. */
157
- private decideFresh;
158
- /** Reserve the review (payments/review-billing.ts), or say why not — a pause, a retry, a stop. */
159
- private reserve;
160
- /**
161
- * A decision recovered from the engine's store → its `decisions` row.
162
- *
163
- * It is FINAL for this claim — the engine will not judge it twice — so it outranks anything this
164
- * attempt would have concluded, including a build that failed. What it lacks is the facts it was
165
- * decided on: the attempt that produced them died before writing the row, and the engine keeps a
166
- * trace, not a FactSet. So they are rebuilt here — and stand in for the originals only if the
167
- * rebuild is PROVABLY the same one, which is exactly what the engine's `factsDigest` proves.
168
- * Anything else (a non-deterministic fact, a re-anchored rubric, a failed rebuild) is written as
169
- * a park with its reason, at write time: better an explicit blocked row naming the drift than a
170
- * decided row the act lane refuses later without knowing why.
171
- *
172
- * The rebuild may not even be ATTEMPTABLE: the owner's delegation can expire while the claim
173
- * sits between attempts, and then the pull fail-closes 403. A deterministic "no" there is NOT a
174
- * reason to lose the decision — the ingest would be marked `failed` with no row at all, while
175
- * the engine holds a final answer it will never issue again. So it is recorded parked, facts
176
- * null, and the act lane refuses to sign it (src/act.ts reads those facts). Only a transient
177
- * failure is rethrown, so the step still retries and a later attempt can record the real facts.
178
- */
179
- private recordRecovered;
180
- /**
181
- * The engine's answer → the `decisions` row, the collection's rubric pin and the `unusable`
182
- * pause: ONE path whether the answer came from `POST /v1/decisions` just now or was recovered
183
- * from the engine's store on a retry (see `evaluate`), so the two can never drift apart.
184
- *
185
- * `sent` is the FactSet this decision was made on — its `ext`/`ai` are frozen onto the row for
186
- * the act policy to read at signing time (schema.ts `decisions.facts`), and nothing else of it
187
- * is: `answers`/`attachments` are the claim's erasable surface and never persisted here. Null
188
- * only on the recovery path when the rebuild could not be proven to be the same facts, which is
189
- * what `park` says: the row is written `blocked` with that reason instead of `pending`, so the
190
- * claim read explains itself and the act lane never has to guess.
191
- */
192
- private record;
193
- /**
194
- * `api.decision.latency` — accept → decision row, the SLO metric. Accepted is the API claim's
195
- * `receivedAt`, or the chain claim's ledger `createdAt` (its first `record-queued`). `status` says
196
- * whether the protocol's rubric runs AI checks: the frozen FactSet's `ai` keys are exactly the
197
- * work order's (validate.ts holds them to the manifest), so no extra read; `-` only when a
198
- * recovered decision's facts could not be rebuilt. Best-effort: a metric never fails the step.
199
- */
200
- private decisionLatency;
201
- /**
202
- * `POST /v1/decisions`, with the one recoverable refusal handled here: `RUBRIC_PIN_MISMATCH` —
203
- * our pin ≠ the chain's `#rub` proof as the engine read it, i.e. `#rub` was re-anchored between
204
- * our (KV-cached) manifest read and theirs. Invalidate and re-read the manifest ONCE: a changed
205
- * pin is a new work order (new facts, so a rebuild — connectors run again; the fact cache is
206
- * keyed by the pin, fact-cache.ts), the same pin means the engine and the chain disagree, which
207
- * no retry of ours fixes. The manifest may also have turned `unusable` under the new anchor —
208
- * then the rebuild records that, and never decides.
209
- */
210
- private decide;
211
- /** One decide call, with the contract's retry-once on `NO_EVALUATE_GRANT` (see `GRANT_RETRY_DELAY`). */
212
- private ask;
213
- /**
214
- * The engine said no (docs/ENGINE-CONTRACT.md "Errors"): do what the table says, then hand back
215
- * the error to throw — NonRetryable for a deterministic refusal, the original for a retryable
216
- * one. Non-engine errors pass through for `stepError` to classify.
217
- */
218
- private engineFailure;
219
- /**
220
- * Facts that don't match the rubric's work order (lib/facts/validate.ts) — the producer's bug,
221
- * never a claim outcome: the issue list is codes and paths (identifiers), logged and paged so the
222
- * fix is one grep away, and the error to throw is NonRetryable (a retry would re-run the same
223
- * producer). The row is already recorded by the caller.
224
- */
225
- private invalidFacts;
226
- /**
227
- * A claim the facts lane could not build for — recorded WITHOUT a decide call (schema.ts
228
- * `decisions`): `status` names the reason, `explanation` the issues, and nothing will ever be
229
- * acted on, so `chainStatus` is `skipped` from the start. Then the reason's consequence:
230
- * `rubric-unusable` is the engine's manifest saying the rubric cannot be bound — the same
231
- * handling as its decide-time `unusable` (pause the collection; steps.ts), minus the receipt, and
232
- * the ingest completes; `invalid-facts` is OUR (or the provider's) bug, never a claim outcome —
233
- * recorded so the claim read says why, then failed loudly, like the engine's own `VALIDATION`
234
- * refusal this check exists to catch first.
235
- */
236
- private buildFailure;
237
128
  /**
238
129
  * A terminal failure's class — the API lane's §5 code (lib/api-lane/record.ts `claimErrorCode`,
239
130
  * read off the same message the ledger stores), for either lane. Best-effort like the rest of
@@ -243,7 +134,10 @@ export declare class ClaimIngest extends WorkflowEntrypoint<OracleEnv, ClaimInge
243
134
  private ledgerRow;
244
135
  /**
245
136
  * Upsert this claim's ledger row. `error` is null unless `failed` (schema.ts), so a rerun that
246
- * succeeds clears the old message.
137
+ * succeeds clears the old message. `queued` never lands on a row already `processing` or
138
+ * `processed`: the inline run (or an earlier run of this instance) got that far, and moving it
139
+ * back would hide an inline run in flight from `evaluate` (two runs, the handlers paid twice) and
140
+ * show a decided claim as `queued` until the run caught up.
247
141
  */
248
142
  private recordStatus;
249
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"claim-ingest.d.ts","sourceRoot":"","sources":["../../src/workflows/claim-ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AAEH,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI/F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAUlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAoDxC,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wGAAwG;AACxG,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAOD;;;GAGG;AACH,eAAO,MAAM,aAAa,mBAAmB,CAAC;AAsB9C;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;IAAK,EAAE;CAAU,CAAC;AAchD,qBAAa,WAAY,SAAQ,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,CAAC;IAChE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,YAAY;gBA3DtE,MAAM;iBACL,MAAM,GAAG,IAAI;sBACR,MAAM,GAAG,IAAI;oBACf,MAAM,GAAG,IAAI;;;OA8ExB;YAEa,MAAM;IA+KpB;;;OAGG;YACW,QAAQ;IAkHtB;;;;;OAKG;YACW,UAAU;IA6CxB;;;OAGG;YACW,UAAU;IAyBxB,gFAAgF;YAClE,WAAW;IAyHzB,kGAAkG;YACpF,OAAO;IAgBrB;;;;;;;;;;;;;;;;;;OAkBG;YACW,eAAe;IA2D7B;;;;;;;;;;;OAWG;YACW,MAAM;IA0HpB;;;;;;OAMG;YACW,eAAe;IAmC7B;;;;;;;;OAQG;YACW,MAAM;IAoCpB,wGAAwG;YAC1F,GAAG;IAmBjB;;;;OAIG;YACW,aAAa;IAmI3B;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAmBpB;;;;;;;;;OASG;YACW,YAAY;IAwC1B;;;;OAIG;YACW,YAAY;IAa1B,OAAO,CAAC,SAAS;IAMjB;;;OAGG;YACW,YAAY;CA4B3B"}
1
+ {"version":3,"file":"claim-ingest.d.ts","sourceRoot":"","sources":["../../src/workflows/claim-ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6FG;AAEH,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI/F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAaxC,OAAO,EAAE,KAAK,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,WAAY,SAAQ,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,CAAC;IAChE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,YAAY;;;;;;;OAsB7E;YAEa,MAAM;IA0LpB;;;;OAIG;YACW,YAAY;IAa1B,OAAO,CAAC,SAAS;IAMjB;;;;;;OAMG;YACW,YAAY;CA+B3B"}