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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ When tests are included, they follow the same user story organization.
- JSON diagram files (existing persistence via Pydantic schemas) (014-iomarker-latex-rendering)
- TypeScript 5.9 (frontend), Python 3.11+ (backend) + React 19.2.3, React Flow 11.11.4, anywidget (Jupyter widget framework), Pydantic (schema validation) (015-block-drag-detection)
- Python 3.11+ + Pydantic 2.12+ (existing schema validation), python-control 0.10+ (existing) (017-diagram-label-indexing)
- Python 3.11+ (existing Lynx requirement) + python-control 0.10+ (existing), no new dependencies required (018-graph-pruning-extraction)

## Key Components

Expand Down Expand Up @@ -606,6 +607,7 @@ blocks/
- `js/src/test/` - Test configuration and setup files

## Recent Changes
- 018-graph-pruning-extraction: Added Python 3.11+ (existing Lynx requirement) + python-control 0.10+ (existing), no new dependencies required
- 017-diagram-label-indexing: Added Python 3.11+ + Pydantic 2.12+ (existing schema validation), python-control 0.10+ (existing)
- **015-block-drag-detection**: Intelligent drag detection with 5-pixel movement threshold
- Click-to-select (< 5px movement) vs drag-to-move (≥ 5px movement) behavior
Expand All @@ -618,7 +620,6 @@ blocks/
- Performance: < 5ms overhead per drag operation, maintains 60 FPS
- 717 total tests passing (407 Python + 310 frontend)
- Frontend-only feature (no Python backend changes required)
- **014-iomarker-latex-rendering**: Complete IOMarker LaTeX rendering with automatic indexing and Simulink-style renumbering
- Automatic index display (0, 1, 2...) via LaTeX for InputMarker and OutputMarker blocks
- Custom LaTeX override using existing useCustomLatex hook (checkbox + textarea UI)
- Removed "Input/Output" text and "Type" dropdown from parameter panel for cleaner UX
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[project]
name = "lynx-nb"
version = "0.1.4"
version = "0.1.5"
description = "Block diagram editor for control systems"
readme = "README.md"
authors = [
Expand All @@ -13,7 +13,7 @@ authors = [
requires-python = ">=3.11"
dependencies = [
"anywidget>=0.9.21",
"control>=0.10.1",
"control>=0.10.2",
"numpy>=2.4.1",
"pydantic>=2.12.5",
"traitlets>=5.14.3",
Expand Down
44 changes: 44 additions & 0 deletions specs/018-graph-pruning-extraction/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Specification Quality Checklist: Graph-Based Subsystem Extraction

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-02-01
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Validation Notes

**Pass**: All checklist items validated successfully.

- Specification is written in user-centric language without implementation details
- All 12 functional requirements are testable and measurable
- Success criteria focus on observable outcomes (TF order, execution time, state count)
- Edge cases cover boundary conditions (same block, algebraic loops, multiple paths)
- Scope clearly defines what is and isn't included
- Dependencies and assumptions documented
- No clarification markers present - all requirements are unambiguous

**Ready for**: `/speckit.plan` - proceed to implementation planning phase
123 changes: 123 additions & 0 deletions specs/018-graph-pruning-extraction/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<!--
SPDX-FileCopyrightText: 2026 Jared Callaham <jared.callaham@gmail.com>

SPDX-License-Identifier: GPL-3.0-or-later
-->

# Data Model: Graph-Based Subsystem Extraction

**Feature**: 018-graph-pruning-extraction
**Created**: 2026-02-01

## Overview

This feature operates on in-memory Diagram objects and does not introduce persistent data structures. The entities below represent transient computational structures used during extraction.

## Entities

### ConnectionGraph

**Purpose**: Directed graph representation of diagram topology for analysis

**Attributes**:
- `nodes`: Set of block IDs (str) representing graph vertices
- `forward_edges`: Dict[str, List[str]] - adjacency list for forward traversal (block_id → [connected_block_ids])
- `backward_edges`: Dict[str, List[str]] - reverse adjacency list for backward traversal

**Relationships**:
- Built from Diagram.blocks and Diagram.connections
- Used by path finding algorithms
- Discarded after pruning completes

**Validation Rules**:
- Every edge (connection) must reference valid nodes (blocks)
- Graph may contain cycles (feedback loops are valid)
- Nodes and edges derived from validated Diagram (no independent validation needed)

**Lifecycle**: Created → used for reachability analysis → discarded

---

### ReachabilityResult

**Purpose**: Container for bidirectional reachability analysis output

**Attributes**:
- `forward_reachable`: Set[str] - block IDs reachable forward from source
- `backward_reachable`: Set[str] - block IDs reachable backward from destination
- `path_blocks`: Set[str] - intersection of forward and backward reachable sets
- `source_block_id`: str - ID of block outputting source signal
- `dest_block_id`: str - ID of block outputting destination signal

**Relationships**:
- Produced by `_find_reachable_blocks()` function
- Consumed by `_prune_diagram()` function
- References block IDs from original Diagram

**Validation Rules**:
- `source_block_id` MUST be in `forward_reachable`
- `dest_block_id` MUST be in `backward_reachable`
- `path_blocks` MUST be non-empty (otherwise no valid path exists)
- `path_blocks` MUST contain both source and destination block IDs

**State Transitions**:
1. Initial: Empty sets
2. Forward DFS: Populates `forward_reachable`
3. Backward DFS: Populates `backward_reachable`
4. Intersection: Computes `path_blocks`

---

### PrunedDiagram

**Purpose**: Modified clone of original Diagram with only relevant blocks for extraction

**Attributes**:
- Inherits all attributes from `Diagram` class
- `blocks`: Subset of original blocks (only those in `path_blocks`)
- `connections`: Subset of original connections (only those between kept blocks)

**Relationships**:
- Cloned from original Diagram via `diagram._clone()`
- Blocks removed using `diagram.remove_block(block_id)`
- Passed to existing `_prepare_for_extraction()` for interconnect building

**Validation Rules**:
- Must contain at least source and destination blocks
- All connections must reference existing blocks (auto-enforced by remove_block)
- Must pass existing diagram validation (InputMarker, OutputMarker, connectivity)

**Lifecycle**:
1. Clone original Diagram
2. Remove blocks not in `path_blocks`
3. Pass to interconnect builder
4. Discarded after extraction completes

---

## Data Flow

```
Original Diagram
Signal Resolution (existing)
Build ConnectionGraph
Bidirectional DFS
ReachabilityResult
Clone & Prune → PrunedDiagram
Interconnect Building (existing)
Extracted Transfer Function
```

## Notes

- No persistent storage required
- All entities are transient (lifetime ~10-500ms during extraction)
- Graph structures use native Python collections (set, dict, list)
- No external graph library dependencies (NetworkX not needed)
Loading