Skip to content

feat(inference): make llama.cpp receipt publication recoverable - #8422

Merged
ericksoa merged 13 commits into
mainfrom
feat/llamacpp-receipt-clean-8414
Aug 6, 2026
Merged

feat(inference): make llama.cpp receipt publication recoverable#8422
ericksoa merged 13 commits into
mainfrom
feat/llamacpp-receipt-clean-8414

Conversation

@ericksoa

@ericksoa ericksoa commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a durable receipt-prepared journal phase containing exact canonical, secret-free receipt bytes and their digest
  • replace one-shot persistence with a transaction- and target-bound exact idempotent writer
  • preserve and replay the exact live runtime after ambiguous prepare, writer, or finalize outcomes
  • recover only after lease acquisition and fresh engine, network, container, model, API-key, and readiness reproof
  • reject partial receipt state, receipt/value/target drift, malformed journal state, and noncanonical bytes before external mutation

This PR is stacked only on the clean dormant lifecycle base in #8418. It remains unregistered and changes no production onboarding, routing, YAML schema, or support claim.

Validation

  • 142 focused lifecycle, GGUF, authority, journal, and source-boundary tests
  • source and CLI TypeScript checks
  • repository, source-shape, test-size, test-title, and test-conditional checks
  • Biome and git diff --check
  • independent security/crash-recovery review: pass
  • independent documentation review: no docs needed

Signed-off-by: Aaron Erickson aerickson@nvidia.com

Closes #8414
Part of #8144

Summary by CodeRabbit

  • Reliability

    • Improved durable receipt publication with preparation, validation, acknowledgment, and finalization.
    • Added protection against receipt tampering, conflicting writes, incomplete states, and invalid serialized data.
    • Enhanced recovery for interrupted operations, including safe rollback and receipt replay.
  • Bug Fixes

    • Improved consistency checks across runtime, filesystem, API key, network, and receipt data during recovery.
    • Finalizing an already finalized receipt is now handled safely and consistently.

@copy-pr-bot

copy-pr-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The Docker llama.cpp lifecycle now uses operation-scoped receipt writers. The host-local journal records canonical receipt bytes and publication state. Recovery revalidates authority, replays prepared receipts exactly, and finalizes or rolls back unfinished operations.

Changes

Llama.cpp receipt recovery

Layer / File(s) Summary
Receipt journal contract
src/lib/onboard/runtime-provider/host-local-inference.ts, src/lib/onboard/runtime-provider/host-local-create-journal.ts, src/lib/onboard/runtime-provider/host-local-create-journal.test.ts
Adds HostLocalInferenceReceiptWriter, the receipt-prepared phase, canonical receipt storage, target metadata, digest validation, and idempotent finalization.
Lifecycle receipt publication
src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts, src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts
Replaces callback persistence and generated transaction IDs with exact writer publication. The lifecycle binds writer authority to the journal and preserves rollback boundaries for uncertain publication outcomes.
Unfinished-operation recovery
src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts, src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts
Recovery validates writer, runtime, model, API-key, network, and filesystem authority before replaying prepared receipts or rolling back other unfinished phases.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DockerLlamaCppManagedLifecycle
  participant HostLocalCreateJournalStore
  participant HostLocalInferenceReceiptWriter
  DockerLlamaCppManagedLifecycle->>HostLocalCreateJournalStore: prepare canonical receipt
  DockerLlamaCppManagedLifecycle->>HostLocalInferenceReceiptWriter: writeExact canonical receipt
  HostLocalInferenceReceiptWriter-->>DockerLlamaCppManagedLifecycle: exact committed bytes
  DockerLlamaCppManagedLifecycle->>HostLocalCreateJournalStore: finalize receipt publication
Loading

Possibly related issues

  • NVIDIA/NemoClaw#8395 — Defines the Docker llama.cpp lifecycle and authority contracts extended by this receipt recovery work.

