Skip to content

Support DMS antibody modality - #23

Open
mzueva wants to merge 7 commits into
mainfrom
mzueva/MILAB-6808-dms-antibody
Open

Support DMS antibody modality#23
mzueva wants to merge 7 commits into
mainfrom
mzueva/MILAB-6808-dms-antibody

Conversation

@mzueva

@mzueva mzueva commented Aug 26, 2026

Copy link
Copy Markdown
Contributor
  • Support DMS antibody modality
  • Structurer migration

Greptile Summary

The PR migrates Sequence Properties to the canonical structurer layout, upgrades its Platforma SDK/build toolchain, and introduces typed initialization parameters for template-based block creation.

  • Block kind — the block’s versioned identity and initialization contract; added as a private workspace component with runtime validation for an optional upstream inputAnchor.
  • Block parameters (BlockParams) — values supplied when creating a block from a template; introduced with inputAnchor?: PlRef, while table, graph, and label fields remain ordinary view state.
  • Block pointer — the public descriptor consumers pass to addBlock; replaces the legacy blockSpec loader with SequencePropertiesBlockPointer, referencing the packaged block-pack.
  • Block contract — inferred public data, output, and link types; added through the slim facade as BlockContract, BlockData, and BlockOutputs.
  • Template parameters — the projection exported from current block data for template reuse; added to preserve the selected input anchor across template export and application.
  • Data model — the persisted block-state schema and migration chain; now binds the block kind and initializes new state from optional parameters without changing existing migrations.
  • Structurer — the canonical package and build layout managed by block-tools; adds the kind package, generated facade surfaces, new build targets, and coordinated development/release variants.
  • DMS antibody modality — antibody-oriented sequence-property processing represented through the block’s existing VDJ input and workflow contracts; this PR primarily adapts packaging, SDK integration, and test dependencies around that functionality.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking defects identified.

The structurer migration keeps component paths aligned with generated artifacts, orders workspace dependencies before block packing, and preserves the optional-input idle state while adding template initialization support.

Important Files Changed

Filename Overview
kind/src/index.ts Defines the versioned block kind and validates the optional input-anchor initialization contract.
model/src/dataModel.ts Binds the kind to persisted state and seeds new blocks from template parameters while preserving existing migrations.
model/src/index.ts Binds the model to the kind and adds the inverse template-parameter projection.
block/src/index.ts Introduces the dependency-free typed facade and package-relative block pointer.
block/package.json Migrates block assembly and publication metadata to the structurer layout.
package.json Replaces legacy build modes with explicit structurer development and release variants.
turbo.json Coordinates checks, component builds, software artifacts, tests, and packing under the new environment model.
test/src/helpers.ts Updates integration scaffolding to consume slim block pointers and current upstream state types.
.github/workflows/build.yaml Moves CI to the new build variants, Node version, runner, and publication workflow inputs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Template[Project template] -->|BlockParams: optional inputAnchor| Kind[Versioned block kind]
  Kind --> Model[Data model initialization]
  Model --> Workflow[Sequence Properties workflow]
  Workflow --> Software[Python property computation]
  Model --> UI[Table and graph UI]
  Kind --> Facade[Published block facade]
  Model --> Facade
  Workflow --> Pack[block-pack]
  Software --> Workflow
  UI --> Pack
  Facade -->|SequencePropertiesBlockPointer| Consumer[Platforma consumer]
  Consumer --> Pack
Loading

Reviews (1): Last reviewed commit: "chore: add changeset for the structurer ..." | Re-trigger Greptile

Context used (5)

mzueva added 7 commits August 26, 2026 15:58
Output of the canonical `structure refresh` flow on block-tools 2.14.3:

    structure refresh --update-deps-only
    pnpm install
    structure refresh
    pnpm install
    pnpm fmt

SDK bumps: model/ui-vue 1.79.20 -> 1.83.0/1.83.1, workflow-tengo 6.6.5 ->
6.8.3, tengo-builder 4.0.11 -> 4.0.23, ts-builder 1.6.0 -> 1.7.1, ts-configs
1.3.0 -> 1.4.0, test 1.79.23 -> 1.83.2, block-tools 2.11.6 -> 2.14.3.

