Skip to content

Commit 1fa1845

Browse files
Merge pull request #22 from Waltham-Data-Science/claude/update-dev-docs-pCV94
Establish Bridge Protocol for MATLAB-Python parity
2 parents dd39893 + 149e1f3 commit 1fa1845

6 files changed

Lines changed: 248 additions & 146 deletions

File tree

.cursorrules

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# NDI Cursor Rules
2+
3+
## 1. Project Context
4+
This is the NDI (Neuroscience Data Interface) Python port. It is a "Lead-Follow" project where the MATLAB codebase is the Source of Truth.
5+
6+
## 2. Core Instructions
7+
Before proposing or writing any code, you MUST read and adhere to the following project-specific files:
8+
9+
- **Entry Point:** Read `AGENTS.md` at the root for a high-level overview of your role.
10+
- **Porting Logic:** Read `docs/developer_notes/PYTHON_PORTING_GUIDE.md` for technical implementation rules (Pydantic, Naming, etc.).
11+
- **Parity Principles:** Read `docs/developer_notes/ndi_xlang_principles.md` for indexing vs. counting rules and data handling.
12+
- **The Contract:** Locate and read the `ndi_matlab_python_bridge.yaml` in the directory you are currently working in.
13+
14+
## 3. Strict Naming Policy
15+
Do not attempt to convert MATLAB camelCase to snake_case. Maintain the casing defined in the bridge YAML files.
16+
17+
## 4. Active Maintenance
18+
If a function is missing from the bridge YAML, add it based on the MATLAB source and notify the user: "INTERFACE UPDATE: I have modified the bridge contract for [Function Name]."

AGENTS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# AGENTS.md
2+
3+
## 1. Role & Mission
4+
5+
You are an AI Developer for the NDI (Neuroscience Data Interface) project. Your mission is to maintain 1:1 functional and semantic parity between the mature MATLAB (Source of Truth) codebase and the Python port.
6+
7+
## 2. The Mandatory Knowledge Base
8+
9+
Before proposing, writing, or refactoring any code, you MUST read the following files in order:
10+
11+
1. **Porting Protocol:** `docs/developer_notes/PYTHON_PORTING_GUIDE.md`
12+
- Focus: Technical workflow, naming rules, Pydantic validation, and linting requirements.
13+
14+
2. **Universal Principles:** `docs/developer_notes/ndi_xlang_principles.md`
15+
- Focus: High-level logic rules (e.g., 0-vs-1 indexing, Semantic Parity for scientific counting, and NumPy usage).
16+
17+
## 3. The Local Contract: The Bridge File
18+
19+
Every sub-package contains a file named `ndi_matlab_python_bridge.yaml`.
20+
21+
- **Rule 1: Consult the Bridge First.** This file defines the exact names, input arguments, and output tuples for that namespace.
22+
- **Rule 2: Active Maintenance.** If a function or class exists in MATLAB but is missing from the bridge file, you must:
23+
1. Analyze the MATLAB `.m` file.
24+
2. Add the new entry to the `ndi_matlab_python_bridge.yaml`.
25+
3. **Notify the User:** You must state: "INTERFACE UPDATE: I have modified the bridge contract for [Function Name] to reflect the MATLAB source."
26+
- **Rule 3: Strict Naming.** You are forbidden from "Pythonizing" names (e.g., changing `ListAllDocuments` to `list_all_documents`) unless the bridge file explicitly instructs you to do so in the `decision_log`.
27+
28+
## 4. Technical Constraints
29+
30+
- **Validation:** All public API functions must use the `@pydantic.validate_call` decorator.
31+
- **Counting:** Any user-facing concept (Epochs, Channels, Trials) uses 1-based counting in Python to match MATLAB.
32+
- **Internal Access:** Use 0-based indexing for internal Python data structures (lists, NumPy arrays).
33+
- **Formatting:** Code must pass `black` and `ruff check --fix` before completion.
34+
35+
## 5. Directory Mapping Reference
36+
37+
- **MATLAB Source:** `VH-Lab/NDI-matlab` (GitHub)
38+
- **Python Target:** `src/ndi/[namespace]/` (Mirrors MATLAB `+namespace/`)

PYTHON_PORTING_GUIDE.md

