Files
StarPunk/docs/design/project-structure.md
2025-11-18 19:21:31 -07:00

22 KiB

Project Structure Design

Purpose

This document defines the complete directory and file structure for the StarPunk project. It provides the exact layout that developer agents should create, including all directories, their purposes, file organization, and naming conventions.

Philosophy

The project structure follows these principles:

  • Flat is better than nested: Avoid deep directory hierarchies
  • Conventional is better than clever: Use standard Python project layout
  • Obvious is better than hidden: Clear directory names over abbreviations
  • Data is sacred: User data isolated in dedicated directory

Complete Directory Structure

starpunk/
├── .venv/                          # Python virtual environment (gitignored)
├── .env                            # Environment configuration (gitignored)
├── .env.example                    # Configuration template (committed)
├── .gitignore                      # Git ignore rules
├── README.md                       # Project documentation
├── CLAUDE.MD                       # AI agent requirements document
├── LICENSE                         # Project license
├── requirements.txt                # Python dependencies
├── requirements-dev.txt            # Development dependencies
│
├── app.py                          # Main application entry point
│
├── starpunk/                       # Application package
│   ├── __init__.py                # Package initialization
│   ├── config.py                  # Configuration management
│   ├── database.py                # Database operations
│   ├── models.py                  # Data models
│   ├── auth.py                    # Authentication logic
│   ├── micropub.py                # Micropub endpoint
│   ├── feed.py                    # RSS feed generation
│   ├── notes.py                   # Note management
│   └── utils.py                   # Helper functions
│
├── static/                         # Static assets
│   ├── css/
│   │   └── style.css              # Main stylesheet (~200 lines)
│   └── js/
│       └── preview.js             # Optional markdown preview
│
├── templates/                      # Jinja2 templates
│   ├── base.html                  # Base layout template
│   ├── index.html                 # Homepage (note list)
│   ├── note.html                  # Single note view
│   ├── feed.xml                   # RSS feed template
│   └── admin/
│       ├── base.html              # Admin base layout
│       ├── login.html             # Login form
│       ├── dashboard.html         # Admin dashboard
│       ├── new.html               # Create note form
│       └── edit.html              # Edit note form
│
├── data/                           # User data directory (gitignored)
│   ├── notes/                     # Markdown note files
│   │   ├── 2024/
│   │   │   ├── 11/
│   │   │   │   ├── first-note.md
│   │   │   │   └── second-note.md
│   │   │   └── 12/
│   │   │       └── december-note.md
│   │   └── 2025/
│   │       └── 01/
│   │           └── new-year.md
│   └── starpunk.db                # SQLite database
│
├── tests/                          # Test suite
│   ├── __init__.py
│   ├── conftest.py                # Pytest configuration and fixtures
│   ├── test_auth.py               # Authentication tests
│   ├── test_database.py           # Database tests
│   ├── test_micropub.py           # Micropub endpoint tests
│   ├── test_feed.py               # RSS feed tests
│   ├── test_notes.py              # Note management tests
│   └── test_utils.py              # Utility function tests
│
└── docs/                           # Architecture documentation
    ├── architecture/
    │   ├── overview.md
    │   ├── components.md
    │   ├── data-flow.md
    │   ├── security.md
    │   ├── deployment.md
    │   └── technology-stack.md
    ├── decisions/
    │   ├── ADR-001-python-web-framework.md
    │   ├── ADR-002-flask-extensions.md
    │   ├── ADR-003-frontend-technology.md
    │   ├── ADR-004-file-based-note-storage.md
    │   ├── ADR-005-indielogin-authentication.md
    │   └── ADR-006-python-virtual-environment-uv.md
    ├── design/
    │   ├── project-structure.md (this file)
    │   ├── database-schema.md
    │   ├── api-contracts.md
    │   ├── initial-files.md
    │   └── component-interfaces.md
    └── standards/
        ├── documentation-organization.md
        ├── python-coding-standards.md
        ├── development-setup.md
        └── testing-strategy.md

Directory Purposes

Root Directory (/)

Purpose: Project root containing configuration and entry point.

Key Files:

  • app.py - Main Flask application (import app from starpunk package)
  • .env - Environment variables (NEVER commit)
  • .env.example - Template for configuration
  • requirements.txt - Production dependencies
  • requirements-dev.txt - Development tools
  • README.md - User-facing documentation
  • CLAUDE.MD - AI agent instructions
  • LICENSE - Open source license

Rationale: Flat root with minimal files makes project easy to understand at a glance.


Application Package (starpunk/)

Purpose: Core application code organized as Python package.

Structure: Flat module layout (no sub-packages in V1)

