This document provides guidelines for AI coding agents working with the PANOPTES Observatory Control System (POCS) codebase. It is designed to be tool-agnostic and applicable to any AI assistant working on this project.
POCS (PANOPTES Observatory Control System) is the main software driver for robotic astronomical observatories designed to detect transiting exoplanets. The system controls telescope hardware, schedules observations, captures images, and manages the entire observation workflow through a state machine architecture.
Key Characteristics:
- Language: Python 3.12+ (type hints expected)
- Architecture: State machine-based observatory control
- Domain: Astronomy, robotics, hardware control
- Testing: pytest with high coverage requirements
- Package Manager: uv (modern Python package manager)
- Code Style: Ruff for linting and formatting
Before making changes, review these documents:
- Architecture:
docs/architecture-for-beginners.md- Understand the layered architecture - Contributing:
CONTRIBUTING.md- Development workflow and standards - CLI Guide:
docs/cli-guide.md- Command-line interface reference - Glossary:
docs/glossary.md- Domain-specific terminology - Conceptual Overview:
docs/conceptual-overview.md- High-level system design
POCS/
├── src/panoptes/pocs/ # Main source code
│ ├── core.py # POCS state machine (the brain)
│ ├── observatory.py # Hardware coordinator
│ ├── scheduler/ # Observation scheduler
│ ├── camera/ # Camera drivers
│ ├── mount/ # Telescope mount drivers
│ ├── dome/ # Dome control
│ ├── focuser/ # Focus control
│ └── utils/ # Utilities and CLI
├── tests/ # Test suite
├── conf_files/ # Configuration files
├── docs/ # Documentation
└── examples/ # Example scripts
Before making any changes:
- Check if an issue exists for the change; reference it in commits/PRs
- Read relevant architecture documentation to understand affected components
- Review existing tests to understand expected behavior
- Check
pyproject.tomlfor dependencies and project configuration
Style and Formatting:
- Use Ruff for linting and formatting (configured in
pyproject.toml) - Line length: 110 characters
- Quote style: double quotes
- Follow PEP 8 conventions
Type Hints:
- Required for all function signatures
- Use modern Python 3.12+ type syntax
- Import from
typingwhen necessary
Documentation:
- Docstrings for all public classes and functions
- Use Google-style docstrings
- Include examples in docstrings when helpful
All code changes must include tests:
- Unit tests in
tests/directory - Test files named
test_*.py - Use pytest fixtures from
conftest.py - Maintain or improve code coverage
- Run tests locally before committing:
pytest
Testing markers available:
@pytest.mark.theskyx # Tests requiring TheSkyX
@pytest.mark.with_camera # Tests requiring camera hardware
@pytest.mark.without_camera # Tests that should skip camera
@pytest.mark.plate_solve # Tests requiring plate solvingAdding Dependencies:
- Add to
dependenciesinpyproject.tomlfor runtime requirements - Add to
[dependency-groups]for development/testing tools - Use
uv add <package>to install and update lockfile - Pin security-sensitive packages (e.g.,
certifi>=2024.2.2)
Optional Dependencies:
focuser: Matplotlib and focus-related toolsgoogle: Google Cloud integrationweather: Weather station supportall: All optional features
File Editing Best Practices:
- Read entire files or large sections before editing
- Preserve existing code style and patterns
- Make minimal, focused changes
- Validate changes by checking for errors after editing
- Run relevant tests to confirm functionality
Commit Messages:
- Clear, descriptive commit messages
- Reference issue numbers when applicable
- Format:
Brief description (#issue-number)
Location: src/panoptes/pocs/core.py
The POCS state machine orchestrates observations. Key states include:
sleeping→ready→scheduling→slewing→tracking→observing→parking
When modifying:
- Understand state transitions (defined by
transitionslibrary) - Respect the state flow logic
- Add appropriate state validation
- Update state documentation if adding new states
Location: src/panoptes/pocs/scheduler/
The scheduler decides WHAT to observe (POCS decides WHEN).
When modifying:
- Understand constraints system
- Preserve target selection logic
- Test with various constraint combinations
- Consider edge cases (no targets available, moon constraints, etc.)
Location: src/panoptes/pocs/observatory.py
Manages all hardware as a unified system.
When modifying:
- Ensure thread safety for hardware access
- Validate hardware initialization sequences
- Handle missing hardware gracefully (simulator mode)
- Update hardware configuration documentation
Locations: camera/, mount/, dome/, focuser/
Device-specific control code.
When modifying:
- Maintain consistent driver interfaces
- Include simulator implementations for testing
- Handle hardware errors gracefully
- Add appropriate timeout handling
- Document device-specific quirks
Configuration files: conf_files/pocs.yaml (and variants)
Important configuration sections:
simulator: Which components to simulatemount: Mount type and configurationcameras: Camera definitions and parametersscheduler: Field lists and constraintsdirectories: Data storage locations
POCS uses a configuration server from the panoptes-utils library to
manage configuration. The server provides centralized configuration access across components.
Starting the config server locally:
# For normal development
panoptes-config-server --host 0.0.0.0 --port 6563 run --config-file conf_files/pocs.yaml
# For testing (use testing config)
panoptes-config-server --host 0.0.0.0 --port 6563 run --config-file tests/testing.yamlNotes:
- The config server must be running before starting POCS
- Default port is 6563
- Use
tests/testing.yamlas the config file when running tests - The server provides a REST API for configuration access
When modifying configuration:
- Maintain backward compatibility when possible
- Update example configs in
conf_files/ - Document new configuration options
- Validate with schema if available
- Restart config server after modifying config files
- Create class in appropriate directory (
camera/,mount/, etc.) - Inherit from base class (e.g.,
AbstractCamera,AbstractMount) - Implement all abstract methods
- Create simulator version for testing
- Add configuration options to
pocs.yaml - Write comprehensive tests
- Update documentation
- Review
core.pystate definitions - Add state to
transitionsconfiguration - Implement state entry/exit methods
- Update state diagram documentation
- Add tests for new state transitions
- Consider error handling and recovery
Location: src/panoptes/pocs/utils/cli/
- Add command to appropriate CLI module
- Use
typerfor command definition - Add help text and examples
- Test command functionality
- Update
docs/cli-guide.md
This process should be followed to create a new release of POCS.
Prerequisites:
- Ensure you have write access to the repository
- Ensure all CI tests are passing on the
mainbranch - Determine the new version number (see Version Numbering below)
Version Numbering:
- Use semantic versioning:
vX.Y.Z - Get the current version:
git describe --tags --abbrev=0 - Increment appropriately:
- X (Major): Breaking changes
- Y (Minor): New features, backward compatible
- Z (Patch): Bug fixes, backward compatible
Release Process:
-
Ensure
mainis clean:git checkout main git pull origin main git status # Should show "nothing to commit, working tree clean" -
Determine version number:
# Get current version CURRENT_VERSION=$(git describe --tags --abbrev=0) echo "Current version: $CURRENT_VERSION" # Set new version (example: v0.8.10 -> v0.8.11) NEW_VERSION="v0.8.11" # Update as appropriate echo "New version: $NEW_VERSION"
-
Create release branch from
main:git checkout -b release-${NEW_VERSION} origin/main -
Update
CHANGELOG.md:- Add release header with version and date:
## X.Y.Z - YYYY-MM-DD - Ensure all changes are documented under appropriate sections (Added, Changed, Fixed, Removed)
- Move any "Unreleased" changes under the new version
- Verify all PR numbers are referenced
- Example:
## 0.8.11 - 2026-02-13 ### Added - New feature description. #123 ### Fixed - Bug fix description. #124
- Add release header with version and date:
-
Commit changelog updates:
git add CHANGELOG.md git commit -m "Update CHANGELOG for ${NEW_VERSION}" -
Push the release branch and open a PR against
main:git push -u origin release-${NEW_VERSION}- Get the PR approved and merged into
main. Ensure all CI checks pass before merging.
- Get the PR approved and merged into
-
Tag
mainwith new version:- Once the PR is merged, switch to
main, pull the latest changes, and verify the build before tagging.
git checkout main git pull origin main # Optionally verify: uv run pytest && uv run ruff check . git tag -a ${NEW_VERSION} -m "Release ${NEW_VERSION}" git push origin ${NEW_VERSION}
- Once the PR is merged, switch to
-
Clean up release branch:
git branch -d release-${NEW_VERSION} git push origin --delete release-${NEW_VERSION}
Post-Release:
- Verify the new tag appears on GitHub releases page
- Monitor CI/CD for any issues
- Confirm the GitHub Actions workflow has successfully built and published the release to PyPI (triggered on tag push)
- Announce release on forum/communications channels
Common Issues:
- Test failures: Fix on the release branch before merging to
main. - Twine check failures: Usually due to missing or malformed metadata in
pyproject.toml.
Automation Notes for AI Agents (During Release Process):
- Parse version from
git describe --tags --abbrev=0 - Calculate next version based on changelog entries or commit messages
- Extract date automatically:
date +%Y-%m-%d - Validate version format matches
vX.Y.Zpattern - Ensure CHANGELOG has proper section headers before merging
- Verify all tests pass before tagging
Best Practices:
- Use specific exception types
- Provide informative error messages
- Log errors appropriately (use
loguru) - Clean up resources in error cases
- Consider recovery strategies
- Don't silently catch exceptions
The project uses loguru for logging, with PanBase providing logger setup:
Most classes inherit from PanBase, which automatically sets up a logger:
class MyClass(PanBase):
def my_method(self):
self.logger.info("Informational message")
self.logger.warning("Warning message")
self.logger.error("Error message")
self.logger.debug("Debug details")For standalone utilities or modules that don't inherit from PanBase:
from loguru import logger
logger.info("Informational message")Guidelines:
- Use
self.loggerin classes that inherit fromPanBase - Import
loggerfromloguruonly for standalone utilities - Log important state changes
- Include context in log messages
- Use appropriate log levels
- Don't log sensitive information
- Consider log volume (avoid spam)
tests/
├── test_*.py # Main test files
├── conftest.py # Pytest configuration and fixtures
├── testing.yaml # Test configuration
├── data/ # Test data files
└── utils/ # Test utilities
Good test characteristics:
- Isolated (don't depend on other tests)
- Repeatable (same result every time)
- Fast (use simulators, not real hardware)
- Clear (obvious what's being tested)
- Comprehensive (test edge cases)
Use fixtures:
def test_camera_exposure(camera):
"""Test camera can take exposure."""
# Use camera fixture from conftest.py# Run all tests
pytest
# Run specific test file
pytest tests/test_camera.py
# Run specific test
pytest tests/test_camera.py::test_camera_exposure
# Run with markers
pytest -m "not with_camera"
# Run with coverage
pytest --cov=panoptes.pocsdef function_name(param1: str, param2: int) -> bool:
"""Brief one-line description.
Longer description if needed. Explain what the function does,
why it exists, and any important details.
Args:
param1: Description of param1
param2: Description of param2
Returns:
Description of return value
Raises:
ValueError: When this happens
RuntimeError: When that happens
Examples:
>>> function_name("test", 42)
True
"""When making changes, update:
- Inline code comments for complex logic
- Docstrings for API changes
- Architecture docs for structural changes
- CLI guide for new commands
- Examples for new features
- All documentation must be written in Markdown for MkDocs. Do not use reStructuredText (.rst) or Sphinx.
- A Changelog (
CHANGELOG.md) entry should exist for every feature or bug fix. Minor changes (e.g., documentation updates, internal refactoring) may not require an entry. - All new entries should be added under an
## [Unreleased]section at the top of the file. If this section does not exist, create it. - Do NOT create a new version heading for individual PRs; new version headings should only be created when performing a release.
- Changelog entries should be categorized under appropriate sections (Added, Changed, Fixed, Removed) and reference PR numbers.
- Changelog entries should be clear and concise, describing the change and its impact, ideally less than one line.
- Pin security-sensitive dependencies
- Validate external input (coordinates, file paths, etc.)
- Handle credentials securely (never commit secrets)
- Sanitize user-provided configuration
- Consider physical safety (hardware commands)
- Validate astronomical calculations (safety limits)
- Observatory control is real-time critical
- Avoid blocking operations in main loop
- Use appropriate timeout values
- Consider hardware response times
- Monitor resource usage (disk, memory)
- Optimize image processing pipelines
- Simulator vs. Real Hardware: Always test with simulators first
- Thread Safety: Hardware access must be thread-safe
- State Machine Flow: Don't bypass state transitions
- Configuration Validation: Validate config before use
- Resource Cleanup: Always clean up hardware connections
- Astropy Units: Use units consistently (especially angles)
- Time Zones: Use UTC for all astronomical calculations
- Path Handling: Use
pathlib.Path, handle both absolute and relative paths
Key concepts to understand:
- Alt/Az vs. RA/Dec: Different coordinate systems
- Sidereal Time: Astronomical time standard
- Transit: When object crosses meridian
- Airmass: Atmospheric thickness (affects observations)
- Field of View: Area of sky visible to camera
- Plate Solving: Determining image coordinates from stars
- Light Frames: Science images
- Dark/Flat/Bias Frames: Calibration images
Useful libraries:
panoptes-utils: Primary source for PANOPTES utilities - Always check here first for common functionality ( time utilities, configuration, logging setup, etc.) before implementing new utilities or importing external librariesastropy: Astronomical calculations and unitsastroplan: Observation planningastroquery: Catalog queries
- Documentation: https://panoptes.github.io/POCS/
- Forum: https://forum.projectpanoptes.org
- Issues: https://github.com/panoptes/POCS/issues
- Code of Conduct:
CODE_OF_CONDUCT.md
-
Start broad, then narrow:
- Read architecture docs first
- Understand component relationships
- Then dive into specific files
-
Search effectively:
- Use semantic search for concepts
- Use grep for specific strings/patterns
- Check test files for usage examples
-
Understand before changing:
- Read the full function/class
- Check call sites to understand usage
- Review related tests
-
Validate assumptions:
- Check current behavior with tests
- Verify understanding of requirements
- Consider edge cases
-
Incremental approach:
- Make small, testable changes
- Run tests frequently
- Fix errors as they appear
-
Preserve intent:
- Maintain existing patterns
- Don't over-engineer solutions
- Keep changes focused
-
Be specific:
- Reference exact file paths
- Quote relevant code sections
- Explain reasoning for changes
-
Show your work:
- Explain what you searched for
- Describe what you found
- Outline your approach
-
Ask when uncertain:
- Clarify requirements if ambiguous
- Confirm understanding of domain concepts
- Request feedback on approach
# Start config server (required before running POCS)
panoptes-config-server --host 0.0.0.0 --port 6563 run --config-file conf_files/pocs.yaml
# Start config server for testing
panoptes-config-server --host 0.0.0.0 --port 6563 run --config-file tests/testing.yaml
# Install dependencies
uv sync
# Run tests
uv run pytest
# Run specific test file
uv run pytest tests/test_camera.py
# Check code style
uv run ruff check .
# Format code
uv run ruff format .
# Run POCS simulator
uv run pocs simulator
# View CLI help
uv run pocs --help- Main POCS class:
src/panoptes/pocs/core.py - Observatory:
src/panoptes/pocs/observatory.py - Scheduler:
src/panoptes/pocs/scheduler/ - Config:
conf_files/pocs.yaml - Tests:
tests/ - CLI:
src/panoptes/pocs/utils/cli/
- Use
self.loggerin classes inheriting fromPanBase - Use
pathlib.Pathfor file paths - Use
astropy.unitsfor physical quantities - Use type hints on all functions
- Write tests for all new code
- Update documentation for API changes
Remember: POCS controls real hardware that moves physical equipment. Always test thoroughly and consider safety implications of changes.