Skip to content

chore: migrate to new column-access mechanism (SDK 1.81.x) - #167

Merged
AStaroverov merged 7 commits into
mainfrom
chore/migrate-column-access-mechanism
Aug 19, 2026
Merged

chore: migrate to new column-access mechanism (SDK 1.81.x)#167
AStaroverov merged 7 commits into
mainfrom
chore/migrate-column-access-mechanism

Conversation

@AStaroverov

@AStaroverov AStaroverov commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

What

Update @platforma-sdk to 1.80.x and migrate the block model off the removed column-access APIs onto the new host-driven ColumnsCollection / ColumnRecipe mechanism.

Why

ColumnCollectionBuilder / ColumnMatch / ArrayColumnProvider were removed in the SDK. The new API discovers and filters columns host-side and fetches specs on demand (getSpec()) instead of pulling every upstream spec eagerly into the 8 MB model sandbox.

Key changes (model)

  • ColumnCollectionBuilder + findColumnsColumnsCollection(["result_pool"]).discover(...); ColumnMatchColumnRecipe (.column.spec/.column.id.getSpec()/.id).
  • resultPool.getPColumnSpecByRef(ref)Column(ref).getSpec().
  • Filtering pushed host-side into include/exclude selectors wherever expressible (linkers, label, cluster-id names, per-sample axis, type: "String", hideDataFromUi/Graphs); only non-expressible predicates (File type, "produced-by-lead-selection" last-trace-step) remain as JS post-filters.
  • Main table createPlDataTableV3 display rules moved from (spec) => boolean lambdas to host-side ColumnSelectors.
  • Annotation-key string literals replaced with Annotation.* constants where available.
  • Catalog @milaboratories/helpers bumped 1.14.2 → 1.14.4 to match the SDK (fixes a TS2742 declaration-portability error).

No user-facing behavior change intended.

Validation

  • type-check, lint, format, build green across model / ui / workflow / test (no unit/integration tests exist in the block).
  • Needs a live-backend integration pass before merge: main table row/column set + rank column, workflow bundleBuilder.addSingle id compatibility (incl. persisted filter/ranking ids), and umap/pf pframe assembly.

Greptile Summary

Migrates the block model from the removed ColumnCollectionBuilder / ColumnMatch / ArrayColumnProvider APIs to the new host-driven ColumnsCollection + ColumnRecipe mechanism introduced in SDK 1.81.x, with the main table's (spec) => boolean display lambdas moved to host-side RelaxedColumnSelectors. No user-facing behavior change is intended.

Key touched terms:

Term Definition Change
ColumnsCollection Host-side factory that discovers/filters columns from a result pool or accessor(s) Replaces ColumnCollectionBuilder + AnchoredColumnCollection; now called with ["result_pool"] or a TreeNodeAccessor[] instead of constructing a builder
ColumnRecipe Tagged union of column handle types returned by .discover().getColumns(); accessed via .getSpec(), .getData(), .id Replaces ColumnMatch; .column.spec / .column.id access replaced by .getSpec() / .id
DataColumn Subtype of ColumnRecipe for bare leaf storage columns; also provides DataColumn.getStatusByPlRef() New — used with isDataColumn() to filter recipes for createPFrameForGraphs; status check replaces try/catch on ColumnAbsentError
ScopedColumnId Wire format persisted in BlockArgs for filter/ranking selections anchorName: string removed (was always "main", never read by the workflow); column type stays PObjectId via extractPObjectId
ColumnsMeta.allMatches / scores Metadata arrays for discovered columns Type updated ColumnMatch[]ColumnRecipe[]
getSpecByRef New helper wrapping Column(ref)?.getSpec() with an absent status guard Replaces scattered ctx.resultPool.getPColumnSpecByRef(ref) calls across the model
discoveryExcludeSelectors New function returning the host-side exclude selectors for filter/rank discovery Supersedes commonExcludeSelectors; adds label, cluster-id, and per-sample axis excludes pushed host-side
dedupByLeafId New function collapsing multiple recipe variants for the same leaf column to one entry Restores pre-migration dedup that findColumns() did internally; required because discover().getColumns() returns one recipe per reachability variant
buildCollection Shared collection builder for filter, rank, MSA, and table outputs Signature drops ctx: RenderCtx argument (relies on SDK ambient context); now exposes anchorSpec directly; returns ColumnsCollection(["result_pool"]) instead of an AnchoredColumnCollection
  • BlockArgs shape change: ScopedColumnId.anchorName dropped → existing projects re-run the workflow once on next open; stored BlockData keeps the field as an inert leftover.
  • Two concerns remain open (flagged in earlier review threads): the ColumnsCollection() no-arg scope used for poolDiscovered in the main table output vs ColumnsCollection(["result_pool"]) everywhere else, and the missing File value-type guard in isSelectableMatch.
  • The changeset notes that a live-backend integration pass is needed before merge (main table row/column set, bundleBuilder.addSingle id compatibility, umap/pf pframe assembly).

