segovia 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. segovia-0.1.0/.claude/hooks/publishing_reminder.py +45 -0
  2. segovia-0.1.0/.claude/settings.json +14 -0
  3. segovia-0.1.0/.claude/skills/README.md +12 -0
  4. segovia-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  5. segovia-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. segovia-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  7. segovia-0.1.0/.github/pull_request_template.md +19 -0
  8. segovia-0.1.0/.github/workflows/ci.yml +55 -0
  9. segovia-0.1.0/.github/workflows/release.yml +96 -0
  10. segovia-0.1.0/.gitignore +33 -0
  11. segovia-0.1.0/CHANGELOG.md +24 -0
  12. segovia-0.1.0/CITATION.cff +26 -0
  13. segovia-0.1.0/CLAUDE.md +193 -0
  14. segovia-0.1.0/CODE_OF_CONDUCT.md +35 -0
  15. segovia-0.1.0/CONTRIBUTING.md +40 -0
  16. segovia-0.1.0/Cargo.lock +653 -0
  17. segovia-0.1.0/Cargo.toml +28 -0
  18. segovia-0.1.0/LICENSE +662 -0
  19. segovia-0.1.0/PKG-INFO +202 -0
  20. segovia-0.1.0/README.md +175 -0
  21. segovia-0.1.0/ROADMAP.md +43 -0
  22. segovia-0.1.0/SECURITY.md +13 -0
  23. segovia-0.1.0/assets/articulo-linkedin-segovia.txt +53 -0
  24. segovia-0.1.0/assets/logo_render.py +101 -0
  25. segovia-0.1.0/assets/segovia-cover.png +0 -0
  26. segovia-0.1.0/assets/segovia-cover.svg +42 -0
  27. segovia-0.1.0/assets/segovia-feed.png +0 -0
  28. segovia-0.1.0/assets/segovia-feed.svg +39 -0
  29. segovia-0.1.0/assets/segovia-social.png +0 -0
  30. segovia-0.1.0/docs/architecture/ARD.md +152 -0
  31. segovia-0.1.0/docs/architecture/adr/0001-language-rust.md +30 -0
  32. segovia-0.1.0/docs/architecture/adr/0002-cpu-not-gpu.md +27 -0
  33. segovia-0.1.0/docs/architecture/adr/0003-reuse-storage-primitives.md +23 -0
  34. segovia-0.1.0/docs/architecture/adr/0004-python-bridge-pyo3.md +28 -0
  35. segovia-0.1.0/docs/architecture/adr/0005-format-priority.md +24 -0
  36. segovia-0.1.0/docs/architecture/adr/0006-concurrency-model.md +29 -0
  37. segovia-0.1.0/docs/architecture/adr/0007-packaging-strategy.md +32 -0
  38. segovia-0.1.0/docs/architecture/adr/0008-domain-neutral-core-verticals.md +65 -0
  39. segovia-0.1.0/docs/architecture/candidate-architectures.md +166 -0
  40. segovia-0.1.0/docs/architecture/roadmap.md +100 -0
  41. segovia-0.1.0/docs/architecture/rust-neuro-research.md +322 -0
  42. segovia-0.1.0/docs/architecture/tech-stack.md +65 -0
  43. segovia-0.1.0/docs/brand/visual-identity.md +101 -0
  44. segovia-0.1.0/docs/future/leukemia-direction.md +125 -0
  45. segovia-0.1.0/pyproject.toml +50 -0
  46. segovia-0.1.0/release.toml +13 -0
  47. segovia-0.1.0/src/core.rs +1 -0
  48. segovia-0.1.0/src/ephys/meta.rs +133 -0
  49. segovia-0.1.0/src/ephys/reader.rs +200 -0
  50. segovia-0.1.0/src/ephys.rs +2 -0
  51. segovia-0.1.0/src/lib.rs +114 -0
  52. segovia-0.1.0/tests/test_spike.py +16 -0
  53. segovia-0.1.0/tests/test_spikeglx.py +81 -0
