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:
parent
3fa8d0b4c4
commit
e02f5a258a
9 changed files with 342 additions and 7 deletions
18
.gitignore
vendored
18
.gitignore
vendored
|
|
@ -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
23
Makefile
Normal 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
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
182
docs/adr-001-implementation-foundation.md
Normal file
182
docs/adr-001-implementation-foundation.md
Normal 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
|
||||
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
|
||||
45
pyproject.toml
Normal file
45
pyproject.toml
Normal 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"]
|
||||
3
src/repo_manager/__init__.py
Normal file
3
src/repo_manager/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"""Repo Manager — HelixForge repository integration boundary."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
51
src/repo_manager/cli.py
Normal file
51
src/repo_manager/cli.py
Normal 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
11
tests/test_version.py
Normal 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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue