Skip to content

feat(chooser): add bounded table readback and validation - #129

Open
kunkunGames wants to merge 1 commit into
tumourlove:masterfrom
kunkunGames:agent/chooser-bounded-readback
Open

feat(chooser): add bounded table readback and validation#129
kunkunGames wants to merge 1 commit into
tumourlove:masterfrom
kunkunGames:agent/chooser-bounded-readback

Conversation

@kunkunGames

@kunkunGames kunkunGames commented Aug 4, 2026

Copy link
Copy Markdown

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 chooser actions 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:

Action Purpose
list_chooser_tables Stable AssetRegistry discovery with bounded pagination
get_chooser_table Counts, bounded columns/references, fallback data, and optional row readback
list_chooser_columns Column type, role, disabled state, and reflected row-value metadata
list_chooser_rows Bounded row pages with explicit partial-cell reporting
list_chooser_references Stable hard/soft reference pages with exact existence evidence
validate_chooser_table Non-mutating row alignment, result-target, and reference validation

The 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_table is intentionally distinct from validate_chooser: the new action is strictly read-only, while the existing action performs a compile-oriented validation pass.

Verification

  • UE 5.7 RunUAT BuildPlugin -NoTargetPlatforms -Rocket: 436/436 actions passed; UAT exit code 0
  • UE 5.8 RunUAT BuildPlugin -NoTargetPlatforms -Rocket: 436/436 actions passed; UAT exit code 0
  • UE 5.7 Chooser-enabled Editor target compiled and linked successfully
  • UE 5.8 Chooser-enabled Editor target compiled and linked successfully
  • namespace-policy follow-up rebuilt the implementation and tests independently, then linked successfully on UE 5.7 and 5.8
  • UE 5.7 Automation RunTests Monolith.Chooser.Read: 6/6 passed, zero failures/skips, exit code 0
  • UE 5.8 Automation RunTests Monolith.Chooser.Read: 6/6 passed, zero failures/skips, exit code 0
  • new source and tests contain no anonymous namespaces, using namespace directives, or individual using declarations; every namespaced symbol is fully qualified at its use site
  • git diff --check passed
  • detailed evidence: Docs/testing/2026-08-04-chooser-read-validation-actions.md

Compatibility and risk

All 16 chooser schemas 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 AssetRegistry dependency 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.

@kunkunGames
kunkunGames force-pushed the agent/chooser-bounded-readback branch from 9e5c087 to 79bd176 Compare August 4, 2026 18:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +622 to +623
return MakeShared<FJsonValueNumber>(
NumericProperty->GetFloatingPointPropertyValue(Value));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +1430 to +1433
for (TFieldIterator<FProperty> It(
Struct,
EFieldIteratorFlags::IncludeSuper,
EFieldIteratorFlags::ExcludeDeprecated);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

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.

1 participant