Modules:

Module Purpose Approximate LOC
__init__.py Package init, create Flask app 50
config.py Load configuration from .env 75
database.py SQLite operations, schema management 200
models.py Data models (Note, Session, Token) 150
auth.py IndieAuth flow, session management 200
micropub.py Micropub endpoint implementation 250
feed.py RSS feed generation 100
notes.py Note CRUD, file operations 300
utils.py Slug generation, hashing, helpers 100

Total Estimated: ~1,425 LOC for core application

Naming Convention:

  • Lowercase with underscores
  • Singular nouns for single-purpose modules (config.py, not configs.py)
  • Plural for collections (notes.py manages many notes)
  • Descriptive names over abbreviations (database.py, not db.py)

Import Strategy:

# In app.py
from starpunk import create_app

app = create_app()

Rationale: Flat package structure is simpler than nested sub-packages. All modules are peers. No circular dependency issues.


Static Assets (static/)

Purpose: CSS, JavaScript, and other static files served directly.

Organization:

static/
├── css/
│   └── style.css        # Single stylesheet
└── js/
    └── preview.js       # Optional markdown preview

CSS Structure (style.css):

  • ~200 lines total
  • CSS custom properties for theming
  • Mobile-first responsive design
  • Semantic HTML with minimal classes

JavaScript Structure (preview.js):

  • Optional enhancement only
  • Vanilla JavaScript (no frameworks)
  • Markdown preview using marked.js from CDN
  • Degrades gracefully if disabled

URL Pattern: /static/{type}/{file}

  • Example: /static/css/style.css
  • Example: /static/js/preview.js

Future Assets (V2+):

  • Favicon: static/favicon.ico
  • Icons: static/icons/
  • Images: static/images/ (if needed)

Rationale: Standard Flask static file convention. Simple, flat structure since we have minimal assets.


Templates (templates/)

Purpose: Jinja2 HTML templates for server-side rendering.

Organization:

templates/
├── base.html              # Public site base layout
├── index.html             # Homepage (extends base.html)
├── note.html              # Single note (extends base.html)
├── feed.xml               # RSS feed (no layout)
└── admin/
    ├── base.html          # Admin base layout
    ├── login.html         # Login form
    ├── dashboard.html     # Admin dashboard
    ├── new.html           # Create note
    └── edit.html          # Edit note

Template Hierarchy:

base.html (public)
  ├── index.html (note list)
  └── note.html (single note)

admin/base.html
  ├── admin/dashboard.html
  ├── admin/new.html
  └── admin/edit.html

admin/login.html (no base, standalone)
feed.xml (no base, XML output)

Naming Convention:

  • Lowercase with hyphens for multi-word names
  • Descriptive names (dashboard.html, not dash.html)
  • Base templates clearly named (base.html)

Template Features:

  • Microformats2 markup (h-entry, h-card)
  • Semantic HTML5
  • Accessible markup (ARIA labels)
  • Mobile-responsive
  • Progressive enhancement

Rationale: Standard Flask template convention. Admin templates in subdirectory keeps them organized.


Data Directory (data/)

Purpose: User-created content and database. This is the sacred directory.

Structure:

data/
├── notes/
│   └── {YYYY}/
│       └── {MM}/
│           └── {slug}.md
└── starpunk.db

Notes Directory (data/notes/):

  • Pattern: Year/Month hierarchy (YYYY/MM/)
  • Files: Markdown files with slug names
  • Example: data/notes/2024/11/my-first-note.md

Database File (data/starpunk.db):

  • SQLite database
  • Contains metadata, sessions, tokens
  • NOT content (content is in .md files)

Permissions:

  • Directory: 755 (rwxr-xr-x)
  • Files: 644 (rw-r--r--)
  • Database: 644 (rw-r--r--)

Backup Strategy:

# Simple backup
tar -czf starpunk-backup-$(date +%Y%m%d).tar.gz data/

# Or rsync
rsync -av data/ /backup/starpunk/

Gitignore: ENTIRE data/ directory must be gitignored.

Rationale: User data is completely isolated. Easy to backup. Portable across systems.


Tests (tests/)

Purpose: Complete test suite using pytest.

Organization:

tests/
├── __init__.py           # Empty, marks as package
├── conftest.py           # Pytest fixtures and configuration
├── test_auth.py          # Authentication tests
├── test_database.py      # Database operations tests
├── test_micropub.py      # Micropub endpoint tests
├── test_feed.py          # RSS feed generation tests
├── test_notes.py         # Note management tests
└── test_utils.py         # Utility function tests