Possibly related PRs

  • NVIDIA/NemoClaw#8418 — Provides the Docker llama.cpp lifecycle and host-local journal implementation extended here.
  • NVIDIA/NemoClaw#8399 — Shares the managed llama.cpp receipt persistence and recovery paths.
  • NVIDIA/NemoClaw#8261 — Shares Docker managed-bootstrap journal and unfinished-operation recovery behavior.

Suggested labels: area: inference, area: local-models, area: providers, feature, platform: container

Suggested reviewers: apurvvkumaria, cv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: making llama.cpp receipt publication recoverable.
Linked Issues check ✅ Passed The changes implement the durable receipt phase, exact writer, drift validation, recovery replay, and lifecycle preservation required by issue #8414.
Out of Scope Changes check ✅ Passed The changes remain within issue #8414 scope and cover only llama.cpp lifecycle, journal, receipt-writer, and related tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/llamacpp-receipt-clean-8414

Comment @coderabbitai help to get the list of available commands.

@ericksoa
ericksoa requested review from apurvvkumaria and cv August 6, 2026 02:07
@github-code-quality

github-code-quality Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 46de11b in the feat/llamacpp-receip... branch remains at 96%, unchanged from commit 26bd9f5 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 46de11b in the feat/llamacpp-receip... branch remains at 82%, unchanged from commit 26bd9f5 in the main branch.

Show a code coverage summary of the most impacted files.
File main 26bd9f5 feat/llamacpp-receip... 46de11b +/-
src/lib/onboard...flight-ports.ts 67% 33% -34%
src/lib/onboard...box-prebuild.ts 91% 77% -14%
src/lib/onboard...shboard-port.ts 96% 90% -6%
src/lib/onboard/preflight.ts 80% 79% -1%
src/lib/onboard...trap/adapter.ts 68% 69% +1%
src/lib/credentials/store.ts 55% 56% +1%
src/lib/onboard...er-gpu-patch.ts 80% 81% +1%
src/lib/adapter...shell/client.ts 88% 90% +2%
src/lib/domain/.../connect-env.ts 89% 97% +8%
src/lib/onboard/docker-cdi.ts 70% 80% +10%

Updated August 06, 2026 03:21 UTC

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · medium confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Model comparison: normalized findings match; normalized terminology decisions differ; normalized E2E selections differ; severity counts match.
5 terminology differences from the second opinion

Advisory only. These are normalized differences from the primary terminology receipt.

  • prepareReceipt at src/lib/onboard/runtime-provider/host-local-create-journal.ts:628: selected only by the second-opinion lane as established.
  • writePreparedReceipt at src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts:935: selected only by the second-opinion lane as established.
  • receipt-prepared at src/lib/onboard/runtime-provider/host-local-create-journal.ts:24: selected only by the second-opinion lane as justified.
  • HostLocalInferenceReceiptWriter at src/lib/onboard/runtime-provider/host-local-inference.ts:137: selected only by the second-opinion lane as define.
  • receiptTargetSha256 at src/lib/onboard/runtime-provider/host-local-create-journal.ts:45: selected only by the second-opinion lane as justified.
1 additional E2E selection from the second opinion

Advisory only. The primary lane did not select these E2E jobs or targets.

  • llama-cpp-dgx-spark-qualification: The completed second-opinion lane identified E2E coverage that the primary lane omitted.

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

3 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • justified — receipt publication at src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts:1468: Keep “receipt publication” for the external write boundary; the modifier identifies the persisted external-state action.
  • established — receipt writer at src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts:926: Keep “receipt writer”; this patch expands its transaction and target identity contract without changing the established concept.
  • justified — publication authority at src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts:948: Keep “publication authority”; it distinguishes the writer binding from existing lifecycle and engine authority concepts.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: inference-routing, network-policy, onboard-repair, onboard-resume, cloud-onboard

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

Base automatically changed from feat/llamacpp-lifecycle-clean-8395 to main August 6, 2026 03:01
…-clean-8414

