Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
21 changes: 21 additions & 0 deletions .changeset/facs-bin-analysis-first-implementation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@platforma-open/milaboratories.sort-seq-analysis.software': patch
'@platforma-open/milaboratories.sort-seq-analysis.block': patch
---

Sort-Seq Analysis — first implementation.

Scores protein variants from a sort-seq (FACS bin) experiment. Per condition the block emits the
read-weighted mean of the gate ranks each variant sorted into (`pl7.app/facsBin/gateRankMean`) and that
value minus the parent's (`pl7.app/facsBin/binScore`), both keyed on the profiler's variant axis and
carrying the condition — and, on the bin score, the reference mode — as matchable domain keys.

- **software** — the score computation as a Python package on the scientific-slim runenv, with its own
pytest suite pinning the arithmetic clause by clause against hand-computed numbers.
- **workflow** — resolves the inputs against the abundance anchor, exports the reads and variants tables,
invokes the entrypoint once, and constructs one PColumn per file the manifest names.
- **model** — the seven-argument surface with every configuration rule validated before the run, the four
option lists, and the outputs the three views read.
- **ui** — one settings drawer plus Results, Read distribution and Run summary.

Integration tests against the real upstream chain are not in this change.
83 changes: 83 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Build, Test and Release Platforma Block
on:
merge_group:
pull_request:
types: [opened, reopened, synchronize]
branches:
- 'main'
push:
branches:
- 'main'
workflow_dispatch: {}
jobs:
init:
runs-on: hz-ubuntu-dind
steps:
- uses: milaboratory/github-ci/actions/context/init@v4
with:
version-canonize: false
branch-versioning: main
run:
needs:
- init
uses: milaboratory/github-ci/.github/workflows/node-simple-pnpm.yaml@v4
with:
app-name: 'Block: Sort Seq Analysis'
app-name-slug: 'block-sort-seq-analysis'
node-version: '20.x'
gha-runner-label: hz-ubuntu-dind
build-script-name: 'build:dev-local'
build-before-publish-script-name: 'build:release'
pnpm-recursive-build: false

test: true
test-script-name: 'test'
pnpm-recursive-tests: false
team-id: 'ciplopen'

publish-to-public: 'true'
package-path: 'block'
create-tag: 'true'

# Require the published `block` package to be bumped by a changeset on
# PRs (empty changeset or the `skip-changelog` label waives it). Needs
# the input to exist on the pinned `@v4` reusable workflow.
require-package-path-bump: true

npmrc-config: |
{
"registries": {
"https://registry.npmjs.org/": {
"scopes": ["milaboratories", "platforma-sdk", "platforma-open"],
"tokenVar": "NPMJS_TOKEN"
}
}
}
secrets:
env: |
{ "PL_LICENSE": ${{ toJSON(secrets.MI_LICENSE) }},
"MI_LICENSE": ${{ toJSON(secrets.MI_LICENSE) }},
"NPMJS_TOKEN": ${{ toJSON(secrets.NPMJS_TOKEN) }},
"PL_CI_TEST_USER": ${{ toJSON(secrets.PL_CI_TEST_USER) }},
"PL_CI_TEST_PASSWORD": ${{ toJSON(secrets.PL_CI_TEST_PASSWORD) }},

"AWS_CI_IAM_MONOREPO_SIMPLE_ROLE": ${{ toJSON(secrets.AWS_CI_IAM_MONOREPO_SIMPLE_ROLE) }},
"AWS_CI_TURBOREPO_S3_BUCKET": ${{ toJSON(secrets.AWS_CI_TURBOREPO_S3_BUCKET) }},

"HZ_CI_TURBO_S3_BUCKET": ${{ toJSON(vars.HZ_CI_TURBO_S3_BUCKET) }},
"HZ_CI_TURBO_S3_ENDPOINT": ${{ toJSON(vars.HZ_CI_TURBO_S3_ENDPOINT) }},
"HZ_CI_TURBO_S3_REGION": ${{ toJSON(vars.HZ_CI_TURBO_S3_REGION) }},
"HZ_CI_TURBO_S3_ACCESS_KEY": ${{ toJSON(secrets.HZ_CI_TURBO_S3_ACCESS_KEY) }},
"HZ_CI_TURBO_S3_SECRET_KEY": ${{ toJSON(secrets.HZ_CI_TURBO_S3_SECRET_KEY) }},
"HZ_CI_CACHE_S3_ACCESS_KEY": ${{ toJSON(secrets.HZ_CI_CACHE_S3_ACCESS_KEY) }},
"HZ_CI_CACHE_S3_SECRET_KEY": ${{ toJSON(secrets.HZ_CI_CACHE_S3_SECRET_KEY) }},

"PL_REGISTRY_PLATFORMA_OPEN_UPLOAD_URL": ${{ toJSON(secrets.PL_REGISTRY_PLOPEN_UPLOAD_URL) }},
"QUAY_USERNAME": ${{ toJSON(secrets.QUAY_USERNAME) }},
"QUAY_ROBOT_TOKEN": ${{ toJSON(secrets.QUAY_ROBOT_TOKEN) }} }