Naming Convention:

  • All test files: test_{module}.py
  • All test functions: def test_{function_name}():
  • Fixtures in conftest.py

Test Organization:

  • One test file per application module
  • Integration tests in same file as unit tests
  • Use fixtures for common setup (database, app context)

Coverage Target: >80% for V1

Rationale: Standard pytest convention. Easy to run all tests or specific modules.


Documentation (docs/)

Purpose: Architecture, decisions, designs, and standards.

Organization: See Documentation Organization Standard

docs/
├── architecture/        # System-level architecture
├── decisions/          # ADRs (Architecture Decision Records)
├── design/             # Detailed technical designs
└── standards/          # Coding and process standards

Key Documents:

  • architecture/overview.md - System architecture
  • architecture/technology-stack.md - Complete tech stack
  • decisions/ADR-*.md - All architectural decisions
  • design/project-structure.md - This document
  • standards/python-coding-standards.md - Code style

Rationale: Clear separation of document types. Easy to find relevant documentation.


File Naming Conventions

Python Files

  • Modules: lowercase_with_underscores.py
  • Packages: lowercase (no underscores if possible)
  • Classes: PascalCase (in code, not filenames)
  • Functions: lowercase_with_underscores (in code)

Examples:

starpunk/database.py         # Good
starpunk/Database.py         # Bad
starpunk/db.py              # Bad (use full word)
starpunk/note_manager.py     # Good if needed

Markdown Files (Notes)

  • Pattern: {slug}.md
  • Slug format: lowercase-with-hyphens
  • Valid characters: a-z, 0-9, - (hyphen)
  • No spaces, no underscores, no special characters

Examples:

first-note.md               # Good
my-thoughts-on-python.md    # Good
2024-11-18-daily-note.md    # Good (date prefix okay)
First Note.md               # Bad (spaces)
first_note.md               # Bad (underscores)
first-note.markdown         # Bad (use .md)

Template Files

  • Pattern: lowercase.html or lowercase-name.html
  • XML: .xml extension for RSS feed

Examples:

base.html                   # Good
note.html                   # Good
admin/dashboard.html        # Good
admin/new-note.html         # Good (if multi-word)
admin/NewNote.html          # Bad

Documentation Files

  • Pattern: lowercase-with-hyphens.md
  • ADRs: ADR-{NNN}-{short-title}.md

Examples:

project-structure.md        # Good
database-schema.md          # Good
ADR-001-python-web-framework.md  # Good
ProjectStructure.md         # Bad
project_structure.md        # Bad (use hyphens)

Configuration Files

  • Pattern: Standard conventions (.env, requirements.txt, etc.)

Examples:

.env                        # Good
.env.example                # Good
requirements.txt            # Good
requirements-dev.txt        # Good
.gitignore                  # Good

Gitignore Requirements

The .gitignore file MUST include the following:

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python

# Virtual Environment
.venv/
venv/
env/
ENV/
.venv.*

# Environment Configuration
.env
*.env
!.env.example

# User Data (CRITICAL - NEVER COMMIT)
data/
*.db
*.sqlite
*.sqlite3

# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Testing
.pytest_cache/
.coverage
htmlcov/
*.cover
.hypothesis/

# Distribution
dist/
build/
*.egg-info/
*.egg

# Logs
*.log
logs/

Critical Rules:

  1. NEVER commit data/ directory - contains user data
  2. NEVER commit .env file - contains secrets
  3. NEVER commit .venv/ - virtual environment is local
  4. ALWAYS commit .env.example - template is safe

Directory Creation Order

When initializing project, create in this order:

  1. Root files:

    touch .gitignore
    touch .env.example
    touch README.md
    touch CLAUDE.MD
    touch LICENSE
    touch requirements.txt
    touch requirements-dev.txt
    
  2. Application package:

    mkdir -p starpunk
    touch starpunk/__init__.py
    
  3. Static assets:

    mkdir -p static/css static/js
    touch static/css/style.css
    touch static/js/preview.js
    
  4. Templates:

    mkdir -p templates/admin
    touch templates/base.html
    touch templates/index.html
    touch templates/note.html
    touch templates/feed.xml
    touch templates/admin/base.html
    touch templates/admin/login.html
    touch templates/admin/dashboard.html
    touch templates/admin/new.html
    touch templates/admin/edit.html
    
  5. Data directory (created on first run):

    mkdir -p data/notes
    
  6. Tests:

    mkdir -p tests
    touch tests/__init__.py
    touch tests/conftest.py
    
  7. Documentation (mostly exists):

    mkdir -p docs/architecture docs/decisions docs/design docs/standards
    

Path Standards

Absolute vs Relative Paths

