feat: layered location ownership model and transfer graph UX - #290
Merged
Conversation
Introduce a three-layer ownership model for locations that matches how labs actually work: node-managed locations (intrinsic to hardware), training (cross-node representation additions), and lab-managed locations (defined in a reconcilable config file). Phase 1 — Foundation: - Add LocationManagement enum (NODE/LAB) and managed_by/owner fields on Location - Add NodeIntrinsicLocationDefinition replacing NodeLocationTemplateDefinition - Add intrinsic_location_handler() on AbstractNode with auto-prefix naming - Add POST /location/init endpoint and init_location() client method - Schema upgrade 2.0.0 → 3.0.0 with managed_by index - Remove one-shot seed file mechanism Phase 2 — Lab Config: - Add LabLocationConfig and RepresentationTrainingEntry types - Add lab_config_file setting (replaces seed_locations_file) - Add _reconcile_lab_config() with mtime caching and training support Phase 3 — Visibility: - Add health fields (num_node/lab_managed_locations, last_reconciliation_at) - Add ?managed_by filter on GET /locations - Add GET /reconciliation/status endpoint Phase 4 — User-Facing Surfaces: - CLI: Managed By column, --managed-by filter, location train command - TUI: Managed By column and detail panel fields - Dashboard: management-type chips, filter controls, color-coded transfer graph - Docs: rewrite location templates guide, CHANGELOG, skill updates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Path.open() call prepares a file handle for httpx upload, not async file I/O — suppress the ruff ASYNC240 warning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ck template - Add platereader_carriage_repr representation template and intrinsic plate_carriage location to PlateReaderNode (now node-managed) - Remove platereader_1.plate_carriage from lab-managed locations in locations.yaml, add training entry for robotarm_1 access instead - Add storage_rack_nest location template to showcase location template functionality (transfer_arm role -> robotarm_wide_access) - Fix reconciliation loop never starting: the lifespan that spawns the periodic reconciliation was only wired through create_app(), not the standard run_server() path. Moved lifespan into LocationManager as a create_server() override so it always runs. - Add test for training applied to node-managed locations that arrive after startup (the exact scenario the reconciliation fix addresses) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…edResponse models Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… per edge Adds a richer transfer graph endpoint that returns all node names capable of executing each transfer and the minimum cost per location pair, enabling clients to show which nodes service each edge in the transfer graph. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds client method for the GET /transfer/graph/detailed endpoint, returning a TransferGraphDetailedResponse with edge-level node names and minimum transfer costs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds transfer_edges ref, updateTransferEdges() function, initial call, and 5-second polling interval to the Vue store, following the same pattern as updateLocationTemplates/updateRepresentationTemplates. Exports transfer_edges for use by consumer components. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l colors, and edge tooltips Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nsfer graph - Derive human-readable node names from location representations keys instead of showing raw ULID owner IDs in group labels - Add group-centroid repulsion force that pushes entire groups apart with minimum separation distance scaled by group size - Increase intra-group attraction to keep groups cohesive - Reduce transfer edge attraction to avoid overriding group separation - Increase layout iterations from 150 to 200 for better convergence Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add numeric index inside each graph vertex, matching a new # column in the locations table (sorted alphabetically by name) - Index text color adapts to fill brightness for contrast - Increase group min separation (220 + 25*size) to reduce overlap - Strengthen center gravity (0.01 -> 0.02) to better fill available space - Reduce edge thickness (2 -> 1) and opacity (0.5 -> 0.3) to cut clutter - Edges highlight on hover (2.5 width, 0.8 opacity) - Default table sort by graph index for easy cross-reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous commit only added the prop to the tabbed layout instance. The stacked and responsive layout instances were missing it, so indices never appeared on the graph vertices. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Resolve merge conflicts from CLI/TUI buildout branch integration: - location.py: adopt _make_client context manager pattern, keep managed_by filter - locations.py TUI: use model attribute access, keep managed_by/owner fields - data_client.py: use unstable's cleaner file handle pattern - test_tui_screens.py: keep location ownership tests with model-based API, include all migration tests from unstable - Fix bare _make_client assignment in train command (missing context manager) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix _sync_locations discarding _initialize_location_resource return value - Optimize health endpoint by adding count_locations_by_managed_by() to state handler - Cache detailed transfer graph alongside simple graph in TransferPlanner - Validate managed_by query parameter with LocationManagement enum type - Prevent template version downgrades using semantic version comparison - Add OTEL span to init_location endpoint - Clean up unnecessary defensive getattr calls for managed_by field - Extract duplicated managed-by filter chips into ManagedByFilter.vue component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the layered location ownership model and redesigns the transfer graph UI — the core changes needed to support node-managed vs lab-managed locations in MADSci.
Layered Location Ownership Model
intrinsic_locationsclass variable onAbstractNodeModulelocations.yaml) simplified to lab-managed locations, cross-node training, and location templates — no longer duplicates node-internal locationsLabLocationConfigmodel withtrainingsection for teaching nodes (e.g., robot arm) how to access other nodes' intrinsic locationsLocationManagementenum (NODE/LAB) andOwnershipInfoonLocationmodellocation_migration.pyandtransfer_planner.pymodulesstorage_rack_nestlocation templateTransfer Graph UX Redesign
/transfer/graph/detailedendpoint returning node names per edge (TransferGraphDetailedEdge,TransferGraphDetailedResponse)get_detailed_transfer_graph()added toLocationClient; detailed graph is cached alongside the simple transfer graph and rebuilt on location changes (avoids O(N²) recomputation on every poll)TransferGraphcomponent redesigned with: location grouping by node, resource fill colors, edge tooltips with node/representation info, location index numbersManagedByFilter.vuecomponent to deduplicate filter chip UI across layout variantsCLI/TUI Enhancements
madsci location list --managed-by node|labfilter option —managed_byquery parameter is now validated against theLocationManagementenum (returns 422 for invalid values)madsci location getdisplays Managed By and Owner fieldsmadsci location traincommand for adding node representations to locationsCode Quality Fixes
_sync_locations: Lab-config locations now correctly capture theresource_idfrom_initialize_location_resource()(previously discarded, causing perpetual "unresolved" state)count_locations_by_managed_by()to state handler usingcount_documents()instead of deserializing all locations on every health checkpackaging.version.Version) — only upgrades are applied, preventing accidental downgradesinit_locationendpoint (matchingadd_locationpattern)getattr(loc, "managed_by", None)patterns in CLI/TUI sincemanaged_byis a required field with defaultOther Changes
LocationsPanelin dashboard UI updated with improved layout10-location-templates.md)Test plan
pytestpasses (3985 tests, all green)just checkspasses (ruff, formatting, pre-commit)just upand locations reconcile correctlymanaged_by=NODEafter node registrationmadsci location list --managed-by nodefilters correctlymadsci location train deck_slot_1 robot_arm --overrides '{"x":1}'works🤖 Generated with Claude Code