From 1cbd575e0e48e0c643e16c9e2c18c814c806c122 Mon Sep 17 00:00:00 2001 From: Donncha O'Toole Date: Tue, 23 Jun 2026 13:52:25 +0100 Subject: [PATCH 001/104] Document DotMatch Pro boundary --- .github/PULL_REQUEST_TEMPLATE.md | 6 + .gitignore | 4 + CONTRIBUTING.md | 7 +- NOTICE | 9 ++ README.md | 8 ++ SECURITY.md | 13 +- TRADEMARKS.md | 18 +++ docs/commercial-boundary.md | 48 +++++++ docs/evidence-packet-v1.md | 34 +++++ docs/index.md | 5 + python/tests/test_check_repository_ready.py | 75 +++++++++- scripts/check_repository_ready.py | 145 +++++++++++++++++++- 12 files changed, 365 insertions(+), 7 deletions(-) create mode 100644 NOTICE create mode 100644 TRADEMARKS.md create mode 100644 docs/commercial-boundary.md create mode 100644 docs/evidence-packet-v1.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5b1c719b..1ce91d81 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,6 +10,7 @@ - [ ] `make pretag-ready` if release, packaging, benchmark evidence, workflow examples, or public docs changed. - [ ] `make asan` if native C code, memory ownership, parsing, indexing, or allocation behavior changed. - [ ] `make scientific-readiness-ready` if claims, gates, packaging, release process, or evidence boundaries changed. +- [ ] `make repository-ready` if governance, security, licensing, trademark, public docs, or commercial-boundary wording changed. - [ ] Relevant benchmark/report gate, if claims or artifacts changed: ## Claim Boundary @@ -17,6 +18,11 @@ - [ ] This PR does not broaden README/docs claims beyond checked evidence. - [ ] New benchmark wording links to raw artifacts, commands, and validation status. - [ ] Large generated datasets or scratch outputs are not committed. +- [ ] No real FASTQ/BAM/BCL/customer assay data is committed; examples are synthetic, minimized, or public and scoped. + +## Acceptance Criteria + +- ## Notes diff --git a/.gitignore b/.gitignore index e595a59e..a3329229 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,9 @@ __MACOSX/ # Local planning and non-public strategic notes .codex-goals/ +/dotmatch-pro/ +/dotmatch-pro-infra/ +/dotmatch-pro-site/ docs/software-paper-*.md docs/software-paper-*.json scripts/check_paper_readiness.py @@ -69,6 +72,7 @@ examples/workflows/snakemake/output/ # Generated example inputs/outputs. Keep checked-in benchmark evidence under # benchmarks/raw, benchmarks/figures, and docs/benchmarks visible. # Large public datasets are fetched by scripts instead of committed. +artifacts/ benchmarks/work/ examples/crispr_guides/data/ examples/crispr_guides/output/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d0ecc80a..507ceae8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,7 +57,12 @@ Run `make coverage` for changes that touch `src/qdalign.c`, `src/qda.c`, CLI par ## Contributor Certification -By contributing, you certify that you wrote the contribution or have the right to submit it under the Apache-2.0 license used by DotMatch. Add a Developer Certificate of Origin sign-off to commits when practical: +Normal contributions are accepted under the same Apache-2.0 terms as DotMatch +unless otherwise agreed in writing before the contribution is submitted. + +By contributing, you certify that you wrote the contribution or have the right +to submit it under the Apache-2.0 license used by DotMatch. Add a Developer +Certificate of Origin sign-off to commits when practical: ```bash git commit -s -m "your message" diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..71da18e7 --- /dev/null +++ b/NOTICE @@ -0,0 +1,9 @@ +DotMatch +Copyright 2026 Donncha O'Toole + +DotMatch is licensed under the Apache License, Version 2.0. See LICENSE for +the full license text. + +"DotMatch" and "DotMatch Pro" are names used by the project maintainer for the +open-source engine and commercial assay reliability workbench. Trademark and +brand-use guidance is documented in TRADEMARKS.md. diff --git a/README.md b/README.md index aeccc465..d2b70fcf 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ release features are only described as publicly available after the matching package version passes the install smoke tests in [Packaging Notes](docs/packaging.md). +## DotMatch Pro + +DotMatch Pro is the commercial assay reliability workbench for teams that need +run registries, signed reports, private assay specs, audit trails, and support. +The open-source DotMatch engine remains available under Apache-2.0. + ![DotMatch workflow: FASTQ reads and a known target table are sliced at the same read position, assigned to known short DNA targets, and written to counts, split FASTQs, QC tables, and reports.](public/dotmatch-read-assignment.svg) ## How Matching Works @@ -727,3 +733,5 @@ A short JOSS software-paper draft is available in [paper/paper.md](paper/paper.m ## License DotMatch is released under the [Apache License 2.0](LICENSE). +Trademark guidance for the DotMatch and DotMatch Pro names is documented in +[TRADEMARKS.md](TRADEMARKS.md). diff --git a/SECURITY.md b/SECURITY.md index cdfe65e3..c08a4bf9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,7 +8,11 @@ Security fixes target the latest development branch until formal releases begin. ## Reporting a Vulnerability -Please do not open a public issue for a suspected vulnerability. Use GitHub private vulnerability reporting when it is enabled for the repository. If that is unavailable, contact the maintainer through the contact method listed on the GitHub profile or package metadata. +Please do not open a public issue for a suspected vulnerability, security bug, +or data-leak concern. Report security and data-leak issues privately. Use +GitHub private vulnerability reporting when it is enabled for the repository. If +that is unavailable, contact the maintainer through the contact method listed on +the GitHub profile or package metadata. Useful reports include: @@ -19,5 +23,10 @@ Useful reports include: ## Scientific Data -Do not attach private sequencing data, patient data, human subject data, unpublished datasets, or proprietary sample sheets to public issues. Reduce reports to synthetic fixtures whenever possible. +Do not attach real FASTQ, BAM, BCL, customer assay data, private sequencing +data, patient data, human subject data, unpublished datasets, or proprietary +sample sheets to public issues. +Use synthetic or minimized reproductions whenever possible. If a reproduction +needs biological context, reduce it to the smallest non-sensitive fixture that +still demonstrates the parser, assignment, provenance, or reporting issue. diff --git a/TRADEMARKS.md b/TRADEMARKS.md new file mode 100644 index 00000000..96af3973 --- /dev/null +++ b/TRADEMARKS.md @@ -0,0 +1,18 @@ +# DotMatch Trademark Guidelines + +This repository is licensed under the Apache License 2.0. That license grants +copyright and patent rights for the code, but it does not grant trademark rights +or permission to imply endorsement. + +The names "DotMatch" and "DotMatch Pro", associated logos, and related product +branding are reserved by the project maintainer. You may use the DotMatch name +truthfully to describe compatibility, forks, packages, citations, and +contributions, for example "built with DotMatch" or "compatible with DotMatch". + +Do not use DotMatch or DotMatch Pro branding in a way that suggests your +project, service, benchmark, or organization is official, endorsed, certified, +or commercially supported by the maintainer unless you have written permission. + +Forks and redistributions should make material changes clear and should not use +DotMatch Pro branding for a separate commercial service. These trademark +guidelines do not change the Apache-2.0 license for the code. diff --git a/docs/commercial-boundary.md b/docs/commercial-boundary.md new file mode 100644 index 00000000..bed0ea6b --- /dev/null +++ b/docs/commercial-boundary.md @@ -0,0 +1,48 @@ +# Commercial Boundary + +DotMatch uses an open-core strategy. The public repository should stay useful, +auditable, and scientifically credible on its own. Commercial work should add +team, provenance, support, and evidence-management capabilities without +crippling the open-source engine or changing its deterministic assignment +semantics. + +The open-source DotMatch project includes: + +- the deterministic assignment engine and CLI; +- local CRISPR guide counting, barcode demultiplexing, panel checking, and + fixed-window workflows; +- public report generation and machine-readable outputs; +- schemas, examples, tutorials, benchmark reports, and evidence boundaries; +- local workflow integration examples for reproducible pipelines. + +DotMatch Pro is the commercial assay reliability workbench for teams that need: + +- hosted or team workspaces; +- run registries and searchable run history; +- signed reports and reviewer-ready evidence packets; +- private assay registries and private assay specifications; +- enterprise connectors for storage, identity, workflow systems, and LIMS-like + handoffs; +- commercial support, onboarding, validation review, and audit packs. + +The open-source engine remains available under Apache-2.0. DotMatch Pro may +package or call the open engine, but it must not silently force assignments, +hide `ambiguous` or `none` outcomes, or weaken the public contract that read +states are reported as `unique`, `ambiguous`, `none`, or `invalid`. + +Commercial features should strengthen reliability, provenance, auditability, or +evidence-backed claims. They should not add clinical, diagnostic, FDA, CE-IVD, +patient-care, or treatment-decision claims unless the project maintainer +explicitly changes the documented scope. + +## Repository Readiness Checklist + +Use this checklist before publishing public-repo changes that affect release, +governance, documentation, or commercial boundary wording: + +- license present and still Apache-2.0; +- security policy present; +- no raw customer assay data, private FASTQ/BAM/BCL data, or unminimized + biological inputs committed; +- docs build passes with `make docs-ready`; +- tests pass with the relevant native, CLI, and Python commands for the change. diff --git a/docs/evidence-packet-v1.md b/docs/evidence-packet-v1.md new file mode 100644 index 00000000..dd9b01f7 --- /dev/null +++ b/docs/evidence-packet-v1.md @@ -0,0 +1,34 @@ +# Evidence Packet v1 + +An evidence packet is a reviewer- and customer-ready bundle that explains what +was run, which deterministic assignment rules were used, what evidence was +produced, and where ambiguity or failure modes remain visible. This document +defines the public v1 shape so the open-source engine and DotMatch Pro can use +the same reliability language. + +Evidence packets should include: + +- run identity: DotMatch version, command, input manifest, timestamps, host + platform, and workflow context; +- assay specification: target table, barcode or guide metadata, fixed-window + coordinates, distance metric, correction radius, and quality filters; +- deterministic outcomes: counts for `unique`, `ambiguous`, `none`, and + `invalid` reads, with no silently forced assignments; +- provenance: hashes or immutable references for public or synthetic inputs, + generated TSV/JSON/HTML outputs, and report build commands; +- safety checks: duplicate targets, ambiguous near-neighbors, invalid windows, + unsafe correction radii, and panel-collision findings when applicable; +- claim boundary: what the packet supports, what it does not support, and which + public evidence gate or benchmark report is relevant; +- review artifacts: summary tables, top-unmatched tables, panel or target + audits, QC summaries, and a human-readable report. + +Evidence packets must not include private customer FASTQ/BAM/BCL files or other +raw biological data in the public repository. Public examples should use +synthetic, minimized, or public reference data with source and scope clearly +documented. + +DotMatch Pro may add signing, team review state, private assay registry links, +access controls, and audit-pack export around this public shape. Those additions +should preserve the open engine's assignment semantics and make provenance +clearer, not less inspectable. diff --git a/docs/index.md b/docs/index.md index 9282cce8..ef1df87e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,6 +33,9 @@ need their own gates before they are public claims. - CRISPR users can follow the [first-run CRISPR guide-counting tutorial](tutorials/crispr-count-first-run.md). - Labs evaluating scientific claims should read [Trust, Scope, and Evidence](trust-and-scope.md). - Workflow and pipeline authors should use the [public output schemas](schemas.md). +- Teams evaluating the open-core boundary should read + [Commercial Boundary](commercial-boundary.md) and + [Evidence Packet v1](evidence-packet-v1.md). ## Core Ideas @@ -73,6 +76,8 @@ packaging release-process workflow-submissions resubmission-evidence +commercial-boundary +evidence-packet-v1 ``` ```{toctree} diff --git a/python/tests/test_check_repository_ready.py b/python/tests/test_check_repository_ready.py index dc557531..ee7064d6 100644 --- a/python/tests/test_check_repository_ready.py +++ b/python/tests/test_check_repository_ready.py @@ -19,19 +19,30 @@ def _write_minimal_repo(root: Path) -> None: "README.md": ( "# DotMatch\n\n" "`v0.1.0` includes stable release artifacts.\n\n" + "## DotMatch Pro\n\n" + "DotMatch Pro is the commercial assay reliability workbench for teams that need " + "run registries, signed reports, private assay specs, audit trails, and support.\n" + "The open-source DotMatch engine remains available under Apache-2.0.\n\n" "See the docs directory for packaging, release, and benchmark notes.\n" ), "CHANGELOG.md": "# Changelog\n\n## 0.1.0\n\n- Initial release.\n", - "LICENSE": "Apache-2.0\n", + "LICENSE": "Apache License\nVersion 2.0\n", + "NOTICE": "DotMatch\n\nApache License, Version 2.0\nDotMatch Pro\n", "CITATION.cff": "cff-version: 1.2.0\ntitle: DotMatch\nversion: \"0.1.0\"\n", "codemeta.json": ( '{"name": "DotMatch", "license": "https://spdx.org/licenses/Apache-2.0", ' '"version": "0.1.0", "softwareVersion": "0.1.0", "keywords": ["bioinformatics"]}\n' ), - "CONTRIBUTING.md": "# Contributing\n", + "CONTRIBUTING.md": "# Contributing\n\nNormal contributions use the same Apache-2.0 terms unless otherwise agreed.\n", "CODE_OF_CONDUCT.md": "# Code of Conduct\n", - "SECURITY.md": "# Security\n", + "SECURITY.md": ( + "# Security\n\n" + "Do not attach real FASTQ, BAM, BCL, customer assay data to public issues.\n" + "Use synthetic or minimized reproductions.\n" + "Report security and data-leak issues privately.\n" + ), "SUPPORT.md": "# Support\n", + "TRADEMARKS.md": "# Trademarks\n\nDotMatch and DotMatch Pro branding do not change the Apache-2.0 license.\n", "pyproject.toml": "[project]\nname = \"dotmatch\"\nversion = \"0.1.0\"\nlicense = \"Apache-2.0\"\n", "package.json": '{"version": "0.1.0", "license": "Apache-2.0"}\n', "MANIFEST.in": "include src/qdalign.c\ninclude include/qdalign.h\n", @@ -51,6 +62,7 @@ def _write_minimal_repo(root: Path) -> None: "- [ ] `make test`\n" "- [ ] `make cli-test`\n" "- [ ] `make python-test`\n" + "- [ ] `make repository-ready`\n" "- [ ] `make pretag-ready` if release surfaces changed.\n" "- [ ] `make asan` if native C code changed.\n" "- [ ] `make scientific-readiness-ready` if claims or evidence changed.\n" @@ -74,6 +86,15 @@ def _write_minimal_repo(root: Path) -> None: "Current native comparator: Edlib exhaustive global edit-distance assignment.\n" ), "docs/schemas.md": "# Schemas\n", + "docs/commercial-boundary.md": ( + "# Commercial Boundary\n\n" + "Open source includes the deterministic assignment engine and CLI.\n" + "Pro includes hosted or team workspaces, run registries, signed reports, " + "private assay registries, enterprise connectors, and commercial support.\n" + "Checklist: license present and still Apache-2.0; security policy present; " + "no raw customer assay data; docs build passes with `make docs-ready`; tests pass.\n" + ), + "docs/evidence-packet-v1.md": "# Evidence Packet v1\n\nunique ambiguous none invalid private customer FASTQ/BAM/BCL\n", "examples/workflows/galaxy/dotmatch_crispr_count.xml": "\n", "examples/workflows/multiqc/multiqc_config.yaml": "custom_data:\n dotmatch_sample_qc:\n", "examples/workflows/nf-core/README.md": "# nf-core-style Module Candidate\n", @@ -330,3 +351,51 @@ def test_repository_ready_rejects_local_absolute_paths(tmp_path): result = checker.audit(tmp_path) assert any("local absolute path" in failure and "example.csv" in failure for failure in result.failures) + + +def test_repository_ready_reports_missing_open_core_governance(tmp_path): + checker = _load_checker() + _write_minimal_repo(tmp_path) + (tmp_path / "TRADEMARKS.md").unlink() + (tmp_path / "docs" / "commercial-boundary.md").unlink() + + result = checker.audit(tmp_path) + + assert any("TRADEMARKS.md" in failure for failure in result.failures) + assert any("docs/commercial-boundary.md" in failure for failure in result.failures) + + +def test_repository_ready_rejects_security_policy_without_raw_data_warning(tmp_path): + checker = _load_checker() + _write_minimal_repo(tmp_path) + (tmp_path / "SECURITY.md").write_text("# Security\n\nReport bugs.\n", encoding="utf-8") + + result = checker.audit(tmp_path) + + assert any("Do not attach real FASTQ, BAM, BCL, customer assay data" in failure for failure in result.failures) + assert any("Use synthetic or minimized reproductions" in failure for failure in result.failures) + assert any("Report security and data-leak issues privately" in failure for failure in result.failures) + + +def test_repository_ready_rejects_unapproved_raw_data_paths(tmp_path): + checker = _load_checker() + _write_minimal_repo(tmp_path) + raw = tmp_path / "private_runs" / "customer.fastq" + raw.parent.mkdir(parents=True) + raw.write_text("@r1\nACGT\n+\nIIII\n", encoding="utf-8") + + result = checker.audit(tmp_path) + + assert any("raw biological data fixture is outside approved public/synthetic paths" in failure for failure in result.failures) + + +def test_repository_ready_allows_public_synthetic_raw_data_paths(tmp_path): + checker = _load_checker() + _write_minimal_repo(tmp_path) + raw = tmp_path / "examples" / "workflows" / "fixtures" / "sample_a.fastq" + raw.parent.mkdir(parents=True, exist_ok=True) + raw.write_text("@r1\nACGT\n+\nIIII\n", encoding="utf-8") + + result = checker.audit(tmp_path) + + assert not any("raw biological data fixture" in failure for failure in result.failures) diff --git a/scripts/check_repository_ready.py b/scripts/check_repository_ready.py index 5f4a0fbe..d09b5fd4 100644 --- a/scripts/check_repository_ready.py +++ b/scripts/check_repository_ready.py @@ -16,12 +16,14 @@ "README.md", "CHANGELOG.md", "LICENSE", + "NOTICE", "CITATION.cff", "codemeta.json", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "SECURITY.md", "SUPPORT.md", + "TRADEMARKS.md", "pyproject.toml", "package.json", "MANIFEST.in", @@ -47,6 +49,8 @@ "docs/native-comparator-scope.md", "docs/packaging.md", "docs/schemas.md", + "docs/commercial-boundary.md", + "docs/evidence-packet-v1.md", "examples/workflows/galaxy/dotmatch_crispr_count.xml", "examples/workflows/multiqc/multiqc_config.yaml", "examples/workflows/nf-core/README.md", @@ -90,6 +94,22 @@ for prefix in LOCAL_ABSOLUTE_PATH_PREFIXES ] +RAW_DATA_SUFFIXES = (".fastq", ".fq", ".fastq.gz", ".fq.gz", ".bam", ".bcl") +ALLOWED_RAW_DATA_PREFIXES = ( + "demo-data/", + "examples/barcode_autopsy/failure_modes/", + "examples/workflows/fixtures/", + "examples/workflows/galaxy/test-data/", + "examples/workflows/nf-core/upstream/modules/nf-core/dotmatch/", + "benchmarks/real/data/", +) +PRIVATE_DATA_MARKERS = [ + re.compile(rb"\bcustomer\b", re.IGNORECASE), + re.compile(rb"\bpatient\b", re.IGNORECASE), + re.compile(rb"\bPHI\b", re.IGNORECASE), + re.compile(rb"\bproprietary\b", re.IGNORECASE), +] + @dataclass class AuditResult: @@ -127,7 +147,7 @@ def repository_files(root: Path) -> list[Path]: stderr=subprocess.PIPE, check=True, ) - return [path for item in proc.stdout.split(b"\0") if item and (path := root / item.decode()).exists()] + return [path for item in proc.stdout.split(b"\0") if item and (path := root / item.decode()).is_file()] files: list[Path] = [] for path in root.rglob("*"): @@ -282,6 +302,93 @@ def check_pull_request_template(root: Path, result: AuditResult) -> None: result.passed.append("pull request template evidence checklist present") +def _require_text(path: Path, needles: list[str], result: AuditResult) -> None: + rel_path = path.relative_to(path.parents[1]).as_posix() if path.parent.name == "docs" else path.name + try: + text = path.read_text(encoding="utf-8") + except OSError as exc: + result.failures.append(f"{rel_path} could not be read: {exc}") + return + for needle in needles: + if needle not in text: + result.failures.append(f"{rel_path} must include: {needle}") + + +def check_open_core_governance(root: Path, result: AuditResult) -> None: + failures_before = len(result.failures) + + try: + license_text = (root / "LICENSE").read_text(encoding="utf-8") + if "Apache License" not in license_text or "Version 2.0" not in license_text: + result.failures.append("LICENSE must remain Apache License 2.0") + except OSError as exc: + result.failures.append(f"LICENSE could not be read: {exc}") + + _require_text( + root / "CONTRIBUTING.md", + ["same Apache-2.0 terms", "unless otherwise agreed"], + result, + ) + _require_text( + root / "SECURITY.md", + [ + "Do not attach real FASTQ, BAM, BCL, customer assay data", + "Use synthetic or minimized reproductions", + "Report security and data-leak issues privately", + ], + result, + ) + _require_text( + root / "TRADEMARKS.md", + [ + "DotMatch", + "DotMatch Pro", + "do not change the Apache-2.0 license", + ], + result, + ) + _require_text( + root / "NOTICE", + ["Apache License, Version 2.0", "DotMatch Pro"], + result, + ) + _require_text( + root / "README.md", + [ + "## DotMatch Pro", + "DotMatch Pro is the commercial assay reliability workbench for teams that need", + "The open-source DotMatch engine remains available under Apache-2.0.", + ], + result, + ) + _require_text( + root / "docs" / "commercial-boundary.md", + [ + "the deterministic assignment engine and CLI", + "hosted or team workspaces", + "run registries", + "signed reports", + "private assay registries", + "enterprise connectors", + "commercial support", + "license present and still Apache-2.0", + "security policy present", + "no raw customer assay data", + "docs build passes with `make docs-ready`", + "tests pass", + ], + result, + ) + _require_text( + root / "docs" / "evidence-packet-v1.md", + ["unique", "ambiguous", "none", "invalid", "private customer FASTQ/BAM/BCL"], + result, + ) + + if len(result.failures) == failures_before: + result.passed.append("open-core governance files present") + + def check_repository_tree(root: Path, result: AuditResult) -> None: files = repository_files(root) total = 0 @@ -323,6 +430,40 @@ def check_no_local_absolute_paths(root: Path, result: AuditResult) -> None: result.passed.append("no local absolute paths in repository files") +def _has_raw_data_suffix(relative: str) -> bool: + return relative.endswith(RAW_DATA_SUFFIXES) + + +def check_no_private_raw_data(root: Path, result: AuditResult) -> None: + offenders: list[str] = [] + marker_offenders: list[str] = [] + for path in repository_files(root): + relative = rel(path, root) + if not _has_raw_data_suffix(relative): + continue + if not relative.startswith(ALLOWED_RAW_DATA_PREFIXES): + offenders.append(relative) + continue + try: + sample = path.read_bytes()[:4096] + except OSError as exc: + result.failures.append(f"could not read raw-data fixture {relative}: {exc}") + continue + if any(pattern.search(sample) for pattern in PRIVATE_DATA_MARKERS): + marker_offenders.append(relative) + + for relative in offenders[:20]: + result.failures.append(f"raw biological data fixture is outside approved public/synthetic paths: {relative}") + if len(offenders) > 20: + result.failures.append(f"raw biological data fixture is outside approved paths: {len(offenders) - 20} more files") + for relative in marker_offenders[:20]: + result.failures.append(f"raw-data fixture contains private/customer marker: {relative}") + if len(marker_offenders) > 20: + result.failures.append(f"raw-data fixture contains private/customer marker: {len(marker_offenders) - 20} more files") + if not offenders and not marker_offenders: + result.passed.append("no private raw-data fixtures detected") + + def audit(root: Path) -> AuditResult: root = root.resolve() result = AuditResult() @@ -333,8 +474,10 @@ def audit(root: Path) -> AuditResult: check_readme_distribution_status(root, result) check_manifest(root, result) check_pull_request_template(root, result) + check_open_core_governance(root, result) check_repository_tree(root, result) check_no_local_absolute_paths(root, result) + check_no_private_raw_data(root, result) return result From 54b5c3799f4822fbc8f42b6cc64f07a937d77926 Mon Sep 17 00:00:00 2001 From: Donncha O'Toole Date: Mon, 29 Jun 2026 15:35:58 +0100 Subject: [PATCH 002/104] Add streaming API and industry exposure surfaces --- README.md | 37 +++ app/globals.css | 139 ++++++++- app/layout.tsx | 7 +- app/page.tsx | 145 +++++++++ app/robots.ts | 15 + app/sitemap.ts | 18 ++ docs/assayspec.md | 8 +- docs/citation-flywheel.md | 3 +- docs/index.md | 5 + docs/industry-exposure.md | 146 +++++++++ docs/proposals-and-roadmap.md | 2 +- docs/schemas.md | 8 + docs/streaming-api.md | 59 ++++ package-lock.json | 7 +- python/dotmatch/__init__.py | 16 + python/dotmatch/assayspec.py | 232 ++++++++++++++ python/dotmatch/core.py | 325 +++++++++++++++++++- python/tests/test_assayspec.py | 25 ++ python/tests/test_check_repository_ready.py | 1 + python/tests/test_tl_and_integrations.py | 51 +++ scripts/check_repository_ready.py | 4 + scripts/check_site_assets.mjs | 65 +++- tsconfig.json | 3 + 23 files changed, 1306 insertions(+), 15 deletions(-) create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 docs/industry-exposure.md create mode 100644 docs/streaming-api.md diff --git a/README.md b/README.md index d2b70fcf..d0175880 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,23 @@ release features are only described as publicly available after the matching package version passes the install smoke tests in [Packaging Notes](docs/packaging.md). +## Industry Evaluation and Exposure + +If you are evaluating DotMatch for a core facility, CRISPR screen team, assay +development group, or workflow project, start with the public homepage and the +scoped handoff documents: + +- [Homepage](https://dnncha.github.io/dotmatch): short positioning and audience + routes for known-target sequencing assignment. +- [Industry Exposure Kit](docs/industry-exposure.md): the five highest-leverage + adoption moves, copy-paste outreach, and claim guardrails. +- [Workflow Submission Pack](docs/workflow-submissions.md): nf-core, MultiQC, + Galaxy, and Snakemake handoff checklist. +- [Methods and Citation](docs/methods-and-citation.md): copyable language for + reports, manuscripts, and release-specific citation. +- [Adopter Notes](docs/adopters/README.md): rules for public, quote-approved + used-by records. + ## DotMatch Pro DotMatch Pro is the commercial assay reliability workbench for teams that need @@ -579,6 +596,26 @@ The Python API also defaults to radius-safe assignment. Pass `policy="best"` to `assign`, `Matcher.assign`, or `Matcher.assign_with_stats` only for explicit best-distance compatibility. +For notebook or workflow glue that needs FASTQ-scale iteration without loading +all reads into Python memory, use the streaming helper: + +```python +import dotmatch + +rows = dotmatch.stream_assign( + "reads.fastq.gz", + "guides.tsv", + target_start=23, + target_length=20, + k=1, +) +summary = dotmatch.write_assignments_tsv(rows, "assignments.tsv") +``` + +`stream_assign` yields one `StreamAssignment` per read in FASTQ order, including +`invalid` rows when the requested window cannot be extracted. See +[docs/streaming-api.md](docs/streaming-api.md). + Optional ecosystem extras (install e.g. `pip install "dotmatch[anndata]"`): ```python diff --git a/app/globals.css b/app/globals.css index 11fd9a78..1162f1e8 100644 --- a/app/globals.css +++ b/app/globals.css @@ -315,6 +315,12 @@ h3 { background: #fff; } +.audience-section { + background: + linear-gradient(180deg, #ffffff 0%, #f5f8fb 100%), + #fff; +} + .context-rail { flex-wrap: wrap; gap: 0.56rem; @@ -351,6 +357,54 @@ h3 { font-weight: 800; } +.audience-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 1rem; +} + +.audience-grid article { + display: flex; + min-width: 0; + min-height: 250px; + flex-direction: column; + padding: 1.15rem; + border: 1px solid var(--line); + border-top: 5px solid var(--blue); + border-radius: 8px; + background: #fff; +} + +.audience-grid article:nth-child(2) { + border-top-color: var(--green); +} + +.audience-grid article:nth-child(3) { + border-top-color: var(--amber); +} + +.audience-grid article:nth-child(4) { + border-top-color: var(--purple); +} + +.audience-grid h3 { + margin-bottom: 0.72rem; +} + +.audience-grid p { + color: var(--muted); + font-size: 0.98rem; + line-height: 1.56; +} + +.audience-grid a { + margin-top: auto; + padding-top: 1rem; + color: var(--green-strong); + font-size: 0.94rem; + font-weight: 780; +} + .evidence-section { background: linear-gradient(180deg, #f7fbf9, #ffffff 72%), @@ -405,6 +459,74 @@ h3 { color: var(--green); } +.exposure-section { + background: #fff; +} + +.exposure-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(300px, 0.36fr); + gap: 1rem; + align-items: start; +} + +.exposure-list { + display: grid; + gap: 1px; + margin: 0; + padding: 0; + border: 1px solid var(--line); + background: var(--line); + list-style: none; +} + +.exposure-list li { + display: grid; + grid-template-columns: minmax(120px, 0.22fr) minmax(0, 1fr); + gap: 1rem; + min-width: 0; + padding: 1rem 1.15rem; + background: #fff; +} + +.exposure-list strong { + color: var(--green-strong); + font-size: 1rem; + line-height: 1.35; +} + +.exposure-list p { + color: var(--muted); + font-size: 0.98rem; + line-height: 1.52; +} + +.exposure-links { + display: grid; + gap: 0.7rem; + padding: 1rem; + border: 1px solid var(--line); + border-radius: 8px; + background: #f7fbf9; +} + +.exposure-links a { + display: flex; + align-items: center; + min-height: 48px; + padding: 0.85rem; + border: 1px solid rgba(15, 107, 87, 0.18); + border-radius: 7px; + background: #fff; + font-size: 0.94rem; + font-weight: 760; +} + +.exposure-links a:hover { + color: var(--green); + border-color: rgba(15, 107, 87, 0.38); +} + .install-section { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(420px, 1.1fr); @@ -520,11 +642,13 @@ h3 { } .outcome-grid, - .failure-grid { + .failure-grid, + .audience-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .evidence-layout { + .evidence-layout, + .exposure-layout { grid-template-columns: 1fr; } } @@ -558,6 +682,11 @@ h3 { grid-template-columns: 1fr; } + .exposure-list li { + grid-template-columns: 1fr; + gap: 0.38rem; + } + .outcome-card { min-height: 128px; } @@ -591,12 +720,14 @@ h3 { } .outcome-grid, - .failure-grid { + .failure-grid, + .audience-grid { grid-template-columns: 1fr; } .failure-grid article, - .workflow-grid article { + .workflow-grid article, + .audience-grid article { min-height: auto; } diff --git a/app/layout.tsx b/app/layout.tsx index 8dbdcfa6..bf0726af 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -30,7 +30,12 @@ export const metadata: Metadata = { "barcode demultiplexing", "barcode panel design", "barcode troubleshooting", - "FASTQ sequence matching" + "FASTQ sequence matching", + "nf-core", + "MultiQC", + "Galaxy workflows", + "Snakemake", + "core facility sequencing" ], openGraph: { title: "DotMatch - Assignment Reliability", diff --git a/app/page.tsx b/app/page.tsx index abc72b46..ce289ea8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,11 +3,41 @@ const scientificClaimsUrl = `${repoUrl}/blob/main/docs/scientific-claims.md`; const evidenceGalleryUrl = `${repoUrl}/blob/main/docs/evidence-gallery/README.md`; const methodsUrl = `${repoUrl}/blob/main/docs/methods-and-citation.md`; const packagingUrl = `${repoUrl}/blob/main/docs/packaging.md`; +const exposureUrl = `${repoUrl}/blob/main/docs/industry-exposure.md`; +const workflowSubmissionsUrl = `${repoUrl}/blob/main/docs/workflow-submissions.md`; +const adoptersUrl = `${repoUrl}/blob/main/docs/adopters/README.md`; const pypiUrl = "https://pypi.org/project/dotmatch/"; const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; const assignmentWorkflowImage = `${basePath}/dotmatch-read-assignment.svg`; +const structuredData = { + "@context": "https://schema.org", + "@graph": [ + { + "@type": "WebSite", + "@id": "https://dnncha.github.io/dotmatch/#website", + name: "DotMatch", + url: "https://dnncha.github.io/dotmatch", + description: + "DotMatch is a deterministic known-target sequencing assignment toolkit for CRISPR guides, inline barcodes, feature tags, primers, and panel targets." + }, + { + "@type": "SoftwareApplication", + "@id": "https://dnncha.github.io/dotmatch/#software", + name: "DotMatch", + applicationCategory: "Bioinformatics software", + operatingSystem: "Linux, macOS", + softwareHelp: "https://dotmatch.readthedocs.io/", + codeRepository: repoUrl, + license: `${repoUrl}/blob/main/LICENSE`, + programmingLanguage: ["C", "Python", "R"], + description: + "DotMatch assigns fixed read windows to known short DNA targets and reports unique, ambiguous, none, and invalid outcomes for auditable sequencing workflows." + } + ] +}; + const outcomes = [ ["unique", "Exactly one target is compatible, so the read can be counted or written to the matching output."], ["ambiguous", "More than one target is compatible, so DotMatch keeps the read out of forced calls."], @@ -59,6 +89,37 @@ const workflowSteps = [ } ] as const; +const audienceRoutes = [ + { + title: "Core facilities", + body: + "Use DotMatch when sample barcodes, guide libraries, or panel targets need visible ambiguity and unmatched-read review before a result leaves the core.", + link: "Start with barcode troubleshooting", + href: `${repoUrl}/blob/main/docs/crispr-qc.md` + }, + { + title: "CRISPR screen teams", + body: + "Count known guide windows, keep MAGeCK-compatible outputs, and preserve assignment failures for methods review and downstream screen analysis.", + link: "Run the CRISPR tutorial", + href: `${repoUrl}/blob/main/docs/tutorials/crispr-count-first-run.md` + }, + { + title: "Workflow maintainers", + body: + "Wrap stable TSV, JSON, FASTQ, and HTML artifacts in nf-core, Galaxy, Snakemake, MultiQC, or institutional pipeline templates.", + link: "Use the submission pack", + href: workflowSubmissionsUrl + }, + { + title: "Assay developers", + body: + "Design and audit barcode panels, test correction radius safety, and export lab-ready panel records before sequencing starts.", + link: "Review panel design", + href: `${repoUrl}/blob/main/docs/barcode-panel-design.md` + } +] as const; + const contexts = [ "CRISPR guides", "inline barcodes", @@ -74,9 +135,41 @@ const evidenceLinks = [ ["Packaging notes", packagingUrl] ] as const; +const exposureActions = [ + { + title: "Evaluate", + body: + "Install the released package, run the tutorial, and compare the explicit assignment outcomes against your current known-target workflow." + }, + { + title: "Integrate", + body: + "Use the workflow submission pack to make DotMatch visible in nf-core, MultiQC, Galaxy, Snakemake, and institutional pipeline reports." + }, + { + title: "Cite", + body: + "Copy methods language from the citation guidance so external reports describe assignment windows, ambiguity policy, and software version." + }, + { + title: "Pilot", + body: + "Record quote-approved external pilots only after a public lab, workflow, or package integration can be linked and reviewed." + }, + { + title: "Share", + body: + "Use the industry exposure kit for conference abstracts, repository announcements, short social copy, and direct maintainer outreach." + } +] as const; + export default function Home() { return ( <> +