docs(events): a step's retry delay, MODULE_API 1.15.0 (PLAN_REDESIGNS §14.3.15)

- MODULE_API.md: 1.15.0, the failure envelope's retryAfterMs (clamped to
  one to thirty minutes, one attempt, ignored beside retry: false), the
  rule bullet and the registration line.
- EVENTS.md §F: the envelope rule and the flat delay as a default.
- STEAM_SIGNIN.md: its module contract moves to 1.16.0.
- PLAN_REDESIGNS: §14.3.15, the stage-2 record (six settled points, the
  test-harness gap found on the way), and pointers from §9's summary and
  §14.3.13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-15 02:41:12 -05:00
parent 9e501719d0
commit 7d51820bfe
4 changed files with 81 additions and 6 deletions

View File

@@ -518,7 +518,9 @@ contract would be a contract the modules were never asked about. An admin's own
Three numbers govern a step, and they live in the runner rather than in a column because no authoring
surface would ever show them: `EVENT_STEP_MAX_ATTEMPTS` (3), `EVENT_STEP_RETRY_MS` (60 000, flat), and
`EVENT_RUN_LEASE_MS` (15 minutes). A step's own lease is not one of them — it is computed from that
`EVENT_RUN_LEASE_MS` (15 minutes). The flat delay is the default rather than the only one since
MODULE_API 1.15.0: a failure that answers `retryAfterMs` is retried at that, clamped to one to thirty
minutes (§F). A step's own lease is not one of them — it is computed from that
action's declared `budgetMs` plus a minute, because a registry that lets an action declare an hour
would otherwise have its steps reclaimed and re-dispatched fifty-nine minutes before they answered.
@@ -1000,6 +1002,14 @@ silently did nothing.
"keep what you have" because staleness is cheap; an action's default is **"nothing happened,
retry"**, because the expensive mistake is recording a world change that did not occur. A rejected
promise, a throw, a timeout, a non-object and a missing `ok` are all `{ ok: false, retry: true }`.
- **A retryable failure may say when to try again** (MODULE_API 1.15.0; Rust PLAN_REDESIGNS
§14.3.13, D371): `{ ok: false, retry: true, retryAfterMs }`. `classify()` clamps it to 60 000 –
1 800 000 ms rather than refusing it, because it is the module's hint and not an author's value;
anything that is not a number is dropped and logged, and the flat delay applies. It is still one
attempt of `EVENT_STEP_MAX_ATTEMPTS`, it degrades the run like any retry, and it is ignored beside
`retry: false`. The `step.retry` log line carries `retryAt` on every retry (and `retryAfterMs` when
hinted), so the console says *"retrying at 20:10"*. A run paused or cancelled in the wait is not
retried, because `findDue` reads only running runs.
- **`cost` is declared by the module and enforced by core, before dispatch.** A module says what a
verb consumes; core owns the caps and the arithmetic. A module cannot spend a budget it did not
declare, and cannot under-report to escape a cap without lying in code the operator installed —