#!/usr/bin/env python3 """Project contract: single source of truth for project types, stacks, expected structure, frontend markers and no_db support. Imported by both ``spec-status.py`` and ``project-status.py`` via ``importlib.util.spec_from_file_location`` (no ``sys.path`` mutation). Stdlib-only imports. Contract symbols: ProjectType — StrEnum with 7 members (incl. MCP_SERVER + UNKNOWN) VALID_TYPES — set[str] excluding "unknown" STACK_REQUIRED — dict[type -> list[str]] of mandatory stack items STRUCTURE_EXPECTED — dict[type -> list[str]] of expected top-level dirs/files FRONTEND_STACK_MARKERS — dict with keys for fullstack frontend detection NO_DB_SUPPORTED — set[str] of types that support no_db=true """ from __future__ import annotations from enum import StrEnum class ProjectType(StrEnum): """Project type enum (auto-detected or spec-declared).""" FULLSTACK = "fullstack" BACKEND = "backend" CLI = "cli" BOT = "bot" WORKER = "worker" MCP_SERVER = "mcp-server" UNKNOWN = "unknown" # Excludes "unknown" — used by spec-status PROJECT_TYPE phase validation. VALID_TYPES: set[str] = {t.value for t in ProjectType if t != ProjectType.UNKNOWN} # Mandatory stack items per project type (spec-status Phase 2 STACK). # `uv` stays in ALL stacks (build-tool, mentioned in stack.md). STACK_REQUIRED: dict[str, list[str]] = { "backend": ["fastapi", "tortoise", "uv", "pytest", "ruff", "mypy", "loguru", "pydantic"], "fullstack": [ "fastapi", "tortoise", "svelte", "sveltekit", "biome", "uv", "ruff", "mypy", "pytest", "tailwind", "shadcn", "typescript", ], "mcp-server": ["fastapi", "mcp", "patchright", "uv"], "cli": ["typer", "uv", "hatchling", "ruff", "mypy", "pytest"], "bot": ["aiogram", "fastapi", "uv", "ruff", "mypy", "pytest"], "worker": ["prefect", "uv", "ruff", "mypy", "pytest"], } # Expected top-level structure per type (project-status check_structure). # BACKEND is resolved dynamically by ``_expected_backend_paths`` (src//...), # so it is NOT in this dict. mcp-server structure is TBD out of scope. # Keys are strings (project type values), NOT ProjectType enum members — # kept as plain strings for portability across both oracles. STRUCTURE_EXPECTED: dict[str, list[str]] = { "fullstack": ["backend", "frontend"], "cli": ["src"], # src// — checked generically "bot": ["src/bot.py"], "worker": ["src/flow.py"], "unknown": [], } # Fullstack frontend stack markers (project-status _check_type_specific_structure). # `tailwindcss` + `bits-ui` (shadcn-svelte proxy) in package.json deps, plus # `components.json` (shadcn config) and `tsconfig.json` (TypeScript) existence. FRONTEND_STACK_MARKERS: dict[str, list[str]] = { "fullstack_package_deps": ["tailwindcss", "bits-ui"], "fullstack_files": ["frontend/components.json", "frontend/tsconfig.json"], } # Project types that support ``[tool.project-status] no_db = true`` in # pyproject.toml (skip ``db/models`` check). Backend is the primary case; # fullstack is included for completeness (latent — _check_scattered_models # already early-returns when db/models absent). NO_DB_SUPPORTED: set[str] = {"backend", "fullstack"}