In Code:

  • Use relative paths from project root
  • Let Flask handle path resolution
  • Use pathlib.Path for file operations

In Configuration:

  • Support both absolute and relative paths
  • Relative paths are relative to project root
  • Document clearly in .env.example

In Documentation:

  • Use absolute paths for clarity
  • Example: /home/phil/Projects/starpunk/data/notes

In Agent Operations:

  • ALWAYS use absolute paths
  • Never rely on current working directory
  • See ADR-006 for details

Path Construction Examples

from pathlib import Path

# Project root
PROJECT_ROOT = Path(__file__).parent.parent

# Data directory
DATA_DIR = PROJECT_ROOT / "data"
NOTES_DIR = DATA_DIR / "notes"
DB_PATH = DATA_DIR / "starpunk.db"

# Static files
STATIC_DIR = PROJECT_ROOT / "static"
CSS_DIR = STATIC_DIR / "css"

# Templates (handled by Flask)
TEMPLATE_DIR = PROJECT_ROOT / "templates"

File Size Guidelines

Target Sizes

File Type Target Size Maximum Size Rationale
Python module 100-300 LOC 500 LOC Keep modules focused
Template 50-100 lines 200 lines Use template inheritance
CSS 200 LOC total 300 LOC Minimal styling only
JavaScript 100 LOC 200 LOC Optional feature only
Markdown note 50-500 words No limit User content
Test file 100-200 LOC 400 LOC One module per file

If file exceeds maximum: Consider splitting into multiple modules or refactoring.


Module Organization

starpunk/init.py

Purpose: Package initialization and Flask app factory.

Contents:

  • create_app() function
  • Blueprint registration
  • Configuration loading

Example:

from flask import Flask

def create_app():
    app = Flask(__name__)

    # Load configuration
    from starpunk.config import load_config
    load_config(app)

    # Initialize database
    from starpunk.database import init_db
    init_db(app)

    # Register blueprints
    from starpunk.routes import public, admin, api
    app.register_blueprint(public.bp)
    app.register_blueprint(admin.bp)
    app.register_blueprint(api.bp)

    return app

app.py (Root)

Purpose: Application entry point for Flask.

Contents:

from starpunk import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True)

Rationale: Minimal entry point. All logic in package.


Import Organization

Follow PEP 8 import ordering:

  1. Standard library imports
  2. Third-party imports
  3. Local application imports

Example:

# Standard library
import os
import sqlite3
from pathlib import Path
from datetime import datetime

# Third-party
from flask import Flask, render_template
import markdown
import httpx

# Local
from starpunk.config import load_config
from starpunk.database import get_db
from starpunk.models import Note

Future Structure Extensions (V2+)

Potential Additions

Media Uploads:

data/
├── notes/
├── media/
│   └── {YYYY}/{MM}/
│       └── {filename}
└── starpunk.db

Migrations:

migrations/
├── 001_initial_schema.sql
├── 002_add_media_table.sql
└── 003_add_tags.sql

Deployment:

deploy/
├── systemd/
│   └── starpunk.service
├── nginx/
│   └── starpunk.conf
└── docker/
    └── Dockerfile

Do NOT create these in V1 - only when needed.


Verification Checklist

After creating project structure, verify:

  • All directories exist
  • .gitignore is configured correctly
  • .env.example exists (.env does not)
  • data/ directory is gitignored
  • All __init__.py files exist in Python packages
  • Template hierarchy is correct
  • Static files are in correct locations
  • Tests directory mirrors application structure
  • Documentation is organized correctly
  • No placeholder files committed (except templates)

Anti-Patterns to Avoid

Don't:

  • Create deeply nested directories (max 3 levels)
  • Use abbreviations in directory names (tpl/ instead of templates/)
  • Mix code and data (keep data/ separate)
  • Put configuration in code (use .env)
  • Create empty placeholder directories
  • Use both src/ and package name (pick one - we chose package)
  • Create scripts/, bin/, tools/ directories in V1 (YAGNI)
  • Put tests inside application package (keep separate)

Do:

  • Keep structure flat where possible
  • Use standard conventions (Flask, Python, pytest)
  • Separate concerns (code, data, tests, docs, static)
  • Make structure obvious to newcomers
  • Follow principle: "Every directory must justify its existence"

Summary

Total Directories: 12 top-level directories/files Total Python Modules: ~9 in starpunk package Total Templates: 9 HTML/XML files Total LOC Estimate: ~1,500 LOC application + 500 LOC tests = 2,000 total

Philosophy: Simple, flat, conventional structure. Every file and directory has a clear purpose. User data is isolated and portable. Documentation is comprehensive and organized.

References