* refactor(scripts): add project_contract.py single source of truth * fix(spec-status): word-boundary regex for stack matching (uv!=uvicorn) * feat(project-status): frontend stack detection + no_db support * fix(template): cookiecutter use_db=no writes no_db=true marker * fix(ci): ruff SIM300/PLC0415/W292/E501 in project_contract tests --------- Co-authored-by: opencode-agent <agent@opencode.local>
87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
#!/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/<pkg>/...),
|
|
# 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/<package>/ — 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"}
|