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.
This commit is contained in:
tegwick 2026-08-09 22:41:26 +02:00
parent 3fa8d0b4c4
commit e02f5a258a
9 changed files with 342 additions and 7 deletions

18
.gitignore vendored
View file

@ -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

23
Makefile Normal file
View file

@ -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

View file

@ -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
```

View file

@ -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
P0P5, 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 P0P2 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

45
pyproject.toml Normal file
View file

@ -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"]

View file

@ -0,0 +1,3 @@
"""Repo Manager — HelixForge repository integration boundary."""
__version__ = "0.1.0"

51
src/repo_manager/cli.py Normal file
View file

@ -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())

11
tests/test_version.py Normal file
View file

@ -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

View file

@ -86,7 +86,7 @@ inbox C-rules retired from RM; phases P0P5; 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.