Make the contract describe the wire it announces: the order options, the money slider, the precision signal, the second pause reason and the retired ceiling schema
This commit is contained in:
parent
358a473a29
commit
75ae6f06ca
1 changed files with 168 additions and 21 deletions
|
|
@ -17,9 +17,11 @@ info:
|
||||||
**Nothing about HOW a book is translated crosses this boundary** — no model names, no phase or
|
**Nothing about HOW a book is translated crosses this boundary** — no model names, no phase or
|
||||||
stage names, no internal vocabularies, and no cost of any model, stage or call. ⚠ **Money the
|
stage names, no internal vocabularies, and no cost of any model, stage or call. ⚠ **Money the
|
||||||
ACCOUNT owns is not in that set** (owner, 05.09): the balance, an order's ceiling and its hold are
|
ACCOUNT owns is not in that set** (owner, 05.09): the balance, an order's ceiling and its hold are
|
||||||
the user's own figures and WILL be shown as amounts — the fields land with the order-form minor, and
|
the user's own figures and WILL be shown as amounts — ⚠ **ИСПР. 06.09: условие ИЗРАСХОДОВАНО.** Минор формы
|
||||||
until they do, `Usage` and `CeilingBounds` below still carry the retired rule (⚠ read them together
|
заказа ЗАЛАНДЕН (`0.11.0`), суммы на проводе: `balance_micro_usd`, `limit.{min,max}_micro_usd`,
|
||||||
with this paragraph, not instead of it). The earlier blanket «no money sums» was the owner's own
|
`estimate.{expected,hold}_micro_usd`. `Usage` ретированное правило по-прежнему НЕСЁТ и это верно
|
||||||
|
(её провод не изменился); `CeilingBounds` — РЕТИРОВАНА целиком и больше не отдаётся ни одной
|
||||||
|
операцией. The earlier blanket «no money sums» was the owner's own
|
||||||
rule of 02.08 and he RETIRED it on 05.09; what stays banned is the structure of OUR spend. The projection is an allowlist: a field not
|
rule of 02.08 and he RETIRED it on 05.09; what stays banned is the structure of OUR spend. The projection is an allowlist: a field not
|
||||||
named here never reaches the client. The rule binds the PROSE too — every description here is
|
named here never reaches the client. The rule binds the PROSE too — every description here is
|
||||||
compiled into the generated client's source.
|
compiled into the generated client's source.
|
||||||
|
|
@ -1569,11 +1571,17 @@ components:
|
||||||
PausedReason:
|
PausedReason:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Machine reason a run is paused; the client draws the phrase. One value today. A client MUST
|
Machine reason a run is paused; the client draws the phrase. **TWO values since the
|
||||||
tolerate an unknown one under a minor bump and MUST render a paused run whose reason is
|
order-form minor (0.11.0), and the distinction is load-bearing because the remedies are
|
||||||
`null` — the ordinary answer when the service has no word for what stopped it — showing the
|
OPPOSITE:** `credit_exhausted` — the ACCOUNT has no money, the remedy is to top up;
|
||||||
neutral "halted, continuable" state.
|
`run_limit_reached` — the account is fine and the RUN delivered everything that was ordered,
|
||||||
enum: [credit_exhausted]
|
the remedy is to order more. Reading the second as the first sends a paying user to a
|
||||||
|
payment page they do not need.
|
||||||
|
|
||||||
|
A client MUST tolerate an unknown one under a minor bump and MUST render a paused run whose
|
||||||
|
reason is `null` — the ordinary answer when the service has no word for what stopped it —
|
||||||
|
showing the neutral "halted, continuable" state.
|
||||||
|
enum: [credit_exhausted, run_limit_reached]
|
||||||
|
|
||||||
AccountHaltReason:
|
AccountHaltReason:
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -1616,6 +1624,8 @@ components:
|
||||||
- chapter_count
|
- chapter_count
|
||||||
- chapters_done
|
- chapters_done
|
||||||
- character_count
|
- character_count
|
||||||
|
- character_count_exact
|
||||||
|
- structure
|
||||||
- added_at
|
- added_at
|
||||||
- note_count
|
- note_count
|
||||||
- shape_epoch
|
- shape_epoch
|
||||||
|
|
@ -1679,8 +1689,34 @@ components:
|
||||||
**The chosen cure is a PRECISION SIGNAL BESIDE the number, not a change to what `null`
|
**The chosen cure is a PRECISION SIGNAL BESIDE the number, not a change to what `null`
|
||||||
means** — `null` already says "still arriving", a live client renders it as `0`, and
|
means** — `null` already says "still arriving", a live client renders it as `0`, and
|
||||||
overloading it would break a reader that is deployed. The field lands with the
|
overloading it would break a reader that is deployed. The field lands with the
|
||||||
order-form pack, which is the one that makes this number a PRICE; until it does, this
|
order-form pack, which is the one that makes this number a PRICE. ⚠ **ИСПР. 06.09: ПРИЕХАЛО
|
||||||
description is the only place that says the number can be wrong.
|
ЛЕНДИНГОМ 0.11.0** — сигнал точности стоит рядом полем `character_count_exact`, и это
|
||||||
|
описание больше не единственное место, где сказано, что число может быть неверным.
|
||||||
|
character_count_exact:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether `character_count` is the source's own characters (`true`) or a proxy this side
|
||||||
|
could not decode — runes of an EPUB's ZIP container, or of a stream in an encoding the
|
||||||
|
engine re-reads (`false`). ⚠ **Added by the order-form minor (0.11.0)**, because the
|
||||||
|
same minor turns this number into a PRICE: selling by characters against an archive's
|
||||||
|
byte count would charge for the container.
|
||||||
|
|
||||||
|
Beside the number, never inside it: `null` on `character_count` keeps its one meaning,
|
||||||
|
"still arriving", and a deployed reader that renders `null` as `0` is not broken by this.
|
||||||
|
structure:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
- type: 'null'
|
||||||
|
description: |
|
||||||
|
Where this book's chapter boundaries came from, passed through from the engine's
|
||||||
|
manifest; `null` while the book is still arriving. ⚠ **Added by the order-form minor
|
||||||
|
(0.11.0)** so a client can tell an order in chapters from an order it must phrase in
|
||||||
|
characters.
|
||||||
|
|
||||||
|
⚠ **An OPEN vocabulary that grows additively** — today `declared`, `detected`, `none`.
|
||||||
|
A value this build does not know is read as "not recognised" and is never promoted into
|
||||||
|
trust. A client MUST do the same: switch on the values it knows and treat the rest as
|
||||||
|
"no usable structure", rather than compile an exhaustive list that a minor will outgrow.
|
||||||
added_at: { type: string, format: date-time }
|
added_at: { type: string, format: date-time }
|
||||||
note_count:
|
note_count:
|
||||||
type: integer
|
type: integer
|
||||||
|
|
@ -1769,12 +1805,32 @@ components:
|
||||||
cleared by a `resume` that SUCCEEDS: continuing IS the withdrawal of the request, and a
|
cleared by a `resume` that SUCCEEDS: continuing IS the withdrawal of the request, and a
|
||||||
run that was asked to continue is no longer a run someone asked to stop. A refused
|
run that was asked to continue is no longer a run someone asked to stop. A refused
|
||||||
`resume` clears nothing — nothing was withdrawn.
|
`resume` clears nothing — nothing was withdrawn.
|
||||||
ceiling_chapters:
|
ordered_chapters:
|
||||||
type: integer
|
type: integer
|
||||||
minimum: 1
|
minimum: 1
|
||||||
description: |
|
description: |
|
||||||
The limit this run was started with, in CHAPTERS — a property of the RUN. Present so a
|
What this run was ORDERED to deliver, in chapters — a property of the RUN. Present so a
|
||||||
reloaded screen can name the limit the user chose and read `progress.total` against it.
|
reloaded screen can name what the user bought and read `progress` against it.
|
||||||
|
|
||||||
|
⚠ **Renamed from `ceiling_chapters` by the order-form minor (0.11.0).** The old name said
|
||||||
|
"limit" when the figure is a PURCHASE: a ceiling is what a run may not exceed, an order is
|
||||||
|
what it owes. The retired name is rejected on the way IN with a pointer to `/chapters`,
|
||||||
|
never silently reinterpreted.
|
||||||
|
delivered_chapters:
|
||||||
|
oneOf:
|
||||||
|
- type: integer
|
||||||
|
minimum: 0
|
||||||
|
- type: 'null'
|
||||||
|
description: |
|
||||||
|
Whole chapters this run has delivered, or `null` when the question does not apply —
|
||||||
|
an order expressed in CHARACTERS stops inside a chapter and closes none, and `0` there
|
||||||
|
would read as "nothing happened" when work was in fact done and paid for.
|
||||||
|
term_consistency_funded:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether THIS run's reservation carries the book-wide terminology pass. It is a flat bond
|
||||||
|
on the book, not a per-chapter cost, so a small order can be short of it while a large
|
||||||
|
one is not — and the screen must say so BEFORE the click, not after.
|
||||||
progress: { $ref: '#/components/schemas/Progress' }
|
progress: { $ref: '#/components/schemas/Progress' }
|
||||||
paused_reason:
|
paused_reason:
|
||||||
oneOf:
|
oneOf:
|
||||||
|
|
@ -2504,7 +2560,7 @@ components:
|
||||||
|
|
||||||
1. the `409` of `POST /books/{bookId}/runs` (`ceiling_unavailable`) — the only authority on
|
1. the `409` of `POST /books/{bookId}/runs` (`ceiling_unavailable`) — the only authority on
|
||||||
whether THIS start may happen; every read below can be stale by the time it is used;
|
whether THIS start may happen; every read below can be stale by the time it is used;
|
||||||
2. `RunOptions.ceiling.max_chapters == 0` and `RunOptions.blocked` — whether a start is worth
|
2. `RunOptions.order.verdict: covers_none` and `RunOptions.blocked` — whether a start is worth
|
||||||
offering AT ALL right now, and what is holding it. This is what the start screen draws;
|
offering AT ALL right now, and what is holding it. This is what the start screen draws;
|
||||||
3. `Run.paused_reason: credit_exhausted` — why a run that already ran stopped. History, not a
|
3. `Run.paused_reason: credit_exhausted` — why a run that already ran stopped. History, not a
|
||||||
gate: a new run may still be startable (see `resumeRun`);
|
gate: a new run may still be startable (see `resumeRun`);
|
||||||
|
|
@ -2514,9 +2570,16 @@ components:
|
||||||
|
|
||||||
A client that read them in the opposite order would refuse to offer a run the server would
|
A client that read them in the opposite order would refuse to offer a run the server would
|
||||||
have accepted.
|
have accepted.
|
||||||
required: [ceiling, blocked]
|
required: [order, balance_micro_usd, limit, blocked]
|
||||||
properties:
|
properties:
|
||||||
ceiling: { $ref: '#/components/schemas/CeilingBounds' }
|
order: { $ref: '#/components/schemas/OrderOptions' }
|
||||||
|
balance_micro_usd:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: |
|
||||||
|
What the account can still spend, in micro-dollars. ⚠ A quantity, not arithmetic: a hold
|
||||||
|
is a debit when taken, so this figure ALREADY excludes the holds open against it.
|
||||||
|
limit: { $ref: '#/components/schemas/MoneyLimit' }
|
||||||
blocked:
|
blocked:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/Blocked'
|
- $ref: '#/components/schemas/Blocked'
|
||||||
|
|
@ -2526,6 +2589,86 @@ components:
|
||||||
an account's second book shows a shrunken scale with no way to learn that its own first
|
an account's second book shows a shrunken scale with no way to learn that its own first
|
||||||
book is the reason.
|
book is the reason.
|
||||||
|
|
||||||
|
OrderOptions:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
What can be ordered on this book right now, and whether the balance covers it. ⚠ **Added by
|
||||||
|
the order-form minor (0.11.0).**
|
||||||
|
required: [chapters_left, affordable_chapters, verdict, chapter_orders, structure, source_chars, term_consistency_funded, estimate]
|
||||||
|
properties:
|
||||||
|
chapters_left:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: Chapters of this book not yet delivered.
|
||||||
|
affordable_chapters:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: |
|
||||||
|
How many of those the balance covers, already clamped to `chapters_left`. A client MUST
|
||||||
|
NOT clamp it again.
|
||||||
|
verdict:
|
||||||
|
type: string
|
||||||
|
enum: [covers_all, covers_part, covers_none]
|
||||||
|
description: |
|
||||||
|
The one-word answer the start screen draws BEFORE the click. Computed by the same
|
||||||
|
arithmetic that admits the order, so the verdict and the click cannot disagree.
|
||||||
|
chapter_orders:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether an order may be phrased in CHAPTERS at all. `false` when this build does not
|
||||||
|
trust the book's chapter boundaries — then the only partial order is in characters, and
|
||||||
|
the screen must say so in words rather than offering a slider against a cut nobody made.
|
||||||
|
structure:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
- type: 'null'
|
||||||
|
description: |
|
||||||
|
Where the chapter boundaries came from, passed through from the engine's manifest.
|
||||||
|
⚠ **An open vocabulary that GROWS additively** — today `declared`, `detected`, `none`.
|
||||||
|
A value this build does not know is read as "not recognised" and never promoted into
|
||||||
|
trust; a client MUST do the same rather than switch on an exhaustive list.
|
||||||
|
source_chars:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: Characters of the source this order is priced against.
|
||||||
|
term_consistency_funded:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether the balance covers the book-wide terminology bond ON TOP of this order. A flat
|
||||||
|
bond on the book, so a small order can fall short of it while a large one does not —
|
||||||
|
and the degradation must be visible BEFORE the click.
|
||||||
|
estimate: { $ref: '#/components/schemas/Estimate' }
|
||||||
|
|
||||||
|
Estimate:
|
||||||
|
type: object
|
||||||
|
description: What an order is expected to cost, and what will be reserved for it.
|
||||||
|
required: [expected_micro_usd, hold_micro_usd]
|
||||||
|
properties:
|
||||||
|
expected_micro_usd:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: |
|
||||||
|
The engine's a-priori projection for this order, in micro-dollars — derived from the
|
||||||
|
text before a single call is made, not a running total.
|
||||||
|
hold_micro_usd:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
description: |
|
||||||
|
What is actually reserved: the expectation with headroom, plus the engine's largest
|
||||||
|
INDIVISIBLE step so the last call of the order cannot be refused for want of room.
|
||||||
|
|
||||||
|
MoneyLimit:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
Bounds of the money slider. ⚠ **These two are NOT a range.** The minimum is the engine's
|
||||||
|
indivisible step — the smallest order any run could move under — and it can exceed the
|
||||||
|
maximum when the balance does not carry even one reservation. That is the honest shape of
|
||||||
|
"nothing can be bought", and `order.verdict: covers_none` says it in a word.
|
||||||
|
required: [min_micro_usd, max_micro_usd]
|
||||||
|
properties:
|
||||||
|
min_micro_usd: { type: integer, minimum: 0 }
|
||||||
|
max_micro_usd: { type: integer, minimum: 0 }
|
||||||
|
|
||||||
Blocked:
|
Blocked:
|
||||||
type: object
|
type: object
|
||||||
description: |
|
description: |
|
||||||
|
|
@ -2551,11 +2694,15 @@ components:
|
||||||
Bounds of the run-limit scale, in CHAPTERS. The conversion to money lives on the platform and
|
Bounds of the run-limit scale, in CHAPTERS. The conversion to money lives on the platform and
|
||||||
is not exposed here in any form.
|
is not exposed here in any form.
|
||||||
|
|
||||||
⚠ **THIS PARAGRAPH IS THE RETIRED RULE, KEPT UNTIL ITS FIELDS LAND (05.09).** The owner retired
|
⛔⛔ **RETIRED BY THE ORDER-FORM MINOR (0.11.0), 06.09 — THIS SCHEMA IS SERVED BY NOTHING.**
|
||||||
the blanket «no money on screen» ban; an order now states its ceiling AS MONEY and the account
|
`GET /books/{bookId}/run-options` no longer carries a `ceiling` member at all; its answer is
|
||||||
shows a balance. The money fields arrive with the order-form pack — the schema is NOT moved
|
`order` + `balance_micro_usd` + `limit` + `blocked` (see `RunOptions`). Everything above is
|
||||||
ahead of the code, so what is written above still describes what this version serves. What is
|
kept as the history of the chapter-scale era and describes NO live wire.
|
||||||
NOT retired and never was: the price of a model, the cost of a stage, the price of one call.
|
⚠ The paragraph that stood here until 06.09 said the opposite — «the schema is NOT moved ahead
|
||||||
|
of the code, so what is written above still describes what this version serves». It was written
|
||||||
|
expecting the canon never to lag the code; the landing did the reverse, and the sentence
|
||||||
|
outlived its condition. What is NOT retired and never was: the price of a model, the cost of a
|
||||||
|
stage, the price of one call.
|
||||||
|
|
||||||
`max_chapters` is what the account can still spend, ALREADY clamped to what is left of the
|
`max_chapters` is what the account can still spend, ALREADY clamped to what is left of the
|
||||||
book; a client MUST NOT clamp it again. ⚠ A quantity, not arithmetic: a hold is a debit when
|
book; a client MUST NOT clamp it again. ⚠ A quantity, not arithmetic: a hold is a debit when
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue