From 5dcf10adb44bac92ec773cedaad7f9468ccbf232 Mon Sep 17 00:00:00 2001 From: Patrick McEvoy Date: Sat, 26 Sep 2026 21:15:31 +0100 Subject: [PATCH] Document bounded V5 first-contact recovery Refs BKLG-20260923-ko6v; Q-20260923-2azh. --- docs/liskov/concepts/replacement-custody.md | 14 +++++++++++--- docs/liskov/operate/deployments-jobs.md | 10 ++++++++++ docs/liskov/reference/capabilities.md | 1 + .../troubleshooting/execution-coverage.md | 17 +++++++++++++++++ scripts/check-liskov-docs.mjs | 8 ++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/liskov/concepts/replacement-custody.md b/docs/liskov/concepts/replacement-custody.md index 5b4cb15..ba994cf 100644 --- a/docs/liskov/concepts/replacement-custody.md +++ b/docs/liskov/concepts/replacement-custody.md @@ -49,9 +49,17 @@ ambiguous nonzero gate into “complete.” ## Failure budgets Launch retries are bounded by policy and surfaced through the Action Plan. -Runtime replace-after-failure is not enabled in the first public capability -set; v1 waits for scheduled end. This avoids hiding repeated spend or creating -unbounded replacement loops. +For a V5 registration that makes no signed runtime contact, Liskov may buy one +replacement for the occurrence after the verified scheduled start, the assigned +maximum start delay, and five more minutes. It does so only while other jobs +have recently made verified contact with Liskov. The original registration and +its financial closeout continue independently, and a late contact does not +erase a replacement already reserved. The absence of contact does not establish +a fault in your application or prove that the processor crashed. + +Other runtime replace-after-failure behavior remains internal; v1 otherwise +waits for scheduled end. The one no-contact replacement still passes the +authored spend ceiling and launch pacing, so it may be deferred or refused. The core distinction is simple: policy describes allowed intent; evidence describes what actually happened. diff --git a/docs/liskov/operate/deployments-jobs.md b/docs/liskov/operate/deployments-jobs.md index b6a29a7..e1e65af 100644 --- a/docs/liskov/operate/deployments-jobs.md +++ b/docs/liskov/operate/deployments-jobs.md @@ -48,6 +48,16 @@ Processor assignment is not runtime readiness. A registered job can still be waiting to boot, fetch configuration, obtain required secret grants, or report health. +For a V5 job that never makes signed runtime contact, Liskov can replace the +paid registration once its chain-verified start, assigned maximum start delay, +and five-minute grace have passed. This can create another paid job while the +first registration remains open. A recent verified contact from elsewhere in +the fleet is required; if Liskov cannot establish that its contact ingest is +working, it defers replacement and spends nothing. Coverage and Activity name +the no-contact reason and whether recovery is waiting, proceeding, or refused. +An exact processor selection with no other eligible processor can be refused +without buying a second registration. + After a job's strict reporting window closes, the timeline may say **Not billed — no report filed**. For managed custody this means the finalized scanner proved report absence, the charge is zero, the full reserve is released, and there is diff --git a/docs/liskov/reference/capabilities.md b/docs/liskov/reference/capabilities.md index ccbe63e..df77148 100644 --- a/docs/liskov/reference/capabilities.md +++ b/docs/liskov/reference/capabilities.md @@ -63,6 +63,7 @@ This page is the availability owner. Guides contain only supported recipes. | Cooperative cease | Release-gated v1 | | Fixed-interval execution (Manifest V5 `execution.mode: interval`) | Release-gated v1; the schema accepts `every` and an optional `until`, but no interval run has been observed in production and Liskov does not yet launch an interval Application: it stops before any Service Credit reserve or job is created. The accepted behavior is one occurrence per boundary, no overlap, no catch-up burst after a pause or outage, and a recorded skip for a missed boundary. Cron, calendar, and local-time schedules are not v1 | | Launch retry budget | v1; `maxRetries` 0–10, default 5 | +| V5 job with no signed first contact | v1; at most one early replacement per occurrence after the chain-verified start, its assigned maximum start delay, and five more minutes. Requires a verified fleet contact within 120 seconds; normal spend and placement limits still apply | | Runtime replace-after-failure | Internal; v1 waits for scheduled end | ## Operation, money, and custody diff --git a/docs/liskov/troubleshooting/execution-coverage.md b/docs/liskov/troubleshooting/execution-coverage.md index 19ad098..6709213 100644 --- a/docs/liskov/troubleshooting/execution-coverage.md +++ b/docs/liskov/troubleshooting/execution-coverage.md @@ -42,6 +42,23 @@ Console labels: Quiet is not stalled. Ended-unsettled is a remaining-charge state, not overdue execution. Incomplete, stale, and unknown never become quiet or overdue. +## A registered V5 job never contacted Liskov + +If Coverage names **No signed first contact**, check the registered start and +latest-start allowance before treating the job as late. Liskov permits one +early replacement only after that allowance and five more minutes. The +replacement is still subject to the Application's spend limit, launch pacing, +and an eligible processor. An exact selection with no alternative can refuse +the replacement before another reserve is made. + +**Waiting for verified fleet contact** means Liskov cannot currently prove its +runtime-contact ingest is healthy; it defers the replacement without spending. +Refresh Coverage and Activity for the recorded recovery decision. Do not infer +that your code failed or that the processor crashed from missing contact alone. +If the deferral persists after contact resumes, collect the Application and +job identifiers for support. The first registration keeps its own scheduled +end and financial closeout even if a successor starts. + ## Remaining charges are not Coverage below desired An ended job can still have: diff --git a/scripts/check-liskov-docs.mjs b/scripts/check-liskov-docs.mjs index 8671e15..0d760b5 100644 --- a/scripts/check-liskov-docs.mjs +++ b/scripts/check-liskov-docs.mjs @@ -398,6 +398,8 @@ check(!existsSync(join(root, 'static', 'examples', 'liskov')), 'superseded downl const retirementPage = readFileSync(join(docsRoot, 'operate', 'retire.md'), 'utf8'); const capabilitiesPage = readFileSync(join(docsRoot, 'reference', 'capabilities.md'), 'utf8'); +const replacementCustodyPage = readFileSync(join(docsRoot, 'concepts', 'replacement-custody.md'), 'utf8'); +const executionCoveragePage = readFileSync(join(docsRoot, 'troubleshooting', 'execution-coverage.md'), 'utf8'); const githubActionsPage = readFileSync(join(docsRoot, 'build', 'github-actions.md'), 'utf8'); check(capabilitiesPage.includes('| Encrypted JavaScript payload delivery | Release-gated v1;'), 'encrypted JavaScript must preserve the separate registered V5 public-release gate'); @@ -1009,6 +1011,12 @@ check( /\| Console Coverage and Executions convergence strip \| v1;/.test(capabilitiesPage), 'capability matrix omits the released Console convergence strip', ); +check( + /\| V5 job with no signed first contact \| v1; at most one early replacement per occurrence/.test(capabilitiesPage) + && /assigned\s+maximum start delay, and five more minutes/.test(replacementCustodyPage) + && /Waiting for verified fleet contact/.test(executionCoveragePage), + 'public V5 first-contact recovery boundary or customer explanation is missing', +); check( /\| CLI execution-convergence sibling on `application execution show` \| Release-gated v1;/.test(capabilitiesPage), 'capability matrix does not gate the unreleased CLI convergence sibling',