# Conflicts:
#	src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts
#	src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts
#	src/lib/onboard/runtime-provider/host-local-create-journal.test.ts
#	src/lib/onboard/runtime-provider/host-local-create-journal.ts
#	src/lib/onboard/runtime-provider/host-local-inference.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts (1)

1355-1371: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the prepare-outcome classification into a named helper.

The nested try/catch inside the main try decides one thing: whether receipt publication may already be durable. It uses a nested try, an empty catch, and three assignments to receiptPublicationPossible to express that decision. start already spans about 160 lines, and the coding guidelines require low function complexity.

Extract the classification so the sequencing in start stays linear and the fail-safe direction is stated once.

♻️ Proposed extraction

Add a module-level helper near writePreparedReceipt:

/** Reports whether a failed prepare may have left publication authority durable. */
function preparePublicationPossible(
  store: HostLocalCreateJournalStore,
  transactionId: string,
): { possible: boolean; journal: HostLocalCreateJournalRecord | null } {
  try {
    const persisted = store.load(transactionId);
    if (persisted === null) return { possible: true, journal: null };
    const journal = normalizeHostLocalCreateJournalRecord(persisted);
    return { possible: journal.phase !== "started", journal };
  } catch {
    // The durable outcome is unknown, so treat publication as possible.
    return { possible: true, journal: null };
  }
}

