Skip to content

nodespace-types: surface SchemaNode relationship-parse failures via eprintln (closes #2130) - #2131

Merged
mstomar125 merged 1 commit into
mainfrom
worktree-issue-2130-relationships-swallow
Aug 18, 2026
Merged

mstomar125 merged 1 commit into
mainfrom
worktree-issue-2130-relationships-swallow

Conversation

@mstomar125

Copy link
Copy Markdown
Contributor

Closes #2130

What changed

Mirrors PR #2128's fix for the sibling fields field, applied to relationships:

  • packages/nodespace-types/src/schema.rs: extracted the relationships JSON parse into a new private parse_relationships(properties, node_id) -> (Vec<SchemaRelationship>, Option<String>), directly mirroring parse_fields. On a genuine parse failure it still returns an empty Vec (unchanged behavior), but now also returns a diagnostic message naming the schema node id and the underlying serde_json error. from_node prints that message via eprintln!, same as it already does for fields.
  • packages/nodespace-types/src/convert.rs: added schema_with_malformed_relationships_does_not_fail_an_unrelated_batch_read, mirroring the existing schema_with_malformed_fields_does_not_fail_an_unrelated_batch_read — pins that a malformed relationships value on one schema node in a nodes_to_typed_values batch does not fail the other, unrelated nodes in the same batch.

Why the same reasoning as #2128 applies unchanged

from_node's Result is deliberately left untouched here too. node_to_typed_value (reachable from every entry point via nodes_to_typed_values, which .collect()s a Vec<Result<_, _>> into a single Result<Vec<_>, _> across ~10 batch-read call sites in core/desktop-app/daemon) and the two direct schemas.rs callers are exactly the same three callers #2128 analyzed — nothing about the caller graph changes for relationships. Turning a relationships parse failure into an Err would carry the identical blast-radius risk (one bad schema node failing an entire unrelated batch read) that #2128 rejected Approach 1 for. Fixing the diagnostic inside from_node itself, the same way, covers all three callers uniformly without touching any signatures or nodes_to_typed_values's batch-collect semantics.

Approach 3 (downstream guard) is rejected for the same reason as before: it wouldn't catch a schema going bad mid-session, only at dev/CI/boot time.

No dependency was added to nodespace-types — same minimal-dependency constraint as #2128, unaffected here since this reuses eprintln! the same way.

Known limitation (unchanged, not re-litigated here): the Windows stderr-visibility gap in the eprintln! approach that #2128 flagged and filed as #2129 applies identically to this diagnostic — it's the same underlying stdio-routing issue, out of scope for this PR (that's #2129's scope, in daemon_setup.rs/main.rs, not nodespace-types).

Testing

  • New unit tests in packages/nodespace-types/src/schema.rs, mirroring nodespace-types: surface SchemaNode field-parse failures via eprintln (closes #2107) #2128's fields coverage exactly:
    • test_parse_relationships_malformed_json_surfaces_diagnostic — malformed relationships JSON (a non-string direction) now produces a diagnostic naming the schema id and the property.
    • test_parse_relationships_valid_json_no_diagnostic / test_parse_relationships_absent_key_no_diagnostic — no false-positive diagnostic on a clean parse or an absent key.
    • test_from_node_malformed_relationships_still_succeeds_with_empty_relationships — end-to-end through from_node: still Ok with an empty relationships Vec (unchanged), diagnostic printed along the way. Verified manually with cargo test -p nodespace-types test_from_node_malformed_relationships_still_succeeds_with_empty_relationships -- --nocapture, which prints:
      nodespace-types: SchemaNode::from_node: failed to parse `relationships` for schema node `<id>`: invalid type: integer `42`, expected string or map — reading back as an empty relationship list. Likely a stale/corrupted storage format.
      
  • New regression test in packages/nodespace-types/src/convert.rs: schema_with_malformed_relationships_does_not_fail_an_unrelated_batch_read.
  • cargo test -p nodespace-types: 55 passed, 0 failed (up from 50 on main, +5 new tests).
  • cargo test -p nodespace-core --lib: 1154 passed, 0 failed.
  • cargo test --workspace --lib --bins: all green (full workspace, including daemon/cli/nlp-engine/agent).
  • cargo clippy -p nodespace-types -p nodespace-core -p nodespace-daemon -p nodespace-cli -p nodespace-app --all-targets -- -D warnings: clean.
  • cargo fmt -p nodespace-types -- --check: clean.
  • Local pre-push gate (full test:all, frontend + Rust workspace) passed before this push.

Acceptance criteria

  • A parse failure in nodespace_types::SchemaNode::from_node's relationships extraction is now surfaced via eprintln! (node id + underlying serde_json error) instead of producing an empty Vec with zero signal, mirroring the fields fix.

…println (#2130)

SchemaNode::from_node's `relationships` extraction had the same
silent-swallow-to-empty-Vec shape that #2107/#2128 just fixed for the
sibling `fields` extraction two lines above it, with zero diagnostic
signal on a genuine serde_json parse failure.

Extracted the parse into a parse_relationships helper mirroring
parse_fields: returns the relationship list alongside an optional
diagnostic message, printed via eprintln! in from_node. Same reasoning
as the fields fix applies unchanged: from_node's Result is left
untouched (a diagnostic-only fix, not a new error path) because
node_to_typed_value feeds nodes_to_typed_values, which collects a
Vec<Result<_, _>> into a single Result<Vec<_>, _> across ~10 batch-read
call sites in core/desktop-app/daemon -- turning this into an Err
would fail an entire unrelated batch over one bad schema node. A
regression test (mirroring schema_with_malformed_fields_does_not_fail_
an_unrelated_batch_read) pins that a malformed relationships value
does not zero out an unrelated batch either.

Closes #2130

Co-Authored-By: Claude <noreply@anthropic.com>
@mstomar125

Copy link
Copy Markdown
Contributor Author

Self-verification before merge (GitHub blocks self-review-approval on own PRs, so recording here per standing merge policy):

  • Only packages/nodespace-types/src/schema.rs and packages/nodespace-types/src/convert.rs touched.
  • cargo test -p nodespace-types: 55/55 pass (50 on main + 5 new).
  • cargo test -p nodespace-core --lib: 1154/1154 pass.
  • cargo test --workspace --lib --bins: all green, full workspace.
  • cargo clippy -p nodespace-types -p nodespace-core -p nodespace-daemon -p nodespace-cli -p nodespace-app --all-targets -- -D warnings: clean.
  • cargo fmt -p nodespace-types -- --check: clean.
  • Local pre-push gate (full test:all, frontend + Rust workspace) passed before push.

Independent adversarial review sub-agent (separate context, told to look for real defects) verified: byte-for-byte structural parity between parse_relationships/parse_fields, confirmed the malformed-JSON test fixtures ("direction": 42) are genuine parse failures since direction/cardinality are non-Option fields on SchemaRelationship, confirmed unchanged behavior for valid/absent relationships data, confirmed scope discipline, confirmed doc-comment accuracy. No BLOCKER/MAJOR/MINOR/NIT findings — it independently ran the test suite/clippy/fmt on the PR branch and got matching results.

Merging.

@mstomar125
mstomar125 merged commit 722e1cd into main Aug 18, 2026
@mstomar125
mstomar125 deleted the worktree-issue-2130-relationships-swallow branch August 18, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nodespace-types::SchemaNode::from_node's relationships extraction has the same silent-swallow shape fields did

1 participant