Lines changed: 0 additions & 146 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# NDI MATLAB to Python Porting Guide
2+
3+
## 1. The Core Philosophy: Lead-Follow Architecture
4+
5+
The MATLAB codebase is the **Source of Truth**. The Python version is a "faithful mirror." When a conflict arises between "Pythonic" style and MATLAB symmetry, **symmetry wins**.
6+
7+
- **Lead-Follow:** MATLAB defines the logic, hierarchy, and naming.
8+
- **The Contract:** Every package contains an `ndi_matlab_python_bridge.yaml`. This file is the binding contract for function names, arguments, and return types for that specific namespace.
9+
10+
## 2. Naming & Discovery (The Mirror Rule)
11+
12+
Function and class names must match MATLAB exactly.
13+
14+
- **Naming Source:** Refer to the local `ndi_matlab_python_bridge.yaml`.
15+
- **Missing Entries:** If a function is not in the bridge file, refer to the MATLAB source to determine the name, add the entry to the bridge file, and notify the user of the addition for their review.
16+
- **Case Preservation:** Use `ListAllDocuments`, not `list_all_documents`. Use `savetofile`, not `save_to_file`.
17+
- **Directory Parity:** Python file paths must mirror MATLAB `+namespace` paths (e.g., `+ndi/+cloud``src/ndi/cloud/`).
18+
19+
## 3. The Porting Workflow (The Bridge Protocol)
20+
21+
To port or update a function, agents must follow these steps:
22+
23+
1. **Check the Bridge:** Open the `ndi_matlab_python_bridge.yaml` in the target package.
24+
2. **Sync the Interface:** If the function is missing or outdated, update the YAML entry first based on the MATLAB `.m` file.
25+
3. **Implement:** Write the Python code to satisfy the `input_arguments` and `output_arguments` defined in the YAML.
26+
4. **Log & Notify:** Document intentional divergences in the YAML's `decision_log`. Explicitly tell the user what changes were made to the bridge file so they can review the contract.
27+
28+
## 4. Input Validation: Pydantic is Mandatory
29+
30+
To replicate the robustness of the MATLAB `arguments` block, use Pydantic for all public-facing API functions.
31+
32+
- **Decorator:** Use the `@pydantic.validate_call` decorator on all functions.
33+
- **Type Mirroring:**
34+
- MATLAB `double`/`numeric` → Python `float | int`
35+
- MATLAB `char`/`string` → Python `str`
36+
- MATLAB `{member1, member2}` → Python `Literal["member1", "member2"]`
37+
- **Union Types:** Implement multiple allowed types as a Type Union (e.g., `str | int`).
38+
- **Coercion:** Allow Pydantic's default casting (e.g., allowing a string `"1"` to satisfy a `bool` type).
39+
- **Arbitrary Types:** For types like `numpy.ndarray`, use `config=ConfigDict(arbitrary_types_allowed=True)`.
40+
41+
## 5. Multiple Returns (Outputs)
42+
43+
MATLAB allows multiple return values natively. In Python, these must be returned as a **tuple** in the exact order defined in the `output_arguments` section of the bridge YAML.
44+
45+
## 6. Code Style & Linting
46+
47+
All Python code must pass formatting and linting before being committed.
48+
49+
- **Black:** The sole code formatter. Use default line length (88).
50+
- **Ruff:** The primary linter. Run `ruff check --fix` before committing.
51+
52+
## 7. Error Handling & Documentation
53+
54+
- **Hard Fails:** If a MATLAB function throws an error, the Python version must raise a corresponding Exception (`ValueError`, `TypeError`, or `NDIError`).
55+
- **Docstring Symmetry:** Include the original MATLAB documentation in the Python docstring. Add a "Python-specific Notes" section at the bottom for library-specific details.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# The NDI Bridge Protocol: YAML Specification
2+
3+
# =============================================================================
4+
# 1. File Purpose & Placement
5+
# =============================================================================
6+
# Name: ndi_matlab_python_bridge.yaml
7+
# Location: One file per sub-package directory
8+
# (e.g., src/ndi/session/ndi_matlab_python_bridge.yaml).
9+
# Role: This is the Primary Contract. It defines how MATLAB names and types
10+
# map to Python. If a function is not here, it does not officially
11+
# exist in the Python port.
12+
13+
# =============================================================================
14+
# 2. Standard Header
15+
# =============================================================================
16+
# Every bridge file must begin with this metadata to orient the agent:
17+
18+
project_metadata:
19+
bridge_version: "1.1"
20+
naming_policy: "Strict MATLAB Mirror"
21+
indexing_policy: "Semantic Parity (1-based for user concepts, 0-based for internal data)"
22+
23+
# =============================================================================
24+
# 3. The "Active Maintenance" Instruction
25+
# =============================================================================
26+
# When an agent or developer works on a function:
27+
#
28+
# 1. Check: Does the function/class exist in the YAML?
29+
# 2. Add/Update: If it is missing or the MATLAB signature has changed,
30+
# update the YAML first.
31+
# 3. Notify: The agent MUST explicitly tell the user:
32+
# "I have updated the ndi_matlab_python_bridge.yaml to include
33+
# [Function Name]. Please review the interface contract."
34+
35+
# =============================================================================
36+
# 4. Structure for Classes and Functions
37+
# =============================================================================
38+
39+
# --- Example: Class ---
40+
# - name: NDI_Session # Exact MATLAB Name
41+
# type: class
42+
# matlab_path: "+ndi/Session.m"
43+
# python_path: "ndi/session.py"
44+
#
45+
# properties:
46+
# - name: reference
47+
# type_matlab: "char"
48+
# type_python: "str"
49+
# decision_log: "Mirroring property name exactly."
50+
#
51+
# methods:
52+
# - name: get_epoch_data
53+
# input_arguments:
54+
# - name: epoch_id
55+
# type_python: "int"
56+
# decision_log: "Semantic Parity: User provides 1-based ID."
57+
58+
# --- Example: Standalone Function ---
59+
# - name: ListAllDocuments
60+
# type: function
61+
# matlab_path: "+ndi/+cloud/ListAllDocuments.m"
62+
# python_path: "ndi/cloud/ListAllDocuments.py"
63+
# input_arguments:
64+
# - name: DatasetID
65+
# type_matlab: "string | numeric"
66+
# type_python: "str | int"
67+
# output_arguments:
68+
# - name: docs
69+
# type_matlab: "struct array"
70+
# type_python: "list[dict]"
71+
# decision_log: "Python returns a list of dictionaries to mimic MATLAB struct arrays."
72+
73+
# =============================================================================
74+
# 5. Summary of Field Rules
75+
# =============================================================================
76+
# Field | Rule
77+
# ------------------|----------------------------------------------------------
78+
# name | Strict Match. Case-sensitive match to the MATLAB .m file.
79+
# input_arguments | Used to generate Pydantic @validate_call.
80+
# output_arguments | Defines the order of the Return Tuple.
81+
# type_python | Use Python 3.10+ syntax (e.g., str | int).
82+
# decision_log | Mandatory for any divergence (e.g., "Shifted to
83+
# | 0-indexing for internal array access").

0 commit comments

Comments
 (0)