Confidence Score: 5/5

  • This PR is a mechanical API migration with well-documented intent and no user-facing behavior change; the core logic paths are preserved and the author has verified the build and type-check pass.
  • The migration faithfully replaces each removed SDK call with its documented successor. Wire formats (ScopedColumnId.column stays a PObjectId leaf, anchorRef preserved) are carefully maintained for workflow and UI compatibility. The two concerns noted in earlier threads are acknowledged in the changeset and PR description as known follow-ups requiring a live-backend pass before merge. The one remaining raw annotation string at line 449 is a style inconsistency with no runtime impact. No logic regressions are introduced by the changed files.
  • model/src/index.ts and model/src/util.ts warrant a live-backend integration pass (main table column set, filter/ranking dropdowns, umap pframe assembly) before merge, as noted in the PR description.

Important Files Changed

Filename Overview
model/src/index.ts Core model output definitions migrated from old ColumnCollectionBuilder/findColumns/ArrayColumnProvider to ColumnsCollection/discover/ColumnRecipe. Display rules for the main table moved from JS lambdas to host-side RelaxedColumnSelectors. Two open concerns flagged in prior threads: ColumnsCollection() (no-arg) scope for the main table's poolDiscovered vs ColumnsCollection(["result_pool"]) used elsewhere, and the missing File-type guard in isSelectableMatch.
model/src/util.ts Utility layer migrated: ColumnCollectionBuilder removed, getSpecByRef added as a safe PlRef→PColumnSpec wrapper, discoveryExcludeSelectors replaces commonExcludeSelectors, dedupByLeafId restores pre-migration dedup semantics, isSelectableMatch simplified to only the JS-only predicates. One raw annotation string remains at line 449 (Annotation.Score.RankingOrder missed).
model/src/types.ts ScopedColumnId.anchorName removed (was always "main", workflow never read it). ColumnsMeta.allMatches/scores type updated ColumnMatch[]→ColumnRecipe[]. Dead Column/ScopedColumn types removed. Clean migration with clear JSDoc explanations.
ui/src/composables/useAnchorSyncedDefaults.ts No logic changes; ScopedColumnId.anchorRef is still present so anchor-staleness detection in configAnchorKey is unaffected by the anchorName removal.
.github/workflows/build.yaml Adds require-package-path-bump: true to enforce changeset bumps on PRs. Straightforward workflow hardening.
pnpm-lock.yaml SDK bumped 1.79.x → 1.81.x across model/ui/workflow/test; helpers 1.14.2 → 1.14.5; ts-builder 1.6.0 → 1.6.2; ts-configs 1.3.0 → 1.4.0; block-tools 2.12.7 → 2.12.13. Lock file updated consistently.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[RenderCtx output evaluation] --> B[buildCollection]
    B --> C["ColumnsCollection(['result_pool'])"]
    C --> D["discover({ anchors, mode: 'related', maxHops: 2 })"]
    D --> E[dedupByLeafId]
    E --> F[isSelectableMatch]
    F --> G[allMatches / scores]
    G --> H[computeDefaultFilters]
    G --> I[computePresets]

    A --> J[filterConfig output]
    J --> K["result.collection.discover({ anchors, exclude: discoveryExcludeSelectors })"]
    K --> L[dedupByLeafId]
    L --> M[isSelectableMatch]
    M --> N[filterableMatches → options]

    A --> O[rankingConfig output]
    O --> P["result.collection.discover({ anchors, exclude: [..., type:'String'] })"]
    P --> Q[dedupByLeafId]
    Q --> R[isSelectableMatch]
    R --> S[rankableMatches → options]

    A --> T[table output]
    T --> U["ColumnsCollection() — no-arg"]
    U --> V["discover({ anchors: leadSelectionSpec })"]
    V --> W[filter isProducedByLeadSelection]
    W --> X[primaryColumns / secondaryColumns]
    X --> Y[createPlDataTableV3]

    A --> Z[umapPf / umapPcols]
    Z --> AA["ColumnsCollection(['result_pool']).discover(umap name pattern)"]
    AA --> AB[toGraphColumns → createPFrameForGraphs]
