Repository navigation
Propagate DB errors from resolve_reverse_segment instead of silently degrading - #2926
Merged
Merged
Conversation
…degrading resolve_reverse_segment had two silent-degradation points: a resolve_type_chain error collapsed the ancestor chain to just the concrete type, and a get_inbound_relationships error on any scope silently skipped that candidate via `continue`. Either failure made a legitimate inherited reverse relationship (e.g. node.blocked_by declared on an ancestor) resolve to None indistinguishable from a genuine typo, and the caller reported it as BrokenPath — blaming the play's own expression for a transient DB error. Changes resolve_reverse_segment to return Result<Option<String>, PlayValidationError>: Ok(Some(source_type)) on a match, Ok(None) for a genuine miss, and Err(SchemaResolutionFailed) for a DB error on either call, mirroring the SchemaResolutionFailed pattern validate_schema_path already uses for its own forward-resolution tokio::try_join! failure. Updates the one call site in validate_schema_path to push the propagated error and stop, rather than falling through to BrokenPath. Closes #2885. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The check-code-references gate flagged a "core#NNNN"-style reference in a new test's doc comment, added in the previous commit. Reword to describe the behavior directly per CLAUDE.md's documentation standard. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
Author
/pragmatic-code-review (MEDIUM effort) — resultRan a review covering all 8 finder angles (line-by-line scan, removed-behavior audit, cross-file tracer, reuse, simplification, efficiency, altitude, conventions) plus an independent verification pass checking:
Result: zero findings. No recommendations to address. Re-Review DecisionDecision: NO RE-REVIEW NEEDED Rationale: The review returned zero findings after full-angle coverage plus independent verification; no code changes were made in response (there was nothing to change). The PR is unchanged since this review ran. The PR is ready for merge (pending final approval). |
mstomar125
added a commit
that referenced
this pull request
Sep 25, 2026
…ormat! allocation - Rebased onto main (PR #2926 for issue #2885 merged first, changing resolve_reverse_segment's signature); resolved the one conflicting signature line, keeping #2926's Result-returning behavior with this PR's SchemaCache type. Verified with a full test run post-rebase. - The trigger node_type check built a `format!` location string on every rule iteration, even when the schema resolved successfully. Checks cache membership first now, so the allocation (and the cached_schema call that needs it) only runs on the error path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6 tasks done
mstomar125
added a commit
that referenced
this pull request
Sep 25, 2026
…ion (#2928) * Distinguish schema lookup failure from missing schema in play validation ensure_schema_cached collapsed a DB/lookup error into the same cache entry as "no schema for this type", so every consumer reported a transient failure as UnknownNodeType — wrongly blaming the Play author's type reference. The schema cache is now a tri-state (Result<Option<SchemaNode>, String>) so a real lookup failure surfaces as SchemaResolutionFailed instead, via a new cached_schema helper used at all four call sites (trigger check, update_node, create_node, relationship action). validate_schema_path also silently returned with no error when a deeper-hop schema (a relationship's target_type reached partway through a condition path) was missing — the "already reported by trigger validation" reasoning only holds for the first hop. A Play could save with an unvalidated tail segment. Now reports UnknownNodeType/SchemaResolutionFailed for any hop past the first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Address review: rebase onto merged sibling fix, avoid unconditional format! allocation - Rebased onto main (PR #2926 for issue #2885 merged first, changing resolve_reverse_segment's signature); resolved the one conflicting signature line, keeping #2926's Result-returning behavior with this PR's SchemaCache type. Verified with a full test run post-rebase. - The trigger node_type check built a `format!` location string on every rule iteration, even when the schema resolved successfully. Checks cache membership first now, so the allocation (and the cached_schema call that needs it) only runs on the error path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resolve_reverse_segment(packages/core/src/playbook/validation.rs) had two silent-degradation points on a DB error:resolve_type_chain(...).unwrap_or_else(|_| vec![node_type])collapsed the ancestor chain to just the concrete type.get_inbound_relationships'sErrcase didcontinue, silently skipping that candidate scope.Either failure made a legitimate inherited reverse relationship name (e.g.
node.blocked_bydeclared on an ancestor, used on a subtype instance) resolve toNone, indistinguishable from a genuine typo. The caller then always reported this asBrokenPath, blaming the play's own expression for what could be a transient DB error.Changes
resolve_reverse_segmentnow returnsResult<Option<String>, PlayValidationError>instead ofOption<String>:Ok(Some(source_type))— a declared reverse name found (unchanged success behavior).Ok(None)— a genuine miss (segment declared nowhere in the chain).Err(PlayValidationError::SchemaResolutionFailed)— a DB error resolving theextendschain or reading a scope's inbound relationships, now propagated instead of silently degrading.SchemaResolutionFailedpatternvalidate_schema_pathalready uses for its own forward-resolutiontokio::try_join!failure a few lines above — same file, same established convention, not a new mechanism.validate_schema_pathto match on the three outcomes: continue traversal onOk(Some), fall through to the existingBrokenPathreporting onOk(None), and push the propagated error + stop onErr(same as the forward-resolution failure handling immediately above it).resolve_reverse_segment's newResultcontract (found-via-ancestor-chain →Ok(Some(..)), genuinely-undeclared →Ok(None)), and one locks in that aSchemaResolutionFailedfrom this call site is structurally distinguishable fromBrokenPath(differentkind(), correctly flagged as inconclusive viais_genuine_failure()).Diff is scoped to only this function, its one call site, and new tests — no reformatting or changes elsewhere in the file (a sibling PR is concurrently touching different functions in the same file).
Test plan
cargo test -p nodespace-core --lib playbook::validation— 98 passed, including the 3 new testscargo test -p nodespace-core --lib playbook::— 462 passed (full playbook module, no regressions)bun run quality:fix— clean (cargo clippy --all-targets -- -D warnings -D clippy::unused-asynczero warnings)test:all+cargo build --bin nodespaced+test:e2e) passed in full on pushCloses #2885.
🤖 Generated with Claude Code