Structural changes:

- `kind/` scaffolded (mandatory fourth component). `src/index.ts` still holds
  the `NEEDS_BLOCK_PARAMS` sentinel; the contract is resolved in the next
  commit.
- `block/` becomes the slim facade: `dependencies: {}`, a bundled `dist/` plus
  `block-pack/`, and the from-pack-v2 `SequencePropertiesBlockPointer` export.
  Every sibling package is now `private: true`.
- Root build scripts move off `PL_PKG_DEV` onto `PL_BUILD_CHANNEL` /
  `PL_BUILD_VARIANT` / `PL_BUILD_LOCATION`. `build` and `build:dev` are
  replaced by `build:dev-local`, `build:dev-remote`, `build:dev-no-software`,
  `build:dev-binary-existing` and `build:release`. The root gains
  `upgrade-sdk`, so later SDK upgrades are one command.
- `software/` builds via `block-tools software build` instead of `pl-pkg`.
- CI (`build.yaml`) is now scaffold-owned: hz-ubuntu-dind runners, node 22,
  turbo remote cache, and `require-package-path-bump`.
- Legacy config removed: `.prettierrc`, `test/eslint.config.mjs`, the
  hand-written `block/index.js` and `workflow/index.js` entry stubs.
- The test scope is lint- and fmt-checked now, so its sources were reformatted.

One hand edit was needed to make `pnpm fmt` pass: `test/src/wf.test.ts`
carries 46 `it.todo`/`it.skip` placeholders that document unimplemented
scenarios, and the new test-scope lint runs with `--deny-warnings`. Added a
file-level `oxlint-disable vitest/warn-todo` with the reason, rather than
dropping the documented gaps.

pnpm was downgraded 9.15.0 -> 9.12.0 by the structurer's canonical pin.
…e build

Author-code work the structurer cannot do, on top of the previous commit.

Kind contract. `BlockParams` is `{ inputAnchor?: PlRef }` — the upstream
dataset a new instance computes properties on, and the only field a creator or
a project template supplies. Optional, because `templateParams` hands live
state back untouched and a block whose input is not picked yet holds
`undefined`; requiring it would make the block export a file its own kind
refuses to apply. `parseInitializationParams` checks that one field with the
SDK's own `isPlRef`, so the kind does not restate a type it does not own. The
model consumes it in `init(({ params }) => …)` and projects the same single
field back through `.templateParams`. Everything else in `BlockData` is view
state that always defaults.

Facade build. `block/dist/index.d.ts` inlines every type in the model's public
surface (the facade config is `external: () => false`), and `BlockData` holds
two `GraphMakerState` fields, which reach `@platforma-sdk/ui-vue` through
`graph-maker/dist/forms`. ui-vue 1.83.1 stopped publishing the four
`dist/components/*.vue.d.ts` files that its own `dist/lib.d.ts` re-exports, so
the bundle failed on four unresolvable `.vue` specifiers. Every version from
1.79.20 through 1.83.0 ships them, which is why blocks already migrated on
ui-vue <= 1.82.6 are unaffected. Held the catalog at 1.83.0 with the reason
recorded inline; `vue` is 3.5.24 either way, so no UI type cascade. Drop the
pin once a fixed ui-vue is published.

The model also gains its own `@platforma-sdk/ui-vue` dependency. graph-maker
declares ui-vue as a peer, and with no local declaration pnpm resolved that
peer to npm-latest rather than the catalog version — so the pin missed the
model's graph-maker instance entirely. Worth having on its own: it stops the
model's ui-vue from drifting on every install.

Cross-block test deps. Bumped samples-and-data to ^1.20.0 and
mixcr-clonotyping-2 to ^2.22.0 and dropped both `.model` deps: the upstreams
are slim facades now, so `BlockData` comes from the facade and blocks are added
by `<Block>BlockPointer` instead of the retired `blockSpec`. The old pins
dragged in second copies of `@platforma-sdk/model` (1.63.1, 1.65.4) and `zod`
(3.23.8); the lockfile now holds exactly one of each. Removed both
`satisfies <Block>BlockData` clauses — a facade bundles its own copy of the
brand symbols behind `PlId` and `CanonicalizedJson`, so those types never unify
with our catalog SDK's, and `mutateBlockStorage` takes the value as `unknown`.
The hand-rolled version-5 `tableState` literal the 2.18 pin needed is replaced
by `createPlDataTableStateV2()`.