Loading

Reviews (2): Last reviewed commit: "refactor: guard on column status instead..." | Re-trigger Greptile

Context used:

  • Context used - Terms is a types in codebase. Provide the list of ... (source)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates @platforma-sdk to 1.80.x and migrates the block model to the new host-driven column-access mechanism, replacing the old ColumnCollectionBuilder and ColumnMatch APIs with ColumnsCollection and ColumnRecipe. The review feedback correctly identifies a critical bug where ColumnsCollection is instantiated without sources, which would break main table rendering, and notes the accidental removal of the assemblingKabatPf resolver. Additionally, a defensive programming improvement was suggested to safely access axesSpec[0]?.name to prevent potential TypeErrors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread model/src/index.ts
Comment thread model/src/util.ts
Comment thread model/src/util.ts
Comment thread model/src/index.ts
…lumn-access mechanism

- Updated versions for @platforma-sdk/workflow-tengo, @platforma-sdk/model, @platforma-sdk/ui-vue, @platforma-sdk/test, and @milaboratories/helpers.
- Migrated the model to the new column-access mechanism, replacing old APIs with host-driven alternatives.
Carries the 1.80.x column-access migration forward to 1.81.

Renames and signature changes:
- `isColumnLazy` → `isDataColumn`, extracted alongside the accessor adapter
  into a shared `toGraphColumns` used by every PColumn-only consumer.
- The `linker` label formatter now receives `LinkerParts` instead of a flat
  label array; the chain is rendered from `parts.linkers[].text`.

Completes two spots the 1.80 pass left on deprecated APIs:
- `umapPf` / `umapPcols` move off `ctx.resultPool.getAnchoredPColumns` and
  `TreeNodeAccessor.getPColumns()` onto anchored `ColumnsCollection`
  discovery. The legacy `{ anchor: "main", idx: 1 }` axis binding has no
  selector form, so the clonotype axis name is read off the resolved anchor
  spec and matched by name.
- Every `PlRef` → spec read goes through `getSpecByRef`, which absorbs the
  `ColumnAbsentError` that `Column(ref)` throws once the pool is locked
  without the column. `ctx.resultPool.getPColumnSpecByRef` returned
  `undefined` there, and an uncaught throw fails the whole output.

`ctx.resultPool.getOptions` stays in `clusterColumnOptions`: the option is
persisted as `diversificationColumn` and feeds `addAnchor`, which needs a
`PlRef`. Reason recorded at the call site.

