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.
- segovia-0.1.0/.claude/hooks/publishing_reminder.py +45 -0
- segovia-0.1.0/.claude/settings.json +14 -0
- segovia-0.1.0/.claude/skills/README.md +12 -0
- segovia-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- segovia-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- segovia-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
- segovia-0.1.0/.github/pull_request_template.md +19 -0
- segovia-0.1.0/.github/workflows/ci.yml +55 -0
- segovia-0.1.0/.github/workflows/release.yml +96 -0
- segovia-0.1.0/.gitignore +33 -0
- segovia-0.1.0/CHANGELOG.md +24 -0
- segovia-0.1.0/CITATION.cff +26 -0
- segovia-0.1.0/CLAUDE.md +193 -0
- segovia-0.1.0/CODE_OF_CONDUCT.md +35 -0
- segovia-0.1.0/CONTRIBUTING.md +40 -0
- segovia-0.1.0/Cargo.lock +653 -0
- segovia-0.1.0/Cargo.toml +28 -0
- segovia-0.1.0/LICENSE +662 -0
- segovia-0.1.0/PKG-INFO +202 -0
- segovia-0.1.0/README.md +175 -0
- segovia-0.1.0/ROADMAP.md +43 -0
- segovia-0.1.0/SECURITY.md +13 -0
- segovia-0.1.0/assets/articulo-linkedin-segovia.txt +53 -0
- segovia-0.1.0/assets/logo_render.py +101 -0
- segovia-0.1.0/assets/segovia-cover.png +0 -0
- segovia-0.1.0/assets/segovia-cover.svg +42 -0
- segovia-0.1.0/assets/segovia-feed.png +0 -0
- segovia-0.1.0/assets/segovia-feed.svg +39 -0
- segovia-0.1.0/assets/segovia-social.png +0 -0
- segovia-0.1.0/docs/architecture/ARD.md +152 -0
- segovia-0.1.0/docs/architecture/adr/0001-language-rust.md +30 -0
- segovia-0.1.0/docs/architecture/adr/0002-cpu-not-gpu.md +27 -0
- segovia-0.1.0/docs/architecture/adr/0003-reuse-storage-primitives.md +23 -0
- segovia-0.1.0/docs/architecture/adr/0004-python-bridge-pyo3.md +28 -0
- segovia-0.1.0/docs/architecture/adr/0005-format-priority.md +24 -0
- segovia-0.1.0/docs/architecture/adr/0006-concurrency-model.md +29 -0
- segovia-0.1.0/docs/architecture/adr/0007-packaging-strategy.md +32 -0
- segovia-0.1.0/docs/architecture/adr/0008-domain-neutral-core-verticals.md +65 -0
- segovia-0.1.0/docs/architecture/candidate-architectures.md +166 -0
- segovia-0.1.0/docs/architecture/roadmap.md +100 -0
- segovia-0.1.0/docs/architecture/rust-neuro-research.md +322 -0
- segovia-0.1.0/docs/architecture/tech-stack.md +65 -0
- segovia-0.1.0/docs/brand/visual-identity.md +101 -0
- segovia-0.1.0/docs/future/leukemia-direction.md +125 -0
- segovia-0.1.0/pyproject.toml +50 -0
- segovia-0.1.0/release.toml +13 -0
- segovia-0.1.0/src/core.rs +1 -0
- segovia-0.1.0/src/ephys/meta.rs +133 -0
- segovia-0.1.0/src/ephys/reader.rs +200 -0
- segovia-0.1.0/src/ephys.rs +2 -0
- segovia-0.1.0/src/lib.rs +114 -0
- segovia-0.1.0/tests/test_spike.py +16 -0
- 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,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,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
|
segovia-0.1.0/.gitignore
ADDED
|
@@ -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
|
segovia-0.1.0/CLAUDE.md
ADDED
|
@@ -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.
|