fix(tree): validate tree index key selection - #28233
Craig Macomber (Microsoft) (CraigMacomber) wants to merge 11 commits into
Conversation
Interpret Simple Tree index selectors as property keys, validate selected fields, refine invalidation scopes, and make identifier indexing deterministic.
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (962 lines, 15 files), I've queued these reviewers:
How this works
|
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate findings remain around broken-index reads and cursor cleanup.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR updates Simple Tree indexing to validate property-key selectors, translate stored keys, and improve failure handling.
Changes:
- Added eager key validation and property-to-stored-key translation.
- Improved dependency scopes, breaker integration, and identifier indexing.
- Expanded tests, examples, exports, and changesets.
File summaries
| File | Description |
|---|---|
packages/dds/tree/src/test/simple-tree/simpleTreeIndex.spec.ts |
Key validation and failure regression coverage |
packages/dds/tree/src/test/simple-tree/identifierIndex.spec.ts |
Identifier regression coverage |
packages/dds/tree/src/test/feature-libraries/indexing/treeIndex.spec.ts |
Index construction and breaker coverage |
packages/dds/tree/src/simple-tree/api/simpleTreeIndex.ts |
Selector validation and index construction |
packages/dds/tree/src/simple-tree/api/identifierIndex.ts |
Identifier discovery and immutable indexing |
packages/dds/tree/src/feature-libraries/indexing/index.ts |
Indexing exports |
packages/dds/tree/src/feature-libraries/indexing/anchorTreeIndex.ts |
Dependency scopes and failure handling |
packages/dds/tree/src/feature-libraries/index.ts |
Feature-library exports |
packages/dds/tree/src/entrypoints/legacy.ts |
Export ordering |
packages/dds/tree/src/entrypoints/beta.ts |
Export ordering |
packages/dds/tree/src/entrypoints/alpha.ts |
Export ordering |
packages/dds/tree/src/core/forest/forest.ts |
Forest breaker contract |
packages/dds/tree/src/core/forest/editableForest.ts |
Breaker contract updates |
.changeset/clear-index-property-keys.md |
Property-key behavior changeset |
.changeset/calm-identifiers-index.md |
Identifier behavior changeset |
Review details
Suppressed comments (4)
packages/dds/tree/src/feature-libraries/indexing/anchorTreeIndex.ts:129
- If
indexFieldthrows while indexing an existing node,forest.breaker.runrethrows before thiscursor.free()executes. The forest contract requires outstanding cursors to be freed or cleared when invalidating, so a failed index construction leaves this subscription cursor live on the broken forest. Move the cleanup into afinallyblock.
this.indexField(cursor);
});
cursor.free();
});
packages/dds/tree/src/feature-libraries/indexing/anchorTreeIndex.ts:159
- When a newly created node has a value rejected by
isKeyValid,this.indexFieldthrows insideforest.breaker.runand the followingdetachedCursor.free()is skipped. That leaves an outstanding cursor after the forest is invalidated; usetry/finallyaround the indexing work so update failures do not leak cursor state.
this.indexField(detachedCursor);
detachedCursor.free();
}),
packages/dds/tree/src/feature-libraries/indexing/anchorTreeIndex.ts:352
- Both
indexFieldandindexSpinecan throw when an update produces an invalid key. In that path thiscursor.clear()is skipped, leaving a current subscription cursor whileforest.breaker.runmarks the forest broken. Put cursor cleanup in afinallyblock to preserve the forest's invalidation contract.
this.indexField(cursor);
if (this.keyFinderDependencyScope === KeyFinderDependencyScope.Subtree) {
this.indexSpine(cursor);
}
cursor.clear();
packages/dds/tree/src/test/simple-tree/simpleTreeIndex.spec.ts:404
- This test verifies the construction error but not the new failure-isolation contract: initial indexing is now run through
forest.breaker.run, so a rejected existing value should leave the checkout broken and preserve the original error as its cause. Add a follow-up assertion that using the view after thisassert.throwsfails with aUsageErrorwhose cause is the construction error, matching the update-failure regression test.
assert.throws(
() =>
createTreeIndex(
view,
(schema) => (schema === NumericName ? "name" : undefined),
(nodes) => nodes,
isStringKey,
),
(error: Error) =>
error instanceof UsageError &&
error.message.includes('The value in key field "name" selected for schema') &&
error.message.endsWith("was rejected by isKeyValid."),
);
- Files reviewed: 15/15 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com>
Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com>
Bundle size comparisonBase commit: Pending — |
Bundle size comparisonBase commit: Notable changes
Per-bundle deltas
|
Description
Clarifies and validates
createTreeIndexkey selection so selectors use Simple Tree property keys, which are translated to stored keys internally.Selected fields are validated eagerly as required, single-valued leaf fields before the index subscribes to forest updates. Index construction or update failures now break the forest and preserve the original error, preventing continued use of an apparently valid checkout.
This also adds explicit key-finder dependency scopes, uses immutable invalidation for identifier indexes, skips schemas with ambiguous identifier fields, and expands executable examples and regression coverage.
Breaking Changes
createTreeIndexwhen indexing over a field which had an explicit stored key now will interpret the provided keys as property keys, consistent with out other APIs. Code which worked around this bug by specifying the stored key will need to be updated.createTreeIndexwhen indexing nodes with multiple identifier fields will no longer index them, instead of picking whatever identifier field comes first.createTreeIndexin cases where valid in schema data could cause it to crash later when updating the index now give proper usages errors when constructing the index, so in the edge cases where such invalid indexes simply happened to not error (for example due to the tree always having none of the type being indexes), the code making them may need to be updated.Reviewer Guidance
The review process is outlined in the pull request guidelines.