SLACK_CHANNEL: ${{ secrets.SLACK_BLOCKS_CI_CHANNEL }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

GH_ZEN_APP_ID: ${{ secrets.GH_ZEN_APP_ID }}
GH_ZEN_APP_PRIVATE_KEY: ${{ secrets.GH_ZEN_APP_PRIVATE_KEY }}
34 changes: 34 additions & 0 deletions .github/workflows/mark-stable.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Mark Platforma Block as Stable
on:
workflow_dispatch: {}
jobs:
init:
runs-on: ubuntu-latest
steps:
- uses: milaboratory/github-ci/actions/context/init@v4
with:
version-canonize: false
branch-versioning: main
run:
needs:
- init
uses: milaboratory/github-ci/.github/workflows/block-mark-stable.yaml@v4
with:
app-name: 'Block: Sort Seq Analysis - Mark Stable'
node-version: '20.x'
npmrc-config: |
{
"registries": {
"https://registry.npmjs.org/": {
"scopes": ["milaboratories", "platforma-sdk", "platforma-open"],
"tokenVar": "NPMJS_TOKEN"
}
}
}
secrets:
env: |
{ "NPMJS_TOKEN": ${{ toJSON(secrets.NPMJS_TOKEN) }},
"AWS_CI_IAM_MONOREPO_SIMPLE_ROLE": ${{ toJSON(secrets.AWS_CI_IAM_MONOREPO_SIMPLE_ROLE) }} }

SLACK_CHANNEL: ${{ secrets.SLACK_BLOCKS_CI_CHANNEL }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
77 changes: 77 additions & 0 deletions .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Python Tests

# The score computation's own suite, run on its own rather than through the block.
# `computation-test-suite`: the arithmetic is where a wrong number comes from, and a
# block-level assertion cannot see one. This lane needs no backend, no registry and no
# upstream pipeline, so it stays fast and fails for one reason only.

on:
merge_group:
pull_request:
types: [opened, reopened, synchronize]
branches:
- 'main'
push:
branches:
- 'main'
workflow_dispatch: {}

jobs:
pytest:
runs-on: ubuntu-latest
defaults:
run:
working-directory: software
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v5
with:
version: "0.11.16"
enable-cache: true
cache-dependency-glob: software/pyproject.toml

- name: Install dependencies
run: uv sync

- name: Run pytest
run: uv run pytest

requirements-sync:
# src/requirements.txt is generated from pyproject.toml (the single source of truth)
# and is what the package builder installs from. This job fails if the committed file
# has drifted — e.g. someone changed a dependency and forgot to regenerate. Fix
# locally with `pnpm deps:export` (see software/package.json).
#
# It matters more here than it looks: requirements.txt is a *selector* over the wheels
# the runenv already ships, installed with --no-index. A drifted pin does not fall back
# to PyPI — it fails the offline install at block build time.
runs-on: ubuntu-latest
defaults:
run:
working-directory: software
steps:
- uses: actions/checkout@v4

- uses: astral-sh/setup-uv@v5
with:
version: "0.11.16"
enable-cache: true
cache-dependency-glob: software/pyproject.toml

- name: Regenerate requirements.txt from pyproject.toml
# The same script `pnpm deps:export` runs locally — single source of truth.
run: bash scripts/deps-export.sh

- name: Verify requirements.txt is in sync with pyproject.toml
run: |
if ! git diff --exit-code -- src/requirements.txt; then
echo ""
echo "::error::src/requirements.txt is out of sync with pyproject.toml"
echo "Dependencies are defined in software/pyproject.toml; src/requirements.txt is generated."
echo "Resolve locally with:"
echo " cd software && pnpm deps:export"
echo "then commit the updated src/requirements.txt."
exit 1
fi
echo "src/requirements.txt is in sync with pyproject.toml."
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.test_auth.json
node_modules/
dist/
block-pack/
dev/
work/
log/
.turbo
vite.config.*.timestamp-*
software/**/*.tgz
.DS_Store
.vscode/sftp.json
test-dry-run.json

# Python — the software package. Not written by `block-tools structure`; re-add these if a
# structure refresh drops them. Without them `__pycache__` gets committed, which has
# already happened in blocks/titeseq-analysis.
__pycache__/
*.py[cod]
.pytest_cache/
.ruff_cache/
.venv/
8 changes: 8 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//registry.npmjs.org/:always-auth=true
//registry.npmjs.org/:_authToken=${NPMJS_TOKEN}
@milaboratories:registry=https://registry.npmjs.org/
@platforma-open:registry=https://registry.npmjs.org/
@platforma-sdk:registry=https://registry.npmjs.org/
//npm.pkg.github.com/:always-auth=true
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
@milaboratory:registry=https://npm.pkg.github.com/
1 change: 1 addition & 0 deletions .structure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":2}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# sort-seq-analysis
FACS Bin Analysis Block

**Sort-Seq Analysis** — a Platforma block that scores protein variants from a sort-seq (FACS bin)
experiment. Per condition it emits the read-weighted mean of the gate ranks each variant sorted into
(`pl7.app/facsBin/gateRankMean`) and that value minus the parent's (`pl7.app/facsBin/binScore`).

## Specification

`docs/text/work/projects/sequence-repertoires/facs-bin-analysis/` in the `docs/text` repo.

- `README.md` — the front door: what the block is for and what always holds.
- `implementation.md` — the implementer's door: the decisions and why each is held.
- `work/atoms/` — the spec source. Never hand-edit the two rendered docs above.

Cross-block contracts this block honours rather than decides live in the umbrella spec,
`docs/text/work/projects/sequence-repertoires/dms-analysis/`.

## Layout

| Path | What |
|---|---|
| `model/` | `BlockModelV3` — args projection, outputs, sections |
| `workflow/` | Tengo template — resolves inputs, invokes the computation, builds the output columns |
| `ui/` | Vue 3 UI — settings drawer and result views |
| `software/` | Python package holding the score computation |
| `block/` | Published facade — block meta and components |
| `test/` | Integration tests against a running backend |

## Build

```bash
pnpm install
pnpm build:dev-local # local software paths; there is no plain `build` script
```

The layout is owned by `block-tools structure`. To take an SDK upgrade, run `pnpm upgrade-sdk` — do not
hand-edit tsconfigs, turbo config, lint config or the catalog.
1 change: 1 addition & 0 deletions block/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @platforma-open/milaboratories.sort-seq-analysis.block
Binary file added block/logos/block-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added block/logos/organization-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions block/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "@platforma-open/milaboratories.sort-seq-analysis.block",
"version": "1.0.0",
"files": [
"dist",
"block-pack"
],
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"sources": "./src/index.ts",
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"scripts": {
"build": "ts-builder build --target block-facade && block-tools pack",
"check": "ts-builder type-check --target block-facade",
"prepublishOnly": "block-tools publish --unstable -r s3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1 --registry-serve-url https://blocks.pl-open.science",
"do-pack": "shx rm -f package.tgz && pnpm pack && shx mv *.tgz package.tgz"
},
"dependencies": {},
"devDependencies": {
"@milaboratories/ts-builder": "catalog:",
"@milaboratories/ts-configs": "catalog:",
"@platforma-open/milaboratories.sort-seq-analysis.model": "workspace:*",
"@platforma-open/milaboratories.sort-seq-analysis.ui": "workspace:*",
"@platforma-open/milaboratories.sort-seq-analysis.workflow": "workspace:*",
"@platforma-sdk/block-tools": "catalog:",
"@platforma-sdk/model": "catalog:",
"shx": "catalog:",
"typescript": "catalog:"
},
"block": {
"components": {
"workflow": "@platforma-open/milaboratories.sort-seq-analysis.workflow/dist/tengo/tpl/main.plj.gz",
"model": "@platforma-open/milaboratories.sort-seq-analysis.model/dist/model.json",
"ui": "@platforma-open/milaboratories.sort-seq-analysis.ui/dist"
},
"meta": {
"title": "Sort-Seq Analysis",
"logo": "file:logos/block-logo.png",
"url": "https://github.com/platforma-open/sort-seq-analysis",
"support": "mailto:support@milaboratories.com",
"description": "Scores protein variants from a sort-seq (FACS bin) experiment: per condition, the read-weighted mean of the gate ranks each variant sorted into, and that value minus the parent's.",
"longDescription": "file:../docs/description.md",
"changelog": "file:./CHANGELOG.md",
"tags": [
"downstream",
"dms",
"antibody",
"assay"
],
"organization": {
"name": "MiLaboratories Inc",
"url": "https://milaboratories.com/",
"logo": "file:logos/organization-logo.png"
}
}
}
}
5 changes: 5 additions & 0 deletions block/src/AGENTS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// This file is managed by `block-tools structure`. Do not edit by hand.
// Narrow MCP / AI surface.

export type { BlockContract, BlockOutputs, BlockData } from "./index";
export * from "./agents-extra";
8 changes: 8 additions & 0 deletions block/src/agents-extra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Author-owned. `block-tools structure` does not modify this file.
// MCP / AI extension surface. Add types and functions the agent
// surface should expose.
//
// In the future this file will host JS functions the MCP runtime
// can execute. The `.d.ts` declarations stay visible to the agent;
// the JS bodies execute in the MCP code-execution context (the
// agent sees the types but not the implementation).
Loading
Loading