feat(chooser): add bounded table readback and validation - #129
feat(chooser): add bounded table readback and validation#129kunkunGames wants to merge 1 commit into
Conversation
9e5c087 to
79bd176
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79bd176f91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| TEXT("column_type"), | ||
| Column->GetScriptStruct() ? Column->GetScriptStruct()->GetName() : FString()); | ||
| Cell->SetBoolField(TEXT("is_output"), IsOutputColumn(*Column)); | ||
| Cell->SetField(TEXT("value"), GetRowValueAt(*Column, RowIndex)); |
There was a problem hiding this comment.
Enforce an aggregate serialization budget
When a table contains many rows/columns or a custom cell with nested containers, this call recursively serializes every cell without sharing a response-wide visit or byte budget. The local limits still multiply (up to 500 rows × 512 columns, while nested fields and containers can fan out by 128 and 256 at each level), so a valid read request can construct millions of JSON values, stall the editor, or exhaust memory despite the advertised bounded behavior. Thread an aggregate budget through row and property serialization and report when it is exhausted.
Useful? React with 👍 / 👎.
| return MakeShared<FJsonValueNumber>( | ||
| NumericProperty->GetFloatingPointPropertyValue(Value)); |
There was a problem hiding this comment.
Encode non-finite reflected floats safely
If any reflected chooser field contains NaN or positive/negative infinity, this creates an FJsonValueNumber that cannot be represented as a valid JSON number, causing the eventual RPC response serialization to fail or emit an invalid payload. Existing or custom chooser column types can contain arbitrary floating-point values, so check FMath::IsFinite here and emit a string or explicit serialization marker for non-finite values.
Useful? React with 👍 / 👎.
| false, | ||
| true); | ||
| } | ||
| return; |
There was a problem hiding this comment.
Traverse embedded nested chooser tables
When a root table owns embedded child UChooserTable objects through NestedObjects, this branch records each child as a hard reference and immediately returns, so the child's result rows, output columns, and soft references are never scanned. Embedded children cannot be passed separately because the new path contract rejects subobject paths, which means validate_chooser_table can report complete=true and valid=true even when a nested child contains an unresolved result asset. Recurse specifically into embedded chooser objects with a visited set and the existing scan budgets.
Useful? React with 👍 / 👎.
| { | ||
| TSharedPtr<FJsonObject> Row = MakeShared<FJsonObject>(); | ||
| Row->SetNumberField(TEXT("row_index"), RowIndex); | ||
| Row->SetBoolField(TEXT("disabled"), GetBoolArrayValue(Chooser, TEXT("DisabledRows"), RowIndex)); |
There was a problem hiding this comment.
Preserve an unknown disabled state for missing entries
When DisabledRows is absent, has the wrong reflected type, or is shorter than ResultsStructs, GetBoolArrayValue returns false, and this line consequently reports the affected row as explicitly enabled. Those malformed alignments are inputs this preflight surface is intended to expose, and although the separate validator reports a count mismatch, list_chooser_rows itself returns a misleading row state. Emit null or an availability flag when the entry cannot be read instead of conflating it with a real false value.
Useful? React with 👍 / 👎.
| for (TFieldIterator<FProperty> It( | ||
| Struct, | ||
| EFieldIteratorFlags::IncludeSuper, | ||
| EFieldIteratorFlags::ExcludeDeprecated); |
There was a problem hiding this comment.
Exclude stale cooked results from editor validation
When editor-side ResultsStructs is present, this unrestricted property walk also descends into CookedResults, even though the implementation elsewhere explicitly treats that array as stale derived data. After a non-compiling authoring edit, an old soft reference retained only in CookedResults can therefore make validate_chooser_table report an unresolved reference for an otherwise valid source table; a compiled table with many rows can also count every result twice under different source paths and hit the 4,096-reference cap prematurely. Scan only the authoritative editor result data when it exists, falling back to cooked results only when the editor array is unavailable.
Useful? React with 👍 / 👎.
Summary
Add a cohesive, read-only preflight surface for Chooser Tables: discovery, bounded readback, reference inspection, and structural validation without compiling or mutating assets.
Problem
The existing
chooseractions support deep inspection and authoring, but automation has no small, predictable contract for inventory and preflight. Callers must either load large reflected structures, invoke compile-oriented validation, or reconstruct row/reference checks themselves. That makes batch audits expensive and makes incomplete traversal difficult to distinguish from a valid result.Solution
Add six actions backed by one reflection serializer, one reference walker, and one structural validator:
list_chooser_tablesget_chooser_tablelist_chooser_columnslist_chooser_rowslist_chooser_referencesvalidate_chooser_tableThe implementation requires canonical mounted package or top-level object paths and rejects aliases rather than silently normalizing them. Independent table, row, column, reference, depth, field, container, string, and global-visit limits make every response finite. Any cutoff is reported through explicit truncation or completeness metadata.
validate_chooser_tableis intentionally distinct fromvalidate_chooser: the new action is strictly read-only, while the existing action performs a compile-oriented validation pass.Verification
RunUAT BuildPlugin -NoTargetPlatforms -Rocket: 436/436 actions passed; UAT exit code 0RunUAT BuildPlugin -NoTargetPlatforms -Rocket: 436/436 actions passed; UAT exit code 0Automation RunTests Monolith.Chooser.Read: 6/6 passed, zero failures/skips, exit code 0Automation RunTests Monolith.Chooser.Read: 6/6 passed, zero failures/skips, exit code 0using namespacedirectives, or individualusingdeclarations; every namespaced symbol is fully qualified at its use sitegit diff --checkpassedDocs/testing/2026-08-04-chooser-read-validation-actions.mdCompatibility and risk
All 16
chooserschemas remain discoverable when the optional Chooser plugin is disabled. Asset-backed handlers then return an explicit availability error; registry-only discovery can still return metadata, and no substitute data is synthesized.The six actions never transact, compile, save, mutate, or dirty a Chooser package. The only new module dependency is the direct
AssetRegistrydependency used for discovery and exact on-disk object evidence.Visual evidence
Not applicable: this PR adds headless action handlers, schemas, automation tests, documentation, and routing guidance with no visual or editor-facing UI change.