Dropped two catalog entries the migration orphaned: `eslint` (its only
consumer was `test/eslint.config.mjs`, which the structurer deleted) and
`@platforma-sdk/blocks-deps-updater` (replaced by the root `upgrade-sdk`).

`pnpm build:dev-local` and every `check` are green, 12/12, from a
frozen-lockfile install. `structure check` is a fixpoint.
…lity vdj

synthetic-repertoire-profiler runs one pipeline over both antibody/TCR parents
and designed libraries, and everything it emits sits on the modality-neutral
pl7.app/variantKey axis. It keeps pl7.app/repertoire/extractionRunId in both
cases, so the run-id heuristic classified a VDJ run as amplicon and scanned it
as a single whole sequence — the FR1-FR4 regions it emits, CDR3 included, were
never read. Nothing failed; the run just produced whole-sequence properties for
a V-domain.

The profiler declares the kind it produced in the entity-axis domain, so read
that first: `vdj` maps to antibody_tcr_universal, the branch that already
existed for VDJ-on-variantKey, and `amplicon` maps to amplicon. Everything
below the declaration is unchanged, so an input without one behaves exactly as
before.

Nothing else in the pipeline needed changing, and each step was checked against
the producer rather than assumed:

- The profiler's region columns are `pl7.app/sequence` with the region name in
  pl7.app/feature. The universalSequences bundle entry already matches those,
  and vdjSequences is empty for this producer, so the existing fallback finds
  them. Its whole-variant amplicon-sequence column is matched too and dropped
  by the REQUIRED_FEATURES filter.
- A vdj run always carries exactly FR1-FR4: the profiler's model rejects a
  vdj-scheme parent with any other region set, and germline auto-detect
  hardcodes the same seven. Coverage lands on full_chain.
- No scClonotypeChain is emitted, so the existing bulk-without-chain default
  gives one chain "A". hasFv needs both chains, and the profiler frames
  variants against one parent with no VH/VL pairing, so Fv columns stay absent.
- pipeline.py branches on mode == "peptide" against everything else, so
  antibody_tcr_universal reaches run_antibody_tcr with no Python change.

R13b wording: a declared vdj input arrives with no receptor, and the profiler
cannot emit one — germline auto-detect builds its reference from the user's own
parents, so there is no library locus to read. The existing notice tells the
user to pick a MiXCR preset that emits the annotation, which is a dead end for
this data. Added receptorNotDeclared for that case; receptorNotDetected keeps
its text for MiXCR input, where the advice is actionable. Per spec R13a the
receptor affects labels only, so the antibody default mislabels without
changing column identity.

detectMode moved out of main.tpl.tengo into modality.lib.tengo so it could be
unit-tested — pure synchronous logic, no resources. modality.test.tengo covers
all four producers, both declared modalities, an unrecognised modality value,
the declaration-beats-run-id ordering, and the undeclared back-compatibility
path. Verified by mutation: moving the declaration read below the run-id checks
fails exactly the two tests that assert the ordering. The workflow test script
is `pl-tengo test`, matching blocks/repertoire-labeling; the dead vitest
placeholder (one it.skip) is gone, and the structurer dropped the now-unused
vitest config and tsconfig with it.

Recorded as SD-010, including why this block keeps the soft partial-region
messages where antibody-sequence-liabilities hard-asserts CDR3 presence.

Spec: synthetic-repertoire-profiler/dms-modality.md (atom A-0060).
The entry said receptor affects labels only. It does not: hasFv requires
receptor == "IG" and so gates whether the Fv columns exist, and the R11c VHH
heuristic reads it too. Neither changes column identity, which was the part
that was right. Records that a declared vdj profiler run satisfies two of the
three VHH conditions by construction, so the heuristic can fire on a designed
library whose single chain is an artifact of the producer emitting no chain
key — flagged as an open question rather than changed.
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