No `keepMatchingRules`-style workaround is needed: the `evaluateRules` bug
that discarded every display rule when one matched nothing is fixed in
@platforma-sdk/model 1.81.1.
The migration started sending `recipe.id` — a full `ColumnUniversalId` — into
`filters[].value.column` / `rankingOrder[].value.column`. The workflow cannot
read that shape: `bundleBuilder.addSingle` branches on `__isRef`, `resolvePath`
and `{source, axisFilters}`, and drops everything else into the anchored-query
path. A `ColumnDiscoveredId`, which is what a linker-reached hit carries, is not
a valid `AnchoredPColumnSelector`, so only zero-hop hits would have resolved.

Restore the pre-migration contract while keeping the new API inside the model:

- `matchToColumnId` reduces the recipe id with `extractPObjectId`. That is the
  same global ref id the old `ColumnMatch.column.id` carried, so persisted
  selections and the workflow keep working with no changes on their side.
- `dedupByLeafId` collapses discovery results to one recipe per storage column
  for the filter/ranking lists. The old `findColumns` did this itself, reducing
  hits into a `Map<PObjectId, ColumnMatch>` and merging reachability variants;
  `discover().getColumns()` returns one recipe per variant instead, which with
  leaf ids on the wire would surface as duplicate options sharing one value.
  The table keeps the full recipes — their distinct ids are what V3 joins on.

This is deliberate scaffolding, confined to those two functions in
`model/src/util.ts`. `docs/handoff-addsingle-discovered-ids.md` specifies the
workflow-tengo support needed to remove it and put the full id on the wire.
`anchorName` was always the literal "main". It only carried meaning while the
wire value was an anchored query id and the workflow had to know which anchor
to resolve it against; with a leaf `PObjectId` resolved by ref, no anchor is
involved. Nothing in the workflow reads the field, and the two UI lookups that
appeared to filter on it — `options.find((o) => o.value?.anchorName === "main")`
— were picking the first entry of a list where every entry carried the same
value. They now say so directly.

`anchorRef` stays: the UI compares it against the current anchor to tell a
freshly arrived filter/ranking config from a stale one.

Also removes the unused `ScopedColumn` and `Column` types.

This changes the shape of `BlockArgs`, so existing projects re-run the workflow
once. Stored `BlockData` keeps the field as an inert leftover; no migration is
needed to read it.

Drops the handoff note added in the previous commit — it had served its purpose;
the requirement it captured is summarised in the changeset instead.
@AStaroverov
AStaroverov force-pushed the chore/migrate-column-access-mechanism branch from ac8ee56 to dcfdbab Compare August 11, 2026 09:57
@AStaroverov AStaroverov changed the title chore: migrate to new column-access mechanism (SDK 1.80.x) chore: migrate to new column-access mechanism (SDK 1.81.x) Aug 11, 2026
`getSpecByRef` wrapped `Column(ref)` in a try/catch to turn `ColumnAbsentError`
into `undefined`. `DataColumn.getStatusByPlRef` answers the same question
directly, so the swallow becomes an explicit guard and exceptions stay out of
the control flow. The guard covers only the `absent` status — `Column(ref)`
already returns `undefined` while a column is still resolving.

Behaviour is unchanged: a missing column is silently ignored. That is deliberate
for this block, which works with whatever the upstream graph provides. Despite
what the SDK's note on the error suggests, `absent` is not a definitive "gone
for good" — it occurs in normal configurations, so it must not drive any
user-facing warning.
@AStaroverov
AStaroverov force-pushed the chore/migrate-column-access-mechanism branch from ca5a9c5 to 1c0d79d Compare August 11, 2026 10:37
@AStaroverov

Copy link
Copy Markdown
Contributor Author

@greptileai

@mzueva
mzueva self-requested a review August 18, 2026 14:36
@AStaroverov
AStaroverov added this pull request to the merge queue Aug 19, 2026
Merged via the queue into main with commit 6236258 Aug 19, 2026
12 checks passed
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.

2 participants