Skip to content

fix(dcode): classify managed non-interactive failures (#8121) - #8206

Merged
jyaunches merged 18 commits into
NVIDIA:mainfrom
TonyLuo-NV:worktree-fix-8121-dcode-noninteractive
Aug 5, 2026
Merged

fix(dcode): classify managed non-interactive failures (#8121)#8206
jyaunches merged 18 commits into
NVIDIA:mainfrom
TonyLuo-NV:worktree-fix-8121-dcode-noninteractive

Conversation

@TonyLuo-NV

@TonyLuo-NV TonyLuo-NV commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Managed Deep Agents Code non-interactive runs reported only error_class=unknown category=unknown retryable=false for most failures. This change classifies exact exception class objects from the bounded active client exception chain. The classes come from the pinned LangGraph SDK, HTTP transport, TLS, and managed model-configuration packages. Persisted checkpoint exception text is not classified because it contains forgeable repr(exception) output. Operator output uses a closed vocabulary and never emits exception messages, checkpoint content, or unlisted type names.

Related Issue

Partially addresses #8121.

Changes

  • Map exact imported client, transport, TLS, and managed model-configuration exception classes to fixed error class, category, and retryable values.
  • Walk only the bounded active __cause__ and __context__ chain.
  • Reject forged checkpoint representations, forged class/module names, application subclasses, and unlisted exception types.
  • Keep hop-neutral transport categories so an MCP, gateway, or policy failure is not described as upstream provider capacity.
  • Remove checkpoint database parsing and its dependence on serialized exception representations.
  • Add adversarial regression coverage and shared pinned-package fixtures.
  • Document the diagnostic fields, correlation ID, retry semantics, unknown fallback, and trust boundary.
  • Make the fresh re-onboard live E2E check poll status health within a fixed bound before failing closed.

Leakage boundary

Classification inputs are exact imported exception class objects in the active client process. Outputs are literals from the classifier table plus the existing correlation ID. Exception messages, checkpoint content, forged names, and unlisted type names never reach logs or the console.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Exact-head security review at 09d437439 passed all nine repository categories. Classification uses exact imported exception classes, bounds the cause/context walk, ignores forgeable checkpoint representations, emits fixed labels without exception content, adds no dependency or authorization surface, and has adversarial regression coverage.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: docs-updated
  • Evidence: docs/manage-sandboxes/run-deep-agents-code.mdx documents the exact-class diagnostic fields, retry semantics, unknown fallback, checkpoint-text trust boundary, and fixed-label output behavior. The final E2E comment records the bounded readiness-retry boundary and removal condition.
  • Agent: Codex Desktop

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — 93 passed and 1 skipped across the seven classifier integration files; all 30 affected E2E-support tests pass.
  • Applicable broad gate passed — npm run checks:repository, npm run test:changed, and npm run validate:pr passed.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Scope note: the exit-code half of #8121

#8121 also reports that the run exits 0. This PR does not change exit propagation. The patched handler already returns 1 and upstream exits with that value. Sibling issues #8137 and #8138 track the runtime exec transport behavior that requires a Linux sandbox to reproduce.


Signed-off-by: Tony Luo xialuo@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Improved non-interactive error reporting with consistent error categories and retryability indicators.
    • Added clearer classifications for transport, remote, model-configuration, and unknown failures.
    • Enhanced diagnostics to preserve correlation IDs and isolate errors between threads.
    • Sanitized error output to prevent sensitive exception or checkpoint details from being exposed.
    • Console messages now include category and retryability metadata for easier troubleshooting.

Managed Deep Agents Code non-interactive runs reduced every failure that
did not match one hard-coded provider-capacity string to
`error_class=unknown category=unknown retryable=false`, so an operator
could not tell a transport failure from an authorization rejection or a
genuine upstream outage.

Replace the single checkpoint pattern with an ordered classifier table
covering capacity, rate limiting, authorization, model/route lookup,
timeout, route reachability, and remote server errors, and add an
exception-type fallback for runs whose cause never reaches the
checkpoint database. Categories stay hop-neutral so an MCP, gateway, or
policy failure is never reported as upstream provider capacity, and both
the log line and the console line remain a closed vocabulary: no
checkpoint text, exception message, or unlisted type name is echoed.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Managed non-interactive diagnostics classify persisted and active errors across ordered categories. Classifiers scan bounded error windows and exception chains, emit fixed sanitized metadata, and include category and retryability in console output. Tests cover classification, isolation, correlation, chain handling, quoted-prose fallback, and secret protection.

Changes

Managed error classification

Layer / File(s) Summary
Classification rules
agents/langchain-deepagents-code/patch-managed-deepagents-code.py
Ordered classifiers for capacity, rate limits, authorization, routing, timeout, connectivity, and server errors. Allow-listed exception types with bounded chain traversal.
Diagnostic resolution
agents/langchain-deepagents-code/patch-managed-deepagents-code.py
Scans up to 20 recent checkpoint errors, applies the first matching classification, and falls back to active exception classification. Console diagnostics include error class, category, retryability, and correlation ID.
Classification validation
test/non-interactive-error-classification.test.ts
Covers structured classifications, retryability, correlation IDs, thread isolation, transport and remote failures, chained exceptions, quoted-prose handling, unknown exceptions, and secret-free output.

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

Suggested labels: bug-fix, integration: dcode

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: classifying managed non-interactive failures in dcode.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

The trailing group in the ResourceExhausted classifier was optional, so
it could never change whether the pattern matched. Match the bare
provider status name instead, and record why the longer NVIDIA#7415 wording
must not be required.

Signed-off-by: Tony Luo <xialuo@nvidia.com>

@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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@agents/langchain-deepagents-code/patch-managed-deepagents-code.py`:
- Around line 803-870: Update the persisted-error classification flow using
_NEMOCLAW_PERSISTED_ERROR_CLASSIFIERS so it extracts and classifies only a known
structured exception type or status field, rather than scanning the entire
__error__ prose; require the ResourceExhausted capacity qualifier where
applicable. When no structural indicator exists, return None and preserve the
active-exception fallback, and add negative fixtures covering quoted model/tool
text containing classifier terms.

In `@test/non-interactive-error-classification.test.ts`:
- Around line 52-54: Update the assertions covering the unlisted exception class
in the non-interactive error classification test to verify that the complete
class name, including its runtime_secret suffix, is absent from the combined
observable output. Keep the existing PLANTED_SECRETS checks, but add a full-name
assertion so separately logged class-name disclosure cannot pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 231d4e5e-0cb0-4db2-9e07-fd5b73385111

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4c42d and 06aef52.

📒 Files selected for processing (2)
  • agents/langchain-deepagents-code/patch-managed-deepagents-code.py
  • test/non-interactive-error-classification.test.ts

Comment thread agents/langchain-deepagents-code/patch-managed-deepagents-code.py Outdated
Comment thread test/non-interactive-error-classification.test.ts Outdated
@github-actions

github-actions Bot commented Aug 4, 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 · high 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 match; severity counts match.
5 terminology differences from the second opinion

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

  • closed vocabulary at agents/langchain-deepagents-code/patch-managed-deepagents-code.py:1221: primary classified it as justified; the second opinion classified it as established.
  • active client exception chain at docs/manage-sandboxes/run-deep-agents-code.mdx:86: primary classified it as define; the second opinion classified it as justified.
  • pinned exception class at docs/manage-sandboxes/run-deep-agents-code.mdx:86: primary classified it as define; the second opinion classified it as justified.
  • correlation ID at docs/manage-sandboxes/run-deep-agents-code.mdx:94: selected only by the second-opinion lane as established.
  • checkpoint exception at docs/manage-sandboxes/run-deep-agents-code.mdx:92: selected only by the second-opinion lane as justified.

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

Since last review: 0 prior items resolved · 0 still apply · 0 new items found

4 semantic terminology decisions

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

  • define — active client exception chain at docs/manage-sandboxes/run-deep-agents-code.mdx:86: Define this term at first use as the in-flight exception and its bounded cause/context chain.
  • define — pinned exception class at docs/manage-sandboxes/run-deep-agents-code.mdx:86: Define this term at first use as an exact imported class from a pinned dependency.
  • justified — persisted checkpoint exception text at docs/manage-sandboxes/run-deep-agents-code.mdx:92: Keep the modifier because it distinguishes untrusted stored representations from active exceptions.
  • justified — closed vocabulary at agents/langchain-deepagents-code/patch-managed-deepagents-code.py:1221: Keep the term because it describes the output disclosure constraint.

E2E guidance

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

Recommended E2E: cloud-inference, cloud-onboard, managed-image-multiarch-startup, security-posture, ubuntu-repo-cloud-langchain-deepagents-code

Workflow run details

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

…A#8121)

A persisted `__error__` row embeds model output and tool results, so
matching prose let quoted text decide the verdict: a row reading
`APIError('tool output: timeout')` produced `Timeout retryable=true` for
an unrelated failure.

Classify only on structural evidence — an exception or gRPC status name,
or a status code carried in a named status field. A row with no
structural indicator now stays unclassified and falls through to the
exception-type fallback, so an inconclusive row can no longer produce a
confident transport, timeout, authorization, or rate-limit claim. Add
negative fixtures covering quoted model and tool text, and assert that an
unlisted exception class name never reaches the output.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

Both CodeRabbit findings addressed in b641bf8.

Major — "Bind persisted categories to structural error data". Valid, and the failure mode is worse than misclassification: a persisted __error__ row embeds model output and tool results, so prose matching let quoted text decide the verdict. Classification now accepts only structural evidence:

  • an exception or gRPC/HTTP status name (ResourceExhausted, APIConnectionError, APITimeoutError, AuthenticationError, NotFoundError, RateLimitError, InternalServerError, …), matched case-sensitively because these are class names; or
  • a status code in a named status field (status_code=429, "status": 503, HTTP 503), never a bare number.

Every prose alternative is gone: timeout, connection refused, unauthorized, an internal error occurred, model … does not exist, and bare 4xx/5xx. A row with no structural indicator returns None and falls through to the exception-type fallback, then to unknown — inconclusive rather than confident.

New negative fixture does not classify prose quoted from model or tool output (#8121) covers exactly your example plus four siblings (tool output: timeout, the model replied: connection refused, assistant said the request was unauthorized, tool result contained 429 items, shell output: rate limit documentation), and asserts each yields only the RemoteException fallback verdict.

The note about the optional capacity qualifier was already fixed in 59a712a, before this review landed — the pattern is now the bare ResourceExhausted status name.

Minor — "Assert that the unlisted class name is absent". Valid; PLANTED_SECRETS used runtime-secret while the class name uses runtime_secret, so the assertion did not actually pin the no-name-disclosure behavior. Added the explicit not.toContain("SomeVendorSpecificFailure_runtime_secret").

npx vitest run --project integration test/non-interactive-error-classification.test.ts → 8 passed.

PRA-1 asked which source leaves a managed run without an `__error__`
checkpoint row to classify, why that source cannot be fixed in this
patch, and when the exception-type fallback can be removed.

Document both sources (a failure raised before the graph writes any
checkpoint, and the LangGraph server writing checkpoints from its own
process so a row can land after the client-side exception), the read-only
consumer boundary, the allow-list scope, the covering tests, and the
removal condition.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

PRA-1 addressed in 6835a8c.

The warning asked which source leaves a managed run with no __error__ row to classify, why that source cannot be fixed in this patch, and when the exception-type fallback can be removed. The fallback now carries the repo's boundary block naming two sources:

  • the failure is raised before the graph writes any checkpoint (transport, authorization, or model-resolution errors on the first request), so no row can exist; and
  • the LangGraph server writes checkpoints from its own process, so a row for a later failure can land after the client-side exception this handler is already reporting.

Source-fix constraint: NemoClaw cannot make a third-party server write a row for a failure that never reached it, and blocking here to wait for a late row would delay the exit path of every failing run for a write that may never arrive. Removal condition: drop the fallback once the managed runtime can read a structured cause for every failure — either the server persists a row before the client observes the exception, or upstream exposes a classified error object on the non-interactive path.

The block also records the read-only consumer boundary (/sandbox/.deepagents/.state/sessions.db), the type-name-only scope, the single call site, and the covering tests.

The two terminology decisions are kept as-is: the first-use explanation of "exception-type fallback" is retained and expanded, and "checkpoint row" stays the term for runtime-stored error records.

npx vitest run --project integration test/non-interactive-error-classification.test.ts → 8 passed.

The advisor's PRA-1 blocker is correct: restricting classification to
exception and status names still searched the complete persisted row, so
a checkpoint reading `APIError('tool output: ResourceExhausted')`
produced a confident capacity verdict from quoted model or tool payload
text — the exact misattribution the surrounding contract claims to
prevent.

Require a name to sit where a serializer would have written it: at the
start of the row, or directly after an opening delimiter, quote, or
module dot with no separating whitespace, and followed by its own message
or argument list. `ResourceExhausted: ...`,
`APIError('ResourceExhausted: ...')`, and `openai.RateLimitError: ...`
still classify, while the same names quoted inside payload prose do not.
Apply the same position rule to the named status field.

Add the parameterized regression the advisor asked for: every supported
name placed in quoted payload content must leave the row unclassified,
plus the serialized-position counterpart.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

PRA-1 blocker addressed in 2ab013f — the finding is correct and the earlier fix was incomplete.

Restricting classification to exception and status names still searched the complete persisted row, so APIError('tool output: ResourceExhausted') produced a confident upstream_provider_capacity verdict from quoted payload text. That is the same misattribution the surrounding comment claims to prevent; my negative fixtures only covered prose words, not allow-listed names inside payload content, so nothing caught it.

Classification now requires a name to sit where a serializer would have written it:

  • prefix: start of the row, or directly after an opening delimiter, quote, or module dot, with no separating whitespace
  • suffix: the name introduces its own message (Name:) or argument list (Name()

Accepted: ResourceExhausted: ..., APIError('ResourceExhausted: Worker local total request limit reached (32/32)'), openai.RateLimitError: ..., APIConnectionError('...'). Rejected: APIError('tool output: ResourceExhausted') and the same shape for every other supported name. The named status field carries the same rule, so APIError('tool said status_code=429') no longer classifies either.

Regressions added as requested:

  • does not classify a supported name quoted inside payload content (#8121) — parameterized over all 25 supported names plus the quoted status_code=429 case, asserting each row stays unclassified so the exception-type fallback decides.
  • classifies a supported name in serialized position (#8121) — the counterpart, including the name nested inside an outer exception repr.

npx vitest run --project integration test/non-interactive-error-classification.test.ts → 10 passed.

Note for the advisor lane: the Nemotron 3 Ultra job has failed on every run of this PR at Verify advisor analysis outcome with CONFIGURE_OUTCOME: failure / ANALYSIS_OUTCOME: skipped ("inference configuration did not complete"), and the Terra lane's last failure was same-session synthesis validation failed, with the finding above recovered from the partial artifact. Those look like advisor infrastructure failures rather than findings about this diff.

@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

cli-test-shards (5) (and therefore cli-tests / checks) is failing from a breakage on main, not from this PR.

test/managed-image-capability-union.test.ts asserts aiohttp==3.14.1 while the manifest it reads now pins aiohttp==3.14.3:

AssertionError: expected [ …(2) ] to deeply equal [ …(2) ]
  [
    "microsoft-teams-apps==2.0.13.4",
-   "aiohttp==3.14.1",
+   "aiohttp==3.14.3",
  ]
 ❯ test/managed-image-capability-union.test.ts:50:51

Two PRs raced on a stale base:

At main (f5049481b) git show main:src/lib/messaging/channels/teams/manifest.ts | grep aiohttp gives 3.14.3 while git show main:test/managed-image-capability-union.test.ts | grep aiohttp gives 3.14.1.

Evidence it is not PR-specific:

  • main at f5049481b reports checks and cli-tests as failure.
  • PR fix(sandbox): remove the incomplete snapshot when creation fails #8211, an unrelated change, fails the same cli-test-shards (5).
  • This branch does not contain test/managed-image-capability-union.test.ts at all, and still carries the pre-bump aiohttp==3.14.1 manifest, so the 3.14.3 in the failure can only come from main.

I have not touched it here, since absorbing another PR's fix into this one would mix concerns. The fix is to update the two literals in test/managed-image-capability-union.test.ts to 3.14.3. Four other tests (test/messaging-build-applier.test.ts, test/messaging-plan-image-boundary.test.ts, src/lib/messaging/compiler/manifest-compiler.test.ts, src/lib/messaging/channels/metadata.test.ts) also hard-code aiohttp==3.14.1 and pass only because they assert against fixtures rather than the manifest — worth checking in the same change. Happy to send that as a separate PR if a maintainer wants it.

E2E / PR Gate Coordination is pending the usual maintainer decision for a fork PR.

@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

Nemotron 3 Ultra lane reported 0 blockers on 2ab013f, with one warning about concurrent PR overlap on patch-managed-deepagents-code.py (#7463, #7822). I checked the merge risk mechanically rather than by inspection.

#7822 — no conflict. git merge-tree --write-tree HEAD refs/pull/7822/head merges clean. Its hunks sit at base lines 592-770 and 1161-1174 (provider class path, create_cli_agent, and run_non_interactive kwargs); mine start at 793 and cover the classifier tables and _nemoclaw_report_non_interactive_error. No shared region, and it does not read or write the classification path.

#7463 — conflicts, but not with this PR. The conflict is at base line ~546 in _get_provider_kwargs, on the extra_body assignment:

<<<<<<< HEAD
        reasoning_effort = managed_reasoning_effort()
        if reasoning_effort is not None:
            kwargs["extra_body"] = {"reasoning_effort": reasoning_effort}
=======
        if model_name in _NEMOCLAW_NEMOTRON_ULTRA_MODEL_IDS:
            kwargs["extra_body"] = { ... }
>>>>>>> refs/pull/7463/head

The HEAD side is main's code, not mine — managed_reasoning_effort() is at main:agents/langchain-deepagents-code/patch-managed-deepagents-code.py:533, and this PR's diff does not contain the string reasoning_effort at all. git merge-tree --write-tree upstream/main refs/pull/7463/head conflicts identically without this branch involved, so #7463 is stale against main and needs a rebase regardless of merge order here.

So no sequencing is required for this PR: classification tables, exception fallback, and the redaction boundary are untouched by both overlapping diffs.

On the terminology decisions: I have left .agents/skills/_shared/controlled-words.md alone, since adding shared vocabulary entries (serialized position, checkpoint-text, exception-type fallback) is a cross-cutting governance change that would also widen this PR's surface. Happy to add them here or in a separate PR if a maintainer prefers.

Both advisor lanes are still failing on their own harness rather than on findings — this run stopped with PR review advisor SDK execution failed: session: omitted required tool result(s): pr_review_read_terminology, and the finding above was recovered from the partial artifact.

cv and others added 2 commits August 4, 2026 04:01
The advisor reported two warnings: the documented late-checkpoint race
and the two scanning limits had no regression coverage, leaving the
constants I introduced unverified at their boundaries.

Add three tests. The first writes an `__error__` row from inside the
failing call, reproducing a row that lands after the exception but
before classification, and asserts the persisted classification wins
over the exception-type fallback. The second inserts one row beyond
`_NEMOCLAW_PERSISTED_ERROR_ROW_LIMIT` and asserts the only classifiable
row, sitting outside the window, is not reached. The third builds a
chain one link deeper than `_NEMOCLAW_EXCEPTION_CHAIN_LIMIT` and asserts
the transport cause beyond the limit is not matched and the walk
terminates.

Both boundary tests were mutation-checked: raising the limits to 21 and
9 makes each classify as `route_unreachable`, so they fail if the bound
stops being enforced.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

Both advisor warnings addressed in 17ac52f (Nemotron lane reported 0 blockers, 2 warnings; recovered from the partial artifact again — the job stopped with canonical review receipt mismatch after same-session validation).

Late checkpoint row race. still classifies a checkpoint row written after the exception is raised (#8121) writes the __error__ row from inside the failing call, which is the earliest point that reproduces "written after the exception, before classification", then asserts the persisted classification (route_unreachable) wins and the exception-type fallback does not pre-empt it. That pins the reason the scan window exists at all.

Boundary values. Two tests, one per constant:

  • scans only the newest rows within the persisted-row limit (#8121) — inserts one row beyond _NEMOCLAW_PERSISTED_ERROR_ROW_LIMIT, with the only classifiable cause as the oldest row, and asserts it is not reached.
  • stops walking the exception chain at the documented depth limit (#8121) — builds a chain one link deeper than _NEMOCLAW_EXCEPTION_CHAIN_LIMIT with the transport cause beyond the bound, and asserts it is not matched and the walk terminates.

Both were mutation-checked rather than assumed: raising the limits to 21 and 9 respectively makes each case classify as route_unreachable, so the tests fail if the bound stops being enforced. The tests reference the constants by name instead of repeating 20 and 8, so a future change to either value cannot silently desynchronize them.

npx vitest run --project integration test/non-interactive-error-classification.test.ts → 13 passed.

On the five terminology decisions (serialized position, exception-type fallback, checkpoint-text classifier, closed vocabulary, structural evidence): all five already appear in the hyphenation and word forms the advisor recommends. I have not added them to .agents/skills/_shared/controlled-words.md, since editing the shared word list is a cross-cutting governance change beyond this fix — happy to do it here or separately if a maintainer wants the entries.

cv and others added 2 commits August 4, 2026 05:13
Decode only the root exception class from pinned MessagePack error rows.

Treat nested payload data, unsupported shapes, and malformed values as unclassified.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
`codebase-growth-guardrails` fails: the MessagePack string header helper
added three `if` statements to a changed test file, and the policy
requires test files not to add any.

Replace the branch chain with a narrowest-first lookup over the fixstr,
str8, str16, and str32 headers. The encoding is unchanged; the scanner
in tools/growth-guardrails/test-conditionals.mts now reports zero `if`
statements for this file.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

@cvillela thanks for 2427d59 — decoding the root exception class from the pinned MessagePack row is the right boundary. My version matched the persisted value as text, which was wrong at the root: writes.value is a msgpack BLOB, so substr(value, 1, 4096) plus regexes was pattern-matching a binary encoding and could only ever approximate the structure. Reading the root class and treating nested payload, unsupported shapes, and malformed values as unclassified removes the whole misattribution surface my serialized-position rule was trying to approximate.

That commit left codebase-growth-guardrails red, so I fixed it in 6dec122:

FAIL: changed test files add if statements.
Changed test files contain 3 if statement(s) at PR head vs 0 at base.
- test/non-interactive-error-classification.test.ts: 3 if statement(s), up from 0

The three came from the messagePackStringHex header branch chain. I replaced it with a narrowest-first lookup over the fixstr, str8, str16, and str32 headers — same encoding, no behavior change. Verified with the policy's own scanner rather than by eye:

scanTextForTestConditionals(...) -> if statements found by scanner: 0

npx vitest run --project integration test/non-interactive-error-classification.test.ts → 15 passed on your revision, and still 15 after the fixture change.

One note in case it matters for your revision: the three tests I added in 17ac52f covered the late-checkpoint-row race and the two scanning limits (_NEMOCLAW_PERSISTED_ERROR_ROW_LIMIT, _NEMOCLAW_EXCEPTION_CHAIN_LIMIT), which the advisor had asked for. If either constant survives in the decoded design, those boundaries are worth keeping covered.

@wscurran wscurran added bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior labels Aug 4, 2026
@apurvvkumaria apurvvkumaria self-assigned this Aug 4, 2026
jyaunches
jyaunches previously approved these changes Aug 4, 2026
@jyaunches
jyaunches dismissed their stale review August 4, 2026 15:49

I think we do need a changes request

@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

E2E / PR Gate failed on the ubuntu-repo-cloud-langchain-deepagents-code target. Triaged as infrastructure, not this change, and reran the failed job (attempt 2).

Failure point:

test/e2e/e2e-cloud-experimental/checks/04-deepagents-code-fresh-reonboard.sh
04-deepagents-code-fresh-reonboard: FAIL: nemoclaw status failed after re-onboard: {
    "detail": "Inference gateway unreachable on https://inference.local/v1/models from inside the sandbox. DNS may ha...",
    "failureLabel": "unreachable",

Why it is not attributable to this PR:

  • The check exercises nemoclaw onboard --fresh --non-interactive and then nemoclaw status. The --non-interactive there is onboard's flag, not dcode -n; the check asserts identity and config parity after a fresh re-onboard.
  • The failing assertion is sandbox-internal reachability of inference.local — DNS and gateway routing. This PR changes only how an already-failed managed non-interactive run is classified for diagnostics, which runs after an inference call has failed and cannot influence whether the gateway resolves.
  • Nothing in the diff touches onboarding, DNS, policy, or gateway startup.
  • failureLabel: unreachable with the DNS hint is the signature of a not-yet-ready cloud instance rather than a deterministic failure.

If attempt 2 reproduces it, I will stop treating it as a flake and dig into the target rather than rerun again.

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

This E2E / PR Gate failure is a different one from the earlier 04-deepagents-code-fresh-reonboard case, and it is not an E2E failure at all: the gate reports PR #8206 CI did not pass … so no E2E run was dispatched, with cli-test-shards (7) as the prerequisite that failed.

Root cause of that shard, from the job log:

AssertionError: promise rejected "Error: Refusing cleanup because PID-file …" instead of resolving
Caused by: Error: Refusing cleanup because PID-file process 4933 does not prove ownership of gateway 'nemoclaw'
 ❯ stopOwnedGatewayPid test/e2e/live/messaging-compatible-endpoint-helpers.ts:170:13
 ❯ cleanupOwnedGatewayRuntimeStrict test/e2e/live/messaging-compatible-endpoint-helpers.ts:197:9
 ❯ test/e2e/support/messaging-compatible-endpoint-helpers.test.ts:85:11

The test spawns a child, writes its PID to openshell-gateway.pid, and expects cleanupOwnedGatewayRuntimeStrict to resolve. inspectGatewayPid proves ownership by reading the live process snapshot and matching hostGatewayCmdlineMatches; when that match fails it returns unverified, and strict mode throws. So the assertion depends on a real /proc read of a just-spawned child agreeing with the expected gateway cmdline — a timing-sensitive check against live process state.

Not attributable to this PR: the changed files are

agents/langchain-deepagents-code/patch-managed-deepagents-code.py
docs/manage-sandboxes/run-deep-agents-code.mdx
test/e2e/e2e-cloud-experimental/checks/04-deepagents-code-fresh-reonboard.sh
test/e2e/support/platform-parity-cloud-experimental.test.ts
test/helpers/langchain-deepagents-code-patch-fixture.ts
test/non-interactive-error-classification.test.ts

Neither test/e2e/live/messaging-compatible-endpoint-helpers.ts nor test/e2e/support/messaging-compatible-endpoint-helpers.test.ts is touched here, and shard 7 passed on earlier revisions of this same branch.

checks is already green on the current revision and shard 7 is re-running, so the gate should re-dispatch once the prerequisite CI passes. I am not rerunning anything by hand this time — flagging it in case the gateway-PID ownership assertion is a known intermittent on CI runners, since it reads live /proc state for a process the test itself just spawned.

@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

@JulieYaunches the Terra advisor lane flagged one warning, and it is about your readiness retry rather than the classifier: Document the source and lifetime of the readiness retry at test/e2e/e2e-cloud-experimental/checks/04-deepagents-code-fresh-reonboard.sh:72. It wants the repo's usual workaround boundary — which component creates the early-Ready state, why it cannot be corrected at the source here, and when the retry can be removed. (0 blockers; the job itself died on canonical review receipt mismatch.)

I checked the two sides rather than guess, in case it saves you the lookup:

  • Ready is published by OpenShell's sandbox lifecycle — this check reads it via the list output at line 275.
  • The probe is NemoClaw's own probeSandboxInferenceGatewayHealth in src/lib/actions/sandbox/inference-route-health.ts, which runs https://inference.local/v1/models from inside the sandbox through openshell exec and documents that it reports the route state at the instant it runs.

So the two are genuinely different owners, which is exactly what the warning asks you to state. A drop-in block if you want it:

# Invalid state: OpenShell publishes the recreated sandbox as Ready before the
#   in-sandbox inference route accepts probes, so the first status after a fresh
#   re-onboard can report failureLabel=unreachable for a sandbox that becomes
#   healthy moments later.
# Source boundary: readiness is published by OpenShell's sandbox lifecycle and
#   only consumed here. The probe is NemoClaw's
#   probeSandboxInferenceGatewayHealth in
#   src/lib/actions/sandbox/inference-route-health.ts, which reports the route
#   state at the instant it runs and must not wait.
# Source-fix constraint: NemoClaw cannot make OpenShell delay Ready until the
#   route serves, and making status retry internally would turn a point-in-time
#   report into a wait, hiding real outages from every other caller. The retry
#   belongs to this check, the only consumer that knows a re-onboard just
#   happened.
# Regression: test/e2e/support/platform-parity-cloud-experimental.test.ts covers
#   eventual status success and retry exhaustion.
# Removal condition: delete this retry once OpenShell publishes Ready only after
#   the in-sandbox inference route serves, or once NemoClaw exposes an explicit
#   readiness-wait command this check can call instead.

Say the word and I will push it; I did not want to edit your commit's comment unprompted, and we have already collided on this branch a few times.

@github-actions github-actions Bot added v0.0.103 Release target and removed v0.0.102 labels Aug 4, 2026
apurvvkumaria and others added 2 commits August 4, 2026 16:05
Both advisor lanes raised the same warning on the post-re-onboard status
retry: the check does not name the component that publishes Ready before
the in-sandbox inference route accepts probes, why that ordering cannot
be corrected here, or when the retry can be removed.

Record the boundary. Readiness is published by OpenShell's sandbox
lifecycle and only consumed here, while the probe is NemoClaw's
probeSandboxInferenceGatewayHealth, which reports the route state at the
instant it runs and must not wait. NemoClaw cannot delay OpenShell's
Ready, and making status retry internally would turn a point-in-time
report into a wait that hides real outages from every caller, so the
retry belongs to the one consumer that knows a re-onboard just happened.

Comment-only; shellcheck and bash -n pass.

Signed-off-by: Tony Luo <xialuo@nvidia.com>
@TonyLuo-NV

Copy link
Copy Markdown
Collaborator Author

Pushed the readiness-retry boundary block in 09d4374. Both advisor lanes raised the same warning independently on the latest revision (Document or correct the readiness race at its source, 04-deepagents-code-fresh-reonboard.sh:77), so it was not going to clear on its own. @JulieYaunches @ApurvKumaria this edits the comment above your retry only — revert or reword freely if you read the ownership differently.

It records what I verified earlier rather than an assumption:

  • Invalid state — OpenShell publishes the recreated sandbox as Ready before the in-sandbox inference route accepts probes, so the first status after a fresh re-onboard can report failureLabel=unreachable for a sandbox that is healthy moments later.
  • Source boundary — readiness is published by OpenShell's sandbox lifecycle and only consumed here; the probe is NemoClaw's probeSandboxInferenceGatewayHealth, which documents that it reports the route state at the instant it runs.
  • Source-fix constraint — NemoClaw cannot delay OpenShell's Ready, and making status retry internally would turn a point-in-time report into a wait that hides real outages from every other caller.
  • Regressiontest/e2e/support/platform-parity-cloud-experimental.test.ts covers eventual success and retry exhaustion.
  • Removal condition — drop the retry once OpenShell publishes Ready only after the route serves, or once NemoClaw exposes an explicit readiness-wait command.

Comment-only change; shellcheck and bash -n pass.

On the advisor's stronger option ("identify and correct the component"): correcting the ordering means changing when OpenShell publishes Ready, which is outside this repo, so documenting the constraint is the available path here. If a maintainer wants the source-level readiness contract pursued, that is an OpenShell-side change and worth its own issue rather than this PR.

Both advisor jobs died on 429 status code from their own SDK this round, with 0 blockers in the preserved ledger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior v0.0.103 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants