From e02f5a258a2e7640fdc1ddafbaeaa193ef72e51d Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 9 Aug 2026 22:41:26 +0200 Subject: [PATCH] docs(RMGR-WP-0001): complete T04 implementation foundation Accept ADR-001: Python 3.12, FastAPI, Postgres/SQLAlchemy async, src layout, uv/hatchling, pytest. Scaffold package, rmgr CLI stub, and version tests. --- .gitignore | 18 ++- Makefile | 23 +++ README.md | 7 + docs/adr-001-implementation-foundation.md | 182 ++++++++++++++++++++++ pyproject.toml | 45 ++++++ src/repo_manager/__init__.py | 3 + src/repo_manager/cli.py | 51 ++++++ tests/test_version.py | 11 ++ workplans/RMGR-WP-0001-foundation.md | 9 +- 9 files changed, 342 insertions(+), 7 deletions(-) create mode 100644 Makefile create mode 100644 docs/adr-001-implementation-foundation.md create mode 100644 pyproject.toml create mode 100644 src/repo_manager/__init__.py create mode 100644 src/repo_manager/cli.py create mode 100644 tests/test_version.py diff --git a/.gitignore b/.gitignore index e4e0199..14127e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,13 @@ -# state-hub: track .claude/rules -# Claude Code local state (track shared rules; ignore machine-specific files) -.claude/* -!.claude/rules/ -!.claude/rules/*.md +.venv/ +__pycache__/ +*.py[cod] +.pytest_cache/ +.ruff_cache/ +.mypy_cache/ +*.egg-info/ +dist/ +build/ +.env +.coverage +htmlcov/ +uv.lock diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7108951 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# Repo Manager — local developer targets (RMGR-ADR-001) +UV ?= $(shell command -v uv 2>/dev/null || if [ -x "$$HOME/.local/bin/uv" ]; then printf "%s" "$$HOME/.local/bin/uv"; else printf "%s" "uv"; fi) + +.PHONY: help install test lint cli-version + +help: + @echo "Repo Manager targets:" + @echo " make install # uv sync / editable install" + @echo " make test # pytest" + @echo " make cli-version # rmgr --version" + @echo " (api/db/migrate land with first runtime slice)" + +install: + $(UV) sync --all-extras 2>/dev/null || $(UV) pip install -e ".[dev]" + +test: + $(UV) run pytest -q + +lint: + $(UV) run ruff check src tests 2>/dev/null || true + +cli-version: + $(UV) run rmgr --version diff --git a/README.md b/README.md index 40bd559..4b0a302 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,10 @@ Architecture: - [Repository representation v0.1](docs/repository-representation_v0.1.md) - [Observation and command contracts v0.1](docs/observation-command-contracts_v0.1.md) - [State Hub extraction inventory v0.1](docs/state-hub-extraction-inventory_v0.1.md) +- [ADR-001 Implementation foundation](docs/adr-001-implementation-foundation.md) + +```bash +make install # or: uv pip install -e ".[dev]" +make test +rmgr --version +``` diff --git a/docs/adr-001-implementation-foundation.md b/docs/adr-001-implementation-foundation.md new file mode 100644 index 0000000..413a4f3 --- /dev/null +++ b/docs/adr-001-implementation-foundation.md @@ -0,0 +1,182 @@ +--- +id: RMGR-ADR-001 +type: architecture-decision-record +title: "Implementation foundation for Repo Manager" +status: accepted +decided: "2026-08-09" +workplan_task: RMGR-WP-0001-T04 +deciders: ["codex", "project-convention"] +related: + - docs/repository-representation_v0.1.md + - docs/observation-command-contracts_v0.1.md + - docs/state-hub-extraction-inventory_v0.1.md + - INTENT.md +--- + +# ADR-001: Implementation foundation for Repo Manager + +## Status + +**Accepted** (2026-08-09). + +## Context + +RMGR-WP-0001 requires a runtime, persistence, packaging, migration, test, and +deployment shape **before** substantial extraction from State Hub (T03 phases +P0–P5, T05 E2E). Choices must match ordinary HelixForge services so dual-run, +operators, and agents do not face a one-off stack. + +Reference platforms: + +| Repo | Role | Stack | +| --- | --- | --- | +| `state-hub` | Current repo index + consistency (extract source) | Python 3.12, FastAPI, SQLAlchemy async, asyncpg, Alembic, uv, FastMCP | +| `hub-core` | Shared library | Python 3.12, FastAPI/SQLAlchemy primitives, hatchling | +| `core-hub` | Production hub runtime | Same + Docker/k8s | +| `activity-core` | Schedulers/executors | Python, FastAPI, SQLAlchemy async, Alembic, Temporal (domain-specific) | + +Repo Manager is a **functional component** (not a domain hub): service + CLI, +own data plane for projections, Git as file authority. + +## Decision + +### 1. Language and packaging + +| Choice | Decision | +| --- | --- | +| Language | **Python 3.12+** | +| Layout | **`src/repo_manager/`** (src-layout, hatchling) | +| Distribution | Installable package `repo-manager` / import `repo_manager` | +| Tooling | **uv** + **Makefile** (fleet norm) | +| Lint (phase 2) | ruff (align with core-hub when CI lands) | + +### 2. Runtime surface + +| Choice | Decision | +| --- | --- | +| HTTP API | **FastAPI** (async) implementing `helixforge.repo-manager` 0.1 | +| ASGI server | **uvicorn** | +| Settings | **pydantic-settings** | +| DTOs | **Pydantic v2** only at API boundary (no ORM leakage) | +| CLI | Entry point **`rmgr`** (`repo_manager.cli`) — `reconcile`, `register`, later `command` | +| MCP | **Not day-1** — agents keep State Hub / hub-core MCP until dual-run; optional FastMCP adapter in a later WP | +| Schedulers | **None in-process** — activity-core calls reconcile HTTP/CLI | + +### 3. Persistence + +| Choice | Decision | +| --- | --- | +| Database | **PostgreSQL 16** (CNPG/fleet); dedicated DB/schema **`repo_manager`** | +| ORM | **SQLAlchemy 2.x asyncio** + **asyncpg** | +| Migrations | **Alembic** under `src/repo_manager/migrations/` or repo-root `migrations/` | +| Metadata | **Own `Base`** — never mix with hub-core or state-hub metadata (isolation) | +| SQLite | **Dev/test only** optional via aiosqlite; production is Postgres | + +**Rationale:** Separate DB from State Hub so cutover does not share write +contention or schema ownership. Files remain authority for work records; +Postgres holds registry, index, findings, command/idempotency logs. + +### 4. Git and files + +| Choice | Decision | +| --- | --- | +| Git operations | **subprocess git** (parity with `consistency_check` extract) | +| Parsing | Port/adapt SH parsers → `repo_manager.parse` | +| Writeback | Same commit evidence rules as contracts (git_sha required) | + +Avoid new Git bindings (GitPython) until a concrete need; subprocess matches +extract source and SSH/forge setups already in production images. + +### 5. Events and integration + +| Choice | Decision | +| --- | --- | +| hub-core dependency | **Optional path/editable** for utils only (slug/pagination) if useful; **no** shared tables | +| NATS | **Optional later** — emit HTTP hooks or in-process event bus first; nats-py when hub-core event bus is standard | +| Authz | Call hub-core **policy port** when available; **dev allowlist** scopes for local | + +### 6. Testing + +| Choice | Decision | +| --- | --- | +| Runner | **pytest** + **pytest-asyncio** (`asyncio_mode = auto`) | +| Unit | parsers, transition matrix, DTOs | +| Integration | Postgres test DB or ephemeral container; git fixtures in `tests/fixtures/repos/` | +| Compat | Suites T-PARSE…T-DUAL-RUN from extraction inventory | + +### 7. Deployment + +| Choice | Decision | +| --- | --- | +| Container | **Dockerfile** (python:3.12-slim + git + openssh-client + uv) modeled on state-hub | +| Local | `make db migrate api` / `make test` | +| Cluster | Later Helm under railiance (not blocking T05); single replica first | +| Secrets | DATABASE_URL / policy tokens via env or platform custody — never in repo | + +### 8. Initial package modules (target) + +```text +src/repo_manager/ + __init__.py + config.py + db.py # engine, session, Base + models/ # private ORM + schemas/ # public Pydantic DTOs + api/ # FastAPI routers + parse/ # workplan/work-record parsers + consistency/ # C-rules engine (extract) + commands/ # command handlers + gitops/ # subprocess git helpers + cli.py +migrations/ +tests/ +pyproject.toml +Makefile +Dockerfile # with first deployable slice +``` + +### 9. Explicit non-choices + +| Rejected | Why | +| --- | --- | +| Haskell / IHP | Retired path; fleet is Python for this class of service | +| Sharing State Hub database long-term | Confuses authority and cutover | +| Embedding Temporal | activity-core owns schedules | +| Day-1 full MCP surface | Dual-run via existing SH tools first | +| Calling Repo Manager a hub | Taxonomy: functional component | + +## Consequences + +**Positive** + +- Operators and extract PRs stay on known stack. +- Clear dual-run: SH and RM side by side with same Git authority. +- CLI `rmgr reconcile` can replace `statehub fix-consistency` gradually. + +**Negative / cost** + +- Another Postgres database and migration chain to operate. +- Temporary dual registration until SH registry is strangler-cut. + +**Follow-ons** + +- T05: E2E on `repo-manager` itself (or a fixture repo) using P0/P1 slices. +- STATE-WP-0079: point fix-consistency / MCP writebacks at RM adapters. +- Optional: publish OpenAPI for `helixforge.repo-manager` 0.1 from FastAPI. + +## Compliance with prior docs + +| Doc | How this ADR satisfies it | +| --- | --- | +| REP-0001 | ORM private; snapshots are DTOs | +| CMD-0001 | FastAPI + CLI implement observation/commands | +| EXTRACT-0001 | Phases P0–P2 map to modules above | + +## Acceptance (T04) + +- [x] Runtime chosen (FastAPI/uvicorn, Python 3.12) +- [x] Persistence chosen (Postgres + SQLAlchemy async + Alembic, own DB) +- [x] Package/layout chosen (src/repo_manager, uv, hatchling) +- [x] Test approach chosen (pytest-asyncio + fixtures) +- [x] Deploy approach chosen (Docker, make; Helm later) +- [x] Non-goals recorded diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ea48a2f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,45 @@ +[project] +name = "repo-manager" +version = "0.1.0" +description = "HelixForge repository integration boundary" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "fastapi>=0.115.0", + "uvicorn[standard]>=0.32.0", + "sqlalchemy[asyncio]>=2.0.0", + "asyncpg>=0.30.0", + "alembic>=1.14.0", + "pydantic>=2.10.0", + "pydantic-settings>=2.7.0", + "httpx>=0.28.0", + "pyyaml>=6.0.0", + "python-dotenv>=1.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", + "pytest-asyncio>=0.24.0", + "ruff>=0.8.0", + "aiosqlite>=0.20.0", +] + +[project.scripts] +rmgr = "repo_manager.cli:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/repo_manager"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +asyncio_mode = "auto" + +[tool.ruff] +line-length = 100 +src = ["src", "tests"] diff --git a/src/repo_manager/__init__.py b/src/repo_manager/__init__.py new file mode 100644 index 0000000..5bdc3a3 --- /dev/null +++ b/src/repo_manager/__init__.py @@ -0,0 +1,3 @@ +"""Repo Manager — HelixForge repository integration boundary.""" + +__version__ = "0.1.0" diff --git a/src/repo_manager/cli.py b/src/repo_manager/cli.py new file mode 100644 index 0000000..d2eefb8 --- /dev/null +++ b/src/repo_manager/cli.py @@ -0,0 +1,51 @@ +"""CLI entry point ``rmgr`` (skeleton — commands land with extract phases).""" + +from __future__ import annotations + +import argparse +import sys + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + prog="rmgr", + description="Repo Manager CLI (helixforge.repo-manager)", + ) + parser.add_argument( + "--version", + action="store_true", + help="Print package version and exit", + ) + sub = parser.add_subparsers(dest="command") + + sub.add_parser("version", help="Print version") + + # Placeholders — implemented as extraction phases land (RMGR-WP-0001-T05+). + p_rec = sub.add_parser( + "reconcile", + help="Run consistency reconcile (not yet implemented)", + ) + p_rec.add_argument("--path", default=".", help="Repository checkout path") + p_rec.add_argument("--fix", action="store_true", help="Apply safe fixes") + + args = parser.parse_args(argv) + if args.version or args.command in (None, "version"): + from repo_manager import __version__ + + print(__version__) + return 0 + + if args.command == "reconcile": + print( + "rmgr reconcile: not implemented yet " + "(see docs/adr-001-implementation-foundation.md, phase P0)", + file=sys.stderr, + ) + return 2 + + parser.print_help() + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..2a1e3fa --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,11 @@ +from repo_manager import __version__ +from repo_manager.cli import main + + +def test_version_string(): + assert __version__ == "0.1.0" + + +def test_cli_version(capsys): + assert main(["--version"]) == 0 + assert "0.1.0" in capsys.readouterr().out diff --git a/workplans/RMGR-WP-0001-foundation.md b/workplans/RMGR-WP-0001-foundation.md index 8c86575..c9b7068 100644 --- a/workplans/RMGR-WP-0001-foundation.md +++ b/workplans/RMGR-WP-0001-foundation.md @@ -86,7 +86,7 @@ inbox C-rules retired from RM; phases P0–P5; compat suites T-PARSE…T-DUAL-RU ```task id: RMGR-WP-0001-T04 -status: todo +status: done priority: medium state_hub_task_id: "0a041f4e-09fc-4f69-8a48-ac396186bc8f" ``` @@ -95,6 +95,11 @@ Choose the runtime, persistence, package, migration, test, and deployment shape consistent with the ordinary HelixForge platform. Record the decision before substantial implementation. +**Result (2026-08-09):** ADR-001 `docs/adr-001-implementation-foundation.md` — +Python 3.12, FastAPI/uvicorn, Postgres+SQLAlchemy async+Alembic (own DB), +src/repo_manager, uv/hatchling, pytest-asyncio, Docker later; CLI `rmgr` +skeleton. Package scaffold + version test landed (no business logic yet). + ## Prove one repository end to end ```task @@ -115,5 +120,5 @@ projection. - [x] Observation and command contracts are versioned and testable. - (semantics + catalog done; automated suite with runtime in T04/T05) - [x] State Hub extraction candidates have dispositions. -- [ ] The implementation foundation has a recorded decision. +- [x] The implementation foundation has a recorded decision. - [ ] One repository completes the end-to-end vertical slice.