Skip to content

Migrate to structure v2 and declare a block kind - #130

Merged
AStaroverov merged 11 commits into
mainfrom
feat/project-template-migration
Aug 20, 2026
Merged

Migrate to structure v2 and declare a block kind#130
AStaroverov merged 11 commits into
mainfrom
feat/project-template-migration

Conversation

@AStaroverov

@AStaroverov AStaroverov commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

What

Brings samples-and-data onto the current canonical block layout and gives it the now-mandatory kind, whose init-params contract is what a project template supplies to seed a new instance.

Structure v2 + SDK upgrade

block-tools structure refresh output: block-tools 2.14.0, model/ui-vue 1.82.x, workflow-tengo 6.8.2, tengo-builder 4.0.23. Software packages move from pl-pkg to block-tools software build; build scripts move to the PL_BUILD_* channel scheme; CI moves to the hz-ubuntu-dind runner.

The kind and its init-params contract

The contract is the study setup a template carries — the metadata columns the study collects, the name of the sample column, how its datasets are configured — plus whichever of its data can be resolved from where the block lands.

Either the whole study travels or none of its data does. Block state is one connected thing: datasets key their files by sample or by group, sample groups and multiplexing rules name those samples, metadata values are keyed by them, and args holds the pieces to each other. So:

  • every file a storage reference (index:// names a {storageId, path}) → the state goes as it stands;
  • any file a local upload (a machine-local path signed with the installation's own secret) → only the setup goes, and the new project imports its own data.

Carrying part of it would mean picking which invariant to break — in particular validateDatasets, which requires as many sample groups as file groups.

metadataUploadHandle is deliberately outside the contract: it is not a link to the metadata table but scaffolding for one import dialog, cleared as soon as the parsed values land in metadata.

The dataset and metadata types move into the kind package and are re-exported from the model, so every existing import keeps resolving. The projection lives in model/src/template_params.ts as the exported deriveTemplateParams.

Two fixes the upgrade forced

  • TreeNodeAccessor.resolveAny was removed from the SDK; the metadataFile output now uses traverse, its alias.
  • @platforma-sdk/model 1.82.0 depends on pl-model-common 1.48.0 while the catalog range resolved the kind's direct dependency to 1.47.3. PlId is branded there, so two copies gave the model two incompatible PlId types. The catalog is now pinned to the version the SDK uses.

Verification

  • turbo run build — 13/13 green; pnpm install --frozen-lockfile clean.
  • The projection checked against built output: storage-only project carries every field unchanged; one local file anywhere reduces every dataset to its setup and drops roster, values and preprocess queues; an unset file slot does not block the transfer; a project with no datasets travels. validateDatasets' invariant holds on every outcome, and the kind's parser accepts what the projection emits.
  • The kind's parser rejects 13 classes of malformed input (non-object, array, unknown content.type, bad valueType, non-string roster entries, …).
  • collectDatasetHandles is exhaustive over DSContent — verified by removing a case and getting TS2345 ... not assignable to parameter of type 'never'.

Not run: the block's integration tests, which need a live backend on PL_ADDRESS. BlockData's shape is unchanged, so test/src/wf.test.ts should pass as is — assumption, not a check.

Before merge

Commit 22891f1 ("pin SDK deps to local monorepo builds") was the temporary file: overrides needed while @platforma-sdk/block-kind was unpublished. It is published now (1.1.0) and 58d8a58 already removed the overrides — the earlier commit is left in history only as the record.

Greptile Summary

The PR migrates the block to structure v2, upgrades its SDK and software-build tooling, and introduces a block-kind contract used to initialize instances from project templates.

  • Block kind — the versioned runtime identity and initialization contract for the block; added as a new kind package and connected to both the data model and block model.
  • BlockParams — optional initialization fields used to seed a block; added with datasets, metadata, sample mappings, and preprocessing queues.
  • DSAny / DSContent — the dataset envelope and discriminated union of supported dataset content; moved from the model into the kind package and re-exported for compatibility.
  • MTColumn — a metadata-column definition and its sample-keyed values; moved into the kind package and included in template projection.
  • ImportFileHandle — a reference to either portable indexed storage or an installation-local upload; now used by the kind parser and template-portability projection.
  • deriveTemplateParams — the projection from current block state to template initialization parameters; added to preserve complete portable studies or strip data when a dataset contains a local upload.
  • DataModelBuilder / BlockModelV3 — model construction APIs; upgraded to register the new kind and initialize block state from parsed parameters.
  • PL_BUILD channel variables — build channel, variant, and location configuration; replace the former PL_PKG_DEV build flow for local and release builds.

Confidence Score: 4/5

The PR is not yet safe to merge because templates can still restore installation-local preprocessing handles on a destination where they cannot resolve.

The template projection checks portability only inside datasets, then copies both preprocessing queues unchanged; destination initialization feeds those handles into prerun imports, leaving the previously reported portability failure reachable.

Files Needing Attention: model/src/template_params.ts

Important Files Changed

Filename Overview
kind/src/types.ts Defines the new block initialization contract and relocates the shared dataset and metadata types into the kind package.
kind/src/params.ts Adds field-level runtime validation for initialization parameters, including dataset discriminators and file-handle forms.
model/src/data_model.ts Registers the block kind and initializes each persisted field from optional template parameters with existing defaults.
model/src/template_params.ts Adds template-state projection, but its dataset-only portability gate still allows local preprocessing handles to reach destination blocks.
model/src/index.ts Registers the kind and template projection, adopts the upgraded model API, and replaces the removed resource-resolution alias.
package.json Replaces legacy package-development build variables with structure-v2 build channel, variant, and location scripts.
.github/workflows/build.yaml Moves CI to Node 22 and the DinD runner while separating local development and release build scripts.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Current block state] --> B[deriveTemplateParams]
  B --> C{All dataset handles portable?}
  C -->|Yes| D[Copy datasets, metadata, samples, and preprocessing queues]
  C -->|No| E[Keep dataset setup and clear sample/group data]
  D --> F[Kind parser validates params]
  E --> F
  F --> G[Data model initializes destination block]
  G --> H[Prerun imports queued preprocessing handles]
Loading

Reviews (2): Last reviewed commit: "refactor: take the array guard from es-t..." | Re-trigger Greptile

Context used:

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

Output of `block-tools structure refresh`, plus the catalog entry for
@platforma-sdk/block-kind and the kind/ workspace member the refresh needs
to discover.

block-tools 2.11.6 -> 2.13.0, model/ui-vue 1.79.20 -> 1.81.1,
workflow-tengo 6.6.5 -> 6.8.2, tengo-builder 4.0.11 -> 4.0.22.
Software packages move from pl-pkg to `block-tools software build`;
build scripts move to the PL_BUILD_* channel scheme.
Every block must declare a kind; this one's init-params contract is the
study setup a project template supplies — the metadata columns the study
collects, the name of the sample column, and how its datasets are
configured — plus whichever of its data can be resolved from where the
block lands.

Either the whole study travels or none of its data does. Block state is
one connected thing: datasets key their files by sample or by group,
sample groups and multiplexing rules name those samples, metadata values
are keyed by them, and args holds the pieces to each other. So when every
file is a storage reference (index:// names a storage and a path) the
state goes as it stands; when any file is a local upload — a machine-local
path signed with the installation's own secret — only the setup goes, and
the new project imports its own data. Carrying part of it would mean
picking which invariant to break.

metadataUploadHandle stays out of the contract: it is not a link to the
metadata table but scaffolding for one import dialog, cleared as soon as
the parsed values land in metadata.

The dataset and metadata types move into the kind package and are
re-exported from the model, so every existing import keeps resolving.
Also replaces the removed TreeNodeAccessor.resolveAny with traverse,
which the SDK upgrade requires.
Temporary. @platforma-sdk/block-kind is not published to npm yet, so the
catalog entry only resolves through these overrides — drop this commit
once it is published.
@platforma-sdk/model 1.82.0 depends on pl-model-common 1.48.0, while the
catalog range resolved the kind's direct dependency to 1.47.3. PlId is
branded there, so the two copies gave the model two incompatible PlId
types and multiplexing-rules-validation.ts failed to compile. Pin the
catalog to the version the SDK uses.

The lockfile still carried the local file: overrides after they were
removed from package.json, which fails the frozen-install gate.

Also puts deriveTemplateParams above its helpers.
Comment on lines +27 to +28
h5adFilesToPreprocess: data.h5adFilesToPreprocess,
seuratFilesToPreprocess: data.seuratFilesToPreprocess,

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 Preprocessing handles bypass portability check

When the datasets are portable or empty but either preprocessing queue still contains a local upload handle, deriveTemplateParams copies that handle into the template because its portability check examines only dataset contents. The destination block restores the queue and attempts to import the installation-local handle during prerun, preventing the corresponding preprocessing outputs from resolving.

Prompt To Fix With AI
This is a comment left during a code review.
Path: model/src/template_params.ts
Line: 27-28

Comment:
**Preprocessing handles bypass portability check**

When the datasets are portable or empty but either preprocessing queue still contains a local upload handle, `deriveTemplateParams` copies that handle into the template because its portability check examines only dataset contents. The destination block restores the queue and attempts to import the installation-local handle during prerun, preventing the corresponding preprocessing outputs from resolving.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

@AStaroverov
AStaroverov marked this pull request as draft August 19, 2026 17:14
Splits the kind into types/params/index like every sibling block, and
replaces the hand-rolled primitive checks with es-toolkit predicates:
isPlainObject for the object envelopes — hand-written it is wrong three
ways, since typeof null is "object", Object.keys(5) is [] and
Object.keys(["a"]) is ["0"] — plus isString and isBoolean for the
fields. The file-handle check now uses the SDK's own prefix guards
instead of accepting any string.

Adds the CONTRACT table with the satisfies drift guard the other kinds
carry, which this one lacked: every field being optional means a parser
that simply forgot one would otherwise return a valid BlockParams and say
nothing.

Numeric and array guards stay native on purpose. es-toolkit only ships
those in its lodash-compat layer, where isInteger returns a plain boolean
and cannot be a type guard at all, and isNumber accepts NaN and boxed
Numbers — both looser than Number.isInteger and Array.isArray.

The per-item error messages get coarser: a bad metadata column now names
the field rather than the row index. That is what the shared shape costs,
and what the five sibling kinds already accept.
… the contract

isArray comes from es-toolkit/compat, which is where the narrowing
version lives; the root entry ships none.

BlockParams was appended at the end of types.ts, under 240 lines of the
dataset and metadata types that support it. It is what the file exists to
declare, so it now leads — as it does in every sibling kind.
@AStaroverov
AStaroverov marked this pull request as ready for review August 20, 2026 12:03
Publishing goes to the normal channel. The flag was carried while the
batch was in flight; the structurer strips it on every refresh, so
keeping it also meant `structure check` reporting this one field as
drift forever.
@AStaroverov
AStaroverov enabled auto-merge August 20, 2026 12:18
@AStaroverov
AStaroverov added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit ab5334a Aug 20, 2026
10 checks passed
@AStaroverov
AStaroverov deleted the feat/project-template-migration branch August 20, 2026 12:23
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