Then simplify the call site:

         try {
           journal = options.journalStore.prepareReceipt(transactionId, serialized);
           receiptPublicationPossible = true;
         } catch (error) {
-          try {
-            const persisted = options.journalStore.load(transactionId);
-            if (persisted === null) {
-              receiptPublicationPossible = true;
-            } else {
-              journal = normalizeHostLocalCreateJournalRecord(persisted);
-              receiptPublicationPossible = journal.phase !== "started";
-            }
-          } catch {
-            receiptPublicationPossible = true;
-          }
+          const outcome = preparePublicationPossible(options.journalStore, transactionId);
+          receiptPublicationPossible = outcome.possible;
+          if (outcome.journal !== null) journal = outcome.journal;
           throw error;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts`
around lines 1355 - 1371, Extract the nested prepare-outcome classification from
start into a module-level preparePublicationPossible helper near
writePreparedReceipt. Have it load and normalize the persisted journal, return
both the fail-safe possible flag and journal value, and treat load or
normalization failures as possible with a null journal; update start to use this
result before rethrowing the original prepare error.

Source: Coding guidelines

src/lib/onboard/runtime-provider/host-local-create-journal.test.ts (1)

168-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for a receipt that is canonical but bound to a different authority.

The new cases reach three distinct rejection points: the phase invariant, the parser, and the digest recompute. None reach the authority-binding branch that raises prepared receipt differs from create authority in normalizeHostLocalCreateJournalRecord. That branch checks providerId, service, runtime.runtimeId, runtime.specSha256, model.generation, and engineAuthority.

Rejecting receipt drift is a stated objective of this PR. Add a case that stores a canonical receipt with a correct digest but a mismatched generation or runtimeId.

💚 Proposed additional case
it("rejects a canonical receipt bound to another transaction (`#8414`)", () => {
  const store = createHostLocalCreateJournalStore(stateDirectory);
  store.create(prepared());
  store.recordCreating(TRANSACTION_ID, CREATE_INTENT_UNIX_MS);
  store.recordCreated(TRANSACTION_ID, RUNTIME_ID);
  store.recordStarted(TRANSACTION_ID);

  const foreign = serializeHostLocalInferenceReceipt({
    ...JSON.parse(serializedReceipt()),
    runtime: {
      ...JSON.parse(serializedReceipt()).runtime,
      model: { ...JSON.parse(serializedReceipt()).runtime.model, generation: "c".repeat(64) },
    },
  });
  expect(() => store.prepareReceipt(TRANSACTION_ID, foreign)).toThrow(
    "prepared receipt differs from create authority",
  );
  expect(store.load(TRANSACTION_ID)?.phase).toBe("started");
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/runtime-provider/host-local-create-journal.test.ts` around
lines 168 - 192, Add a test near the existing receipt tampering coverage that
creates the journal through the started phase, serializes a canonical receipt
with a mismatched authority field such as model generation or runtime ID while
preserving a valid digest, and verifies prepareReceipt rejects it with “prepared
receipt differs from create authority” while the journal remains in the started
phase. Reuse the existing helpers and constants, including
createHostLocalCreateJournalStore, prepared, serializedReceipt, TRANSACTION_ID,
and RUNTIME_ID.
src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts (1)

978-1009: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for a malformed receipt writer.

requireReceiptWriter is a new validation at both public entrypoints. It raises Docker llama.cpp receipt writer authority is malformed. when transactionId or targetSha256 is not a SHA-256 string, or when writeExact is not a function. The drift test covers only well-formed writers with mismatched identities, so that branch stays uncovered.

Assert that start rejects the malformed writer before it touches the engine or the journal.

💚 Proposed additional case
it("rejects a malformed receipt writer before any mutation (`#8414`)", () => {
  const fixture = dockerFixture();
  const store = journalStore();
  const lifecycle = controller(fixture, store);

  expect(() =>
    lifecycle.start({ ...receiptWriter(), targetSha256: "not-a-digest" }),
  ).toThrow("receipt writer authority is malformed");
  expect(fixture.capture).not.toHaveBeenCalled();
  expect(store.list()).toEqual([]);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts`
around lines 978 - 1009, Add a test beside the existing receipt-writer drift
coverage that passes a malformed writer, such as one returned by receiptWriter
with an invalid targetSha256, to lifecycle.start. Assert it throws the
receipt-writer authority malformed error and verify fixture.capture was not
called and journalStore.list() remains empty, proving validation occurs before
engine or journal mutation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts`:
- Around line 978-1009: Add a test beside the existing receipt-writer drift
coverage that passes a malformed writer, such as one returned by receiptWriter
with an invalid targetSha256, to lifecycle.start. Assert it throws the
receipt-writer authority malformed error and verify fixture.capture was not
called and journalStore.list() remains empty, proving validation occurs before
engine or journal mutation.

In `@src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts`:
- Around line 1355-1371: Extract the nested prepare-outcome classification from
start into a module-level preparePublicationPossible helper near
writePreparedReceipt. Have it load and normalize the persisted journal, return
both the fail-safe possible flag and journal value, and treat load or
normalization failures as possible with a null journal; update start to use this
result before rethrowing the original prepare error.

In `@src/lib/onboard/runtime-provider/host-local-create-journal.test.ts`:
- Around line 168-192: Add a test near the existing receipt tampering coverage
that creates the journal through the started phase, serializes a canonical
receipt with a mismatched authority field such as model generation or runtime ID
while preserving a valid digest, and verifies prepareReceipt rejects it with
“prepared receipt differs from create authority” while the journal remains in
the started phase. Reuse the existing helpers and constants, including
createHostLocalCreateJournalStore, prepared, serializedReceipt, TRANSACTION_ID,
and RUNTIME_ID.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d60a3dcb-dd1d-4c94-a1c1-a4a79f5f25e0

📥 Commits

Reviewing files that changed from the base of the PR and between 43be454 and b719c41.

📒 Files selected for processing (5)
  • src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.test.ts
  • src/lib/onboard/runtime-provider/docker-llama-cpp-managed-lifecycle.ts
  • src/lib/onboard/runtime-provider/host-local-create-journal.test.ts
  • src/lib/onboard/runtime-provider/host-local-create-journal.ts
  • src/lib/onboard/runtime-provider/host-local-inference.ts

@ericksoa
ericksoa merged commit bf429e9 into main Aug 6, 2026
45 checks passed
@ericksoa
ericksoa deleted the feat/llamacpp-receipt-clean-8414 branch August 6, 2026 03:45
@github-actions github-actions Bot added the v0.0.104 Release target label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v0.0.104 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Managed inference] Make llama.cpp receipt publication crash-recoverable

1 participant