@@ -0,0 +1,45 @@
1
+ import json
2
+ import sys
3
+ import time
4
+ from pathlib import Path
5
+
6
+ CADENCE_DAYS = 7
7
+ PROJECT = "Segovia"
8
+ STATE_PATH = Path(__file__).resolve().parents[1] / "state" / "publishing_reminder.json"
9
+
10
+
11
+ def load_last_epoch():
12
+ try:
13
+ return int(json.loads(STATE_PATH.read_text(encoding="utf-8")).get("last_epoch", 0))
14
+ except Exception:
15
+ return 0
16
+
17
+
18
+ def save_epoch(epoch):
19
+ STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
20
+ STATE_PATH.write_text(json.dumps({"last_epoch": epoch}), encoding="utf-8")
21
+
22
+
23
+ def reminder_text():
24
+ return (
25
+ f"[{PROJECT}] Publishing reminder (every {CADENCE_DAYS} days): consider a milestone-only "
26
+ f"post for {PROJECT} and MaskOps - benchmark results, a release, or a tutorial, not every "
27
+ f"commit. LinkedIn: adapt assets/draft_linkedin_es.* (ES), best Tue/Wed 9-11h local, 3-5 "
28
+ f"hashtags. dev.to: a technical write-up mirroring the MaskOps cadence. Keep the honest "
29
+ f"ephys->leukemia arc (aided-by, not made-for)."
30
+ )
31
+
32
+
33
+ def main():
34
+ now = int(time.time())
35
+ last = load_last_epoch()
36
+ if last and (now - last) < CADENCE_DAYS * 86400:
37
+ print(json.dumps({"suppressOutput": True}))
38
+ return 0
39
+ save_epoch(now)
40
+ print(json.dumps({"systemMessage": reminder_text(), "suppressOutput": True}))
41
+ return 0
42
+
43
+
44
+ if __name__ == "__main__":
45
+ sys.exit(main())
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "Stop": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/publishing_reminder.py\""
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ # Project skills
2
+
3
+ Project-scoped Claude Code skills live here, one directory per skill, each with a `SKILL.md`.
4
+ They are invoked with `/<skill-name>` and are available to anyone working in this repository.
5
+
6
+ Nothing is defined yet. Likely future candidates for Segovia:
7
+
8
+ - a benchmark-runner skill (set up the SC1 gate run and collect memory/throughput numbers),
9
+ - a release skill (changelog + version bump + tag, gated on explicit approval),
10
+ - a dataset-fetch skill (pull free IBL/DANDI/SpikeGLX fixtures for tests).
11
+
12
+ Hooks (automated, non-invokable behaviors) live in `../hooks/` and are wired in `../settings.json`.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a problem with Segovia
4
+ title: ""
5
+ labels: bug
6
+ ---
7
+
8
+ **Describe the bug**
9
+ A clear description of what is wrong.
10
+
11
+ **To reproduce**
12
+ Minimal steps or code to reproduce.
13
+
14
+ **Expected behavior**
15
+ What you expected to happen.
16
+
17
+ **Environment**
18
+ - OS:
19
+ - Python version:
20
+ - Segovia version / commit:
21
+
22
+ **Additional context**
23
+ Anything else relevant — data shape, recording size, stack trace.
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Discussions
4
+ url: https://github.com/fcarvajalbrown/Segovia/discussions
5
+ about: Questions, ideas, and design discussion.
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea or improvement for Segovia
4
+ title: ""
5
+ labels: enhancement
6
+ ---
7
+
8
+ **Problem / motivation**
9
+ What problem would this solve?
10
+
11
+ **Proposed solution**
12
+ What you would like to happen.
13
+
14
+ **Alternatives considered**
15
+ Other approaches you have weighed.
16
+
17
+ **Additional context**
18
+ Anything else — links, datasets, prior art.
@@ -0,0 +1,19 @@
1
+ ## Situation
2
+ _What's the context and the problem this addresses?_
3
+
4
+ ## Task
5
+ _What does this PR set out to do?_
6
+
7
+ ## Action
8
+ _What changes were made?_
9
+
10
+ ## Result
11
+ _What's the outcome — tests, benchmarks, behavior?_
12
+
13
+ ---
14
+
15
+ - [ ] `cargo fmt --all -- --check` passes
16
+ - [ ] `cargo clippy --all-targets -- -D warnings` passes
17
+ - [ ] `cargo test` passes
18
+ - [ ] Conventional commits; one commit per logical change
19
+ - [ ] No code comments; `ROADMAP.md` / `CHANGELOG.md` updated for any user-visible change
@@ -0,0 +1,55 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ lint-test:
18
+ name: fmt + clippy + test
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+ - uses: dtolnay/rust-toolchain@stable
26
+ with:
27
+ components: rustfmt, clippy
28
+ - uses: Swatinem/rust-cache@v2
29
+ - run: cargo fmt --all -- --check
30
+ - run: cargo clippy --all-targets -- -D warnings
31
+ - run: cargo test --all
32
+
33
+ wheels:
34
+ name: build wheels (${{ matrix.os }})
35
+ needs: lint-test
36
+ runs-on: ${{ matrix.os }}
37
+ strategy:
38
+ fail-fast: false
39
+ matrix:
40
+ os: [ubuntu-latest, windows-latest, macos-latest]
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.12"
46
+ - name: Build wheels
47
+ uses: PyO3/maturin-action@v1
48
+ with:
49
+ command: build
50
+ args: --release --out dist
51
+ manylinux: auto
52
+ - uses: actions/upload-artifact@v4
53
+ with:
54
+ name: wheels-${{ matrix.os }}
55
+ path: dist
@@ -0,0 +1,96 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: release-${{ github.ref }}
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ verify-version:
16
+ name: verify tag matches Cargo.toml version
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - name: Assert git tag equals crate version
21
+ run: |
22
+ crate_version=$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')
23
+ tag="${GITHUB_REF_NAME}"
24
+ echo "tag=$tag crate_version=$crate_version"
25
+ if [ "$tag" != "v$crate_version" ]; then
26
+ echo "::error::release tag '$tag' does not match Cargo.toml version 'v$crate_version'"
27
+ exit 1
28
+ fi
29
+
30
+ publish-crate:
31
+ name: publish crate (crates.io)
32
+ needs: verify-version
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: dtolnay/rust-toolchain@stable
37
+ - uses: Swatinem/rust-cache@v2
38
+ - run: cargo publish
39
+ env:
40
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
41
+
42
+ build-wheels:
43
+ name: build wheels (${{ matrix.os }})
44
+ needs: verify-version
45
+ runs-on: ${{ matrix.os }}
46
+ strategy:
47
+ fail-fast: false
48
+ matrix:
49
+ os: [ubuntu-latest, windows-latest, macos-latest]
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - uses: actions/setup-python@v5
53
+ with:
54
+ python-version: "3.12"
55
+ - name: Build wheels
56
+ uses: PyO3/maturin-action@v1
57
+ with:
58
+ command: build
59
+ args: --release --out dist
60
+ manylinux: auto
61
+ - uses: actions/upload-artifact@v4
62
+ with:
63
+ name: wheels-${{ matrix.os }}
64
+ path: dist
65
+
66
+ build-sdist:
67
+ name: build sdist
68
+ needs: verify-version
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ - name: Build sdist
73
+ uses: PyO3/maturin-action@v1
74
+ with:
75
+ command: sdist
76
+ args: --out dist
77
+ - uses: actions/upload-artifact@v4
78
+ with:
79
+ name: sdist
80
+ path: dist
81
+
82
+ publish-pypi:
83
+ name: publish to PyPI
84
+ needs: [build-wheels, build-sdist]
85
+ runs-on: ubuntu-latest
86
+ environment:
87
+ name: pypi
88
+ url: https://pypi.org/project/segovia/
89
+ permissions:
90
+ id-token: write
91
+ steps:
92
+ - uses: actions/download-artifact@v4
93
+ with:
94
+ path: dist
95
+ merge-multiple: true
96
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,33 @@
1
+ /target
2
+ **/*.rs.bk
3
+
4
+ .venv/
5
+ venv/
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ *.pyd
11
+ *.dylib
12
+
13
+ dist/
14
+ build/
15
+ wheelhouse/
16
+ *.egg-info/
17
+ .eggs/
18
+
19
+ .pytest_cache/
20
+ .ruff_cache/
21
+ .mypy_cache/
22
+ .coverage
23
+ htmlcov/
24
+
25
+ *.profraw
26
+
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ .claude/state/
31
+ .claude/settings.local.json
32
+
33
+ .env
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-06-09
10
+
11
+ ### Added
12
+ - Initial repository scaffold: Rust crate + PyO3/maturin Python packaging, architecture docs,
13
+ AGPL-3.0-or-later license, CI, and contributor/community files.
14
+ - Day-1 zero-copy bridge spike: `segovia.zeros(channels, samples)` returns an `int16` NumPy array
15
+ backed by Rust-owned memory (no copy), plus `segovia.__version__`.
16
+ - Chunked, memory-bounded SpikeGLX reader (`segovia.SpikeGlxReader`): parses the `.meta` sidecar
17
+ (channel count, sample rate, stream type, declared file size, raw fields), memory-maps the `.bin`,
18
+ and streams it in fixed-size `(samples, channels)` `int16` chunks via `reader.chunks(chunk_samples)`
19
+ with the GIL released during each chunk copy. Sample count is derived from the **actual** `.bin`
20
+ size and validated for frame alignment; a stale or truncated meta `fileSizeBytes` is tolerated and
21
+ surfaced via the `declared_file_size_bytes` property. Validated byte-for-byte against the real
22
+ `Noise4Sam_g0` Neuropixels recording from the NEO `ephy_testing_data` corpus.
23
+
24
+ [Unreleased]: https://github.com/fcarvajalbrown/Segovia/commits/main
@@ -0,0 +1,26 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use Segovia in your research, please cite it as below."
3
+ title: "Segovia: a chunked, memory-bounded Rust engine for electrophysiology signal processing"
4
+ abstract: >-
5
+ Segovia is a lazy-evaluated, chunked, concurrent Rust compute engine for massive multi-channel
6
+ electrophysiology time-series (Neuropixels-scale), exposed to Python via PyO3 and built to
7
+ integrate with the SpikeInterface ecosystem over SpikeGLX, Zarr, and NWB. It targets bounded-memory,
8
+ out-of-core streaming preprocessing with GIL-released shared-memory threading.
9
+ type: software
10
+ authors:
11
+ - given-names: Felipe
12
+ family-names: "Carvajal Brown"
13
+ email: fcarvajalbrown@gmail.com
14
+ keywords:
15
+ - neuroscience
16
+ - electrophysiology
17
+ - neuropixels
18
+ - spike-sorting
19
+ - signal-processing
20
+ - rust
21
+ - python
22
+ - out-of-core
23
+ license: "AGPL-3.0-or-later"
24
+ repository-code: "https://github.com/fcarvajalbrown/Segovia"
25
+ version: 0.1.0
26
+ date-released: 2026-06-09
@@ -0,0 +1,193 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working in this repository.
4
+
5
+ ## How I want you to work with me
6
+
7
+ **Decisions are mine — present them interactively.** When a choice is mine to make, ALWAYS
8
+ present it as an interactive multiple-choice question I answer with the arrow keys (the
9
+ `AskUserQuestion` picker), with a recommended option marked. Never bury a decision in prose
10
+ and proceed. Never present a wall of options as plain text when the picker will do.
11
+
12
+ **Always ask, never assume.** If any detail is unclear, unspecified, or ambiguous, stop and
13
+ ask before writing code or documents. Do not guess. Do not fill gaps with assumptions and
14
+ keep going. A wrong assumption costs more than a question.
15
+
16
+ **Work sequentially and confirm direction.** Prefer one step at a time. Before any large or
17
+ hard-to-reverse change, confirm the approach with me first. Show me the plan, get a yes, then act.
18
+
19
+ **Report honestly.** If something failed, say so with the output. If a step was skipped, say
20
+ that. State what a thing does and what it does not do. No hedging, no inflation.
21
+
22
+ ## Code style
23
+
24
+ - **No comments of any kind.** No `//`, `///`, `//!`, `/* */` in Rust; no `#` comments or
25
+ docstrings in Python. Names and types are the only documentation.
26
+ - **Bug fixes target root cause only** — never patch test parameters or add workarounds to
27
+ make tests pass. Never write code just to make it compile; code must reflect real behavior.
28
+
29
+ ## Rust conventions
30
+
31
+ - `thiserror` for error types in libraries.
32
+ - `serde` + `serde_json` for serialization.
33
+ - `rayon` for parallelism.
34
+ - Release the GIL (`Python::allow_threads`) around all heavy Rust work — see the architecture docs.
35
+
36
+ ## Python / build (Windows-first)
37
+
38
+ - Always work inside a `.venv` at the project root. Never assume one exists — check or create it.
39
+ - On Windows (PowerShell): run each command separately, **no `&&`** chaining.
40
+ - `maturin develop --release` recompiles Rust + installs the editable Python package. **Re-run it
41
+ after any Rust change** before running Python or tests.
42
+ - The Rust↔Python bridge is **`pyo3` 0.28**; the scaffold compiles clean (`cargo check`). No Polars
43
+ dependency, so the MaskOps `pyo3`/`pyo3-polars` coupling lesson does not apply — but still never
44
+ bump `pyo3` blindly.
45
+ - Known CI realities to watch (precedent from my MaskOps project): Windows wheel building +
46
+ HDF5 C-library linking is painful; MaskOps had to exclude Ubuntu + Python 3.12 from the test
47
+ matrix over a `dlopen` failure. Expect platform-specific extension-load issues.
48
+
49
+ ## Commits
50
+
51
+ - Conventional commits: `<type>(<scope>): <description>` — lowercase, present-tense imperative.
52
+ Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `ci`.
53
+ - **One commit per logical change** — no layer-split commits.
54
+ - **No AI attribution anywhere** — no `Co-Authored-By`, no "Generated with Claude Code" in
55
+ commit messages, PR descriptions, or code.
56
+ - Never force-push without telling me and waiting for confirmation.
57
+
58
+ ## Branches, PRs, releases
59
+
60
+ - **Branch only for roadmap work** (a feature/fix listed on the roadmap). Tooling, config, and
61
+ housekeeping go directly to `main`.
62
+ - **PR descriptions in STAR format:** Situation / Task / Action / Result.
63
+ - **Version sources of truth — keep them in lockstep.** `Cargo.toml` `version` is the *machine*
64
+ source of truth: `pyproject.toml` carries no static version (`dynamic = ["version"]`), so `maturin`
65
+ derives the wheel version from `Cargo.toml`, and the git tag `vX.Y.Z` must equal it (CI fails the
66
+ publish on mismatch). `ROADMAP.md` is the *human* source of truth for version + scope and must
67
+ state the same number. Read `ROADMAP.md` before any version or release action.
68
+ - **Finishing a task includes updating `ROADMAP.md` and the changelog** — automatically, as the
69
+ last step of any user-visible change. Pure chore/docs/ci/tooling work touches neither.
70
+ - **A release is a deliberate roadmap event, and every approved roadmap PR ships one.** My **"yes"
71
+ to the PR is the release approval** — no separate tagging or merge approval. **PRs and branches are
72
+ a formality to keep history clean**, so Claude drives them end to end: open the PR with a
73
+ conventional-commit title (its type drives the SemVer bump) → **wait for all GitHub Actions checks
74
+ to pass** → **Claude does the squash-merge** (don't wait for me to click merge) → after verifying
75
+ `main` actually contains the squash commit, cut the release (`cargo release <level> --execute`
76
+ bumps `Cargo.toml`, rewrites the doc surfaces, tags `vX.Y.Z`, pushes) → `gh release create`. The
77
+ published release triggers `release.yml`. **Never merge on red CI, and never tag code that is not on
78
+ `main`.** Pure chore/docs/ci/tooling work never releases.
79
+ - **Announce releases via Discussions natively:** cut releases with
80
+ `gh release create <tag> --discussion-category "Announcements"` so GitHub auto-posts the release
81
+ notes as an Announcements discussion. No standing Action — it rides on the deliberate release step.
82
+
83
+ ### Versioning & release mechanics
84
+
85
+ **One number; everything else derives from it — never hand-maintain a second copy.** `Cargo.toml`
86
+ `version` is the only place a version is written. Every shipped surface reads from it, so they
87
+ *cannot* drift:
88
+
89
+ - **crates.io** ← `Cargo.toml` directly (it *is* the manifest).
90
+ - **PyPI wheel + sdist** ← `maturin` reads `Cargo.toml`, because `pyproject.toml` declares
91
+ `dynamic = ["version"]` and carries **no** static `version` field. (This is the exact MaskOps
92
+ trap — a hand-kept `pyproject` version — and it is now structurally impossible.)
93
+ - **`segovia.__version__`** ← `env!("CARGO_PKG_VERSION")` in `src/lib.rs`, i.e. `Cargo.toml` at
94
+ compile time.
95
+ - **git tag** ← `cargo-release` creates `v{{version}}` from `Cargo.toml`; then `release.yml`'s
96
+ `verify-version` job re-asserts `tag == Cargo.toml version` and **fails the publish on any
97
+ mismatch** (belt and suspenders).
98
+
99
+ The only files that hold a *literal* number are docs, and `cargo-release` rewrites them in the same
100
+ bump commit via `pre-release-replacements` (`release.toml`): `CHANGELOG.md` (`[Unreleased]` → version
101
+ + date), `CITATION.cff` (`version:` + `date-released:`), and `ROADMAP.md` (`**Version:**`). So **a
102
+ release is one command** — `cargo release <minor|patch> --execute` — and nothing is edited by hand.
103
+
104
+ - **SemVer, pre-1.0 (`0.MINOR.PATCH`):** `feat` → minor; `fix`/`perf`/`refactor` → patch; a breaking
105
+ change (`!` / `BREAKING CHANGE`) → minor while `< 1.0`. `chore`/`docs`/`ci`/`style`/`test` alone →
106
+ no release. The squash-merge's conventional-commit type selects the bump.
107
+ - **Publish pipeline:** publishing a GitHub release (`gh release create v* --discussion-category
108
+ "Announcements"`) fires `release.yml` → `verify-version` → `cargo publish` (via the
109
+ `CARGO_REGISTRY_TOKEN` secret) + maturin wheels/sdist → PyPI via Trusted Publishing (OIDC, the
110
+ `pypi` environment; no token stored). `cargo-release` itself does **not** publish (`publish = false`).
111
+ - **One-time setup state:** `release.yml`, `release.toml`, the `dynamic` pyproject switch, the
112
+ `CARGO_REGISTRY_TOKEN` secret, and the `pypi` environment are all in place. Still required on the
113
+ host that cuts a release: `cargo install cargo-release`; and PyPI's Trusted-Publishing entry for
114
+ this repo must exist before the first PyPI upload.
115
+
116
+ ## Automated publishing reminders (LinkedIn + dev.to) — ACTIVE
117
+
118
+ A **Stop hook** is wired in `.claude/settings.json` running `.claude/hooks/publishing_reminder.py`.
119
+ It surfaces a milestone-publishing reminder at most once every **7 days** (cooldown tracked in
120
+ `.claude/state/publishing_reminder.json`, gitignored) via the hook's `systemMessage` output. The
121
+ reminder covers **both Segovia and MaskOps** and nudges:
122
+
123
+ - **LinkedIn** — adapt the Spanish draft/teaser in `assets/draft_linkedin_es.*`; best **Tue/Wed
124
+ 9–11h** local; 3–5 hashtags; "saves" are the strongest signal.
125
+ - **dev.to** — a technical write-up, mirroring the MaskOps dev.to cadence.
126
+
127
+ Post **milestones only** (benchmark results, releases, tutorials) — never every commit — and keep the
128
+ honest **ephys→leukemia arc** (aided-by, not made-for; never overclaim). To change the cadence, edit
129
+ `CADENCE_DAYS` in the hook; to pause it, remove the `Stop` block from `.claude/settings.json`. Project
130
+ skills (none yet) live under `.claude/skills/`.
131
+
132
+ ## Project state (2026-06-09)
133
+
134
+ - **Repo:** https://github.com/fcarvajalbrown/Segovia (`origin`). **First runnable code landed** — the
135
+ day-1 zero-copy NumPy spike (`segovia.zeros`, `segovia.__version__`), merged via PR #2 with CI green
136
+ on Windows/macOS/Linux. The `segovia` crate is **published on crates.io at v0.0.0**
137
+ (AGPL-3.0-or-later, published manually 2026-06-09). **PyPI not yet.**
138
+ - **Scaffold:** standalone `segovia` crate with `src/` core/ephys module seams, `pyproject.toml` +
139
+ maturin packaging, **AGPL-3.0-or-later** license, `.github/` CI (fmt/clippy/test + maturin wheel
140
+ matrix) and issue/PR templates, `ROADMAP.md` (version SSoT), `CHANGELOG.md`, `CITATION.cff`,
141
+ `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, and a project `.venv`.
142
+ - **Single-cell / leukemia vertical — deep-research COMPLETE.** Verdict: the out-of-core capability
143
+ gap is already closed (BPCells in C++, Scarf in Python), so the vertical survives only as a
144
+ *differentiation* play via **path E (interop)** built on **`scverse/anndata-rs`** — NOT a SingleRust
145
+ dependency (SingleRust is in-memory). Deferred and gated to a post-ship **M12+** phase. Full verdict
146
+ and the BPCells-Python monitor live in `docs/future/leukemia-direction.md`.
147
+ - **GitHub page setup:** topics **set** ✅. Still TODO: set the About **description**, upload
148
+ `assets/segovia-social.png` as the social preview, set the Website field, enable Issues + Discussions.
149
+ - **Next up (next session):** (1) **automate package publishing** — see the TODO below; (2) M0–2
150
+ roadmap work — the SpikeGLX `.meta`/`.bin` + Zarr chunked, memory-bounded reader.
151
+
152
+ ## TODO (next session) — automate package publishing ("set it and forget it")
153
+
154
+ The crates.io publish was manual. Next session, wire publishing into CI so a deliberate release ships
155
+ both packages automatically:
156
+
157
+ - **Rotate keys.** Revoke the crates.io token used 2026-06-09 (it was pasted in chat) and create a
158
+ **new** crates.io token. For PyPI, prefer **Trusted Publishing (OIDC)** so no token is stored at all;
159
+ otherwise create a PyPI API token.
160
+ - **Store them as GitHub Actions *secrets*** (encrypted) — **not** plaintext repo "variables": e.g.
161
+ `CARGO_REGISTRY_TOKEN` (and `PYPI_API_TOKEN` if not using Trusted Publishing).
162
+ - **Add `.github/workflows/release.yml`** triggered on a `v*` tag / published GitHub Release: run
163
+ `cargo publish` for the crate and build + upload PyPI wheels via `PyO3/maturin-action`. Then a
164
+ release publishes both automatically — no manual `cargo publish`.
165
+ - Keep a release deliberate (tag = approved event), and link each release to an Announcements
166
+ discussion (`gh release create <tag> --discussion-category "Announcements"`).
167
+
168
+ ## What this is
169
+
170
+ **Segovia** — a lazy-evaluated, chunked, concurrent **Rust compute engine for massive
171
+ multi-channel electrophysiology time-series** (Neuropixels-scale: 30 kHz × thousands of
172
+ channels), exposed to Python via PyO3, integrating with the SpikeInterface ecosystem over
173
+ SpikeGLX / Zarr / NWB. The name honors **Claudio Segovia**, a friend who died of leukemia at 26,
174
+ and evokes the Aqueduct of Segovia — a continuous stream carried across a row of segmented stone
175
+ arches, the metaphor for this engine's chunked, span-by-span streaming model.
176
+
177
+ - **Target CPU, not GPU.** The workload is IO/memory-bound (~22 MB/s per probe). The value is
178
+ bounded-memory streaming preprocessing with GIL-released threading — not SIMD throughput.
179
+ - **The differentiating win** over `SpikeInterface + Zarr/Dask` is true shared-memory threading
180
+ (Rayon, no pickle/process-pool overhead) and tighter memory bounds. This must be proven by
181
+ benchmark early (the M2–4 go/no-go gate) — see `docs/architecture/`.
182
+
183
+ ## Architecture docs
184
+
185
+ Read these before substantive work:
186
+
187
+ - `docs/architecture/ARD.md` — Architecture Requirements Document.
188
+ - `docs/architecture/candidate-architectures.md` — candidate architectures, pros/cons, recommendation.
189
+ - `docs/architecture/adr/` — Architecture Decision Records (one per significant decision).
190
+ - `docs/architecture/tech-stack.md` — concrete crate choices and their sharp edges.
191
+ - `docs/architecture/roadmap.md` — 12-month milestones and the benchmark go/no-go gate.
192
+ - `docs/architecture/rust-neuro-research.md` — the fact-checked research dossier this project is founded on.
193
+ - `docs/future/leukemia-direction.md` — the deferred, gated single-cell vertical and its deep-research verdict.
@@ -0,0 +1,35 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a
6
+ harassment-free experience for everyone, regardless of age, body size, visible or invisible
7
+ disability, ethnicity, sex characteristics, gender identity and expression, level of experience,
8
+ education, socio-economic status, nationality, personal appearance, race, religion, or sexual
9
+ identity and orientation.
10
+
11
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
12
+ healthy community.
13
+
14
+ ## Our Standards
15
+
16
+ Examples of behavior that contributes to a positive environment include demonstrating empathy and
17
+ kindness, being respectful of differing opinions and experiences, giving and gracefully accepting
18
+ constructive feedback, and focusing on what is best for the community.
19
+
20
+ Examples of unacceptable behavior include the use of sexualized language or imagery, trolling,
21
+ insulting or derogatory comments, public or private harassment, publishing others' private
22
+ information without permission, and other conduct which could reasonably be considered inappropriate
23
+ in a professional setting.
24
+
25
+ ## Enforcement
26
+
27
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project
28
+ maintainer at **fcarvajalbrown@gmail.com**. All complaints will be reviewed and investigated promptly
29
+ and fairly. The maintainer is obligated to respect the privacy and security of the reporter.
30
+
31
+ ## Attribution
32
+
33
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
34
+ version 2.1, available at
35
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
@@ -0,0 +1,40 @@
1
+ # Contributing to Segovia
2
+
3
+ Thanks for your interest. Segovia is in an early, pre-implementation phase — the most useful
4
+ contributions right now are issues, design feedback, and small focused PRs.
5
+
6
+ ## Development setup (Windows-first)
7
+
8
+ A `.venv` at the project root is required. On Windows (PowerShell), run commands separately — do not
9
+ chain with `&&`.
10
+
11
+ ```powershell
12
+ python -m venv .venv
13
+ .\.venv\Scripts\Activate.ps1
14
+ pip install maturin
15
+ maturin develop --release
16
+ ```
17
+
18
+ `maturin develop --release` recompiles the Rust extension and installs the editable Python package.
19
+ **Re-run it after any Rust change** before running Python or tests.
20
+
21
+ ## Checks before opening a PR
22
+
23
+ ```powershell
24
+ cargo fmt --all
25
+ cargo clippy --all-targets -- -D warnings
26
+ cargo test
27
+ ```
28
+
29
+ ## Conventions
30
+
31
+ - **Conventional commits:** `<type>(<scope>): <description>` — lowercase, present-tense imperative.
32
+ Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `ci`. One commit per logical change.
33
+ - **No code comments.** Names and types are the documentation.
34
+ - **Bug fixes target the root cause** — no workarounds that only make tests pass.
35
+ - **PR descriptions use STAR format:** Situation / Task / Action / Result.
36
+
37
+ ## Licensing of contributions
38
+
39
+ Unless you state otherwise, any contribution you submit is licensed under
40
+ **AGPL-3.0-or-later**, the same terms as the project, without any additional conditions.