Compare commits

...

3 Commits

Author SHA1 Message Date
d340e06dad docs: add git workflow to working agreement (§18)
Long-lived master + dev; all non-doc work on branches off dev; doc-only
edits go straight to dev; human owns the dev->master merge and pushes
master; Claude pushes only dev, only after a --no-ff merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:27:04 -05:00
c5eccbb2a5 chore: gitignore and untrack .claude harness files
These are Claude Code session/state files, not project code. They were
swept into the first commit; remove from the index (kept on disk) and
ignore going forward so they stop dirtying status.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:24:46 -05:00
d4ada9bedb chore: dockerize api proxy and add gitignore
Add a Docker-based FastAPI proxy skeleton (charter §4) for parity across
dev / homelab VPS / fly.io, plus root .gitignore for Godot 4.7 and Python.

- api/Dockerfile: python:3.12-slim, non-root, PORT-aware CMD
- api/app/main.py: /health + five role endpoint stubs (§4)
- api/requirements.txt: fastapi/uvicorn/httpx/pydantic, pinned
- docker-compose.yml: local dev with live reload
- api/fly.toml: prod deploy stub with /health check
- api/.env.example: key lives in the proxy, never the client (§4)
- .gitignore: ignores .env, whitelists .env.example

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:23:27 -05:00
12 changed files with 218 additions and 47 deletions

View File

@@ -1,46 +0,0 @@
[2026-07-09 10:54:03.926] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:54:03.929] Processing: hook_event=UserPromptSubmit, tool=
[2026-07-09 10:56:09.126] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:09.128] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:18.474] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:18.476] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:21.852] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:21.855] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:25.336] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:25.338] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:28.741] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:28.744] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:32.070] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:32.073] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:35.419] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:35.422] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:39.157] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:39.159] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:42.516] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:42.518] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:48.542] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:48.545] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:51.814] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:51.816] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:55.095] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:55.098] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:56:59.075] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:56:59.077] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:02.336] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:02.338] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:05.616] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:05.618] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:08.886] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:08.888] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:12.207] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:12.209] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:16.216] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:16.218] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:19.540] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:19.542] Processing: hook_event=PostToolUse, tool=Write
[2026-07-09 10:57:31.383] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:31.386] Processing: hook_event=PostToolUse, tool=Edit
[2026-07-09 10:57:51.755] Hook called with args: ['/home/ptarrant/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.6/hooks/security_reminder_hook.py']
[2026-07-09 10:57:51.758] Processing: hook_event=Stop, tool=
[2026-07-09 10:57:51.761] _git_status_porcelain error: [Errno 138] emscripten does not support processes.
[2026-07-09 10:57:51.761] Stop hook: empty review set

View File

@@ -1 +0,0 @@
{"shown_warnings": [], "touched_paths": []}

63
.gitignore vendored Normal file
View File

@@ -0,0 +1,63 @@
# ─────────────────────────────────────────────
# Godot 4.7 (client/)
# ─────────────────────────────────────────────
# Editor + import cache (4.x replaced .import/ with .godot/)
.godot/
# Exported builds
/client/build/
export.cfg
# export_presets.cfg holds keystore paths / signing config — keep secrets out of history
export_presets.cfg
# Mono/C# (only if we drop to C# per charter §16)
.mono/
data_*/
*.mono/
# ─────────────────────────────────────────────
# Python / FastAPI (api/)
# ─────────────────────────────────────────────
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
.eggs/
build/
dist/
# Virtual envs
.venv/
venv/
env/
ENV/
# Tooling caches
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
htmlcov/
.tox/
# ─────────────────────────────────────────────
# Secrets — the API key lives here in dev, NEVER in the client (charter §4)
# ─────────────────────────────────────────────
.env
.env.*
!.env.example
# ─────────────────────────────────────────────
# Editors / OS
# ─────────────────────────────────────────────
.vscode/
.idea/
*.swp
.DS_Store
Thumbs.db
# ─────────────────────────────────────────────
# Logs (charter §4/§10 logs go to a store, not the repo)
# ─────────────────────────────────────────────
*.log
# ─────────────────────────────────────────────
# Claude Code harness session/state files
# ─────────────────────────────────────────────
.claude/

View File

@@ -530,3 +530,15 @@ Test that before building anything else. Every other system exists to make that
- **When a request conflicts with this document, say so.** Do not silently comply. This file is the argument; if it is wrong, change the file first.
- **When adding a system, state which side of §2 it falls on.** State or text. If it is both, it is two systems.
- **Prefer deleting scope.** The POC's value is answering one question fast.
### Git workflow
The standard flow for this project. Claude follows it; the human owns `master`.
- **Branches.** `master` and `dev` are long-lived. `master` is release; `dev` is integration. All work branches off `dev`.
- **Never commit non-doc changes directly to `master` or `dev`.** Anything that is not purely docs gets its own branch (`feature/…`, `fix/…`, `chore/…`, etc.), branched off `dev`.
- **Doc-only edits may commit directly to `dev`** — no branch required.
- **Never commit to `master`.** Ever. The human merges `dev``master` as they see fit. Claude never touches that merge.
- **Merging a working branch → `dev`** happens only after the work is done, smoke-tested, and **the human confirms**. Merge locally with `--no-ff`.
- **After merging to `dev`, delete the working branch locally.**
- **Pushing.** Origin is set. Claude pushes **only `dev`**, and only after a merge. Never push working branches or `master` — the human always pushes `master`.

16
api/.dockerignore Normal file
View File

@@ -0,0 +1,16 @@
# Keep the build context small and secrets out of the image.
.env
.env.*
__pycache__/
*.py[cod]
.venv/
venv/
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
htmlcov/
docs/
README.md
Dockerfile
.dockerignore

11
api/.env.example Normal file
View File

@@ -0,0 +1,11 @@
# Copy to .env for local dev. NEVER commit the real .env. The client never sees
# any of these — keys live only in the proxy (charter §4).
# Where the proxy sends model calls in dev (Ollama on the homelab).
OLLAMA_BASE_URL=http://localhost:11434
# Prod model provider (charter §4). Leave blank in dev.
REPLICATE_API_TOKEN=
# Port the proxy binds. compose / fly.io override this.
PORT=8000

26
api/Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
# /api — FastAPI proxy (charter §4). Docker-based for parity across
# dev / homelab VPS / fly.io.
FROM python:3.12-slim
# Faster, quieter Python in containers.
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
# Install deps first so the layer caches when only app code changes.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# App code.
COPY app ./app
# Run as non-root.
RUN useradd --create-home --uid 1000 appuser
USER appuser
EXPOSE 8000
# fly.io / compose set PORT; default to 8000 (charter localhost:8000).
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]

47
api/app/main.py Normal file
View File

@@ -0,0 +1,47 @@
"""FastAPI proxy entrypoint — the guarding proxy of charter §4.
Skeleton only: a health check plus the five role endpoints as stubs so the
container runs and the client has something to point at. Real prompt routing,
model selection, logging, and auth land later — all server-side (§4, §5).
"""
from fastapi import FastAPI
app = FastAPI(title="coc-rpg proxy", version="0.0.1")
@app.get("/health")
def health() -> dict:
"""Liveness probe for compose / fly.io."""
return {"status": "ok"}
# ── Role endpoints (charter §4) ──────────────────────────────────────────────
# The client knows these paths and nothing about which model or prompt serves
# them. Stubs for now; each will load its prompt from api/prompts/ and route to
# the configured model.
@app.post("/dm/narrate")
def narrate() -> dict:
return {"detail": "not implemented"}
@app.post("/dm/adjudicate")
def adjudicate() -> dict:
return {"detail": "not implemented"}
@app.post("/dm/improvise")
def improvise() -> dict:
return {"detail": "not implemented"}
@app.post("/npc/speak")
def npc_speak() -> dict:
return {"detail": "not implemented"}
@app.post("/party/banter")
def banter() -> dict:
return {"detail": "not implemented"}

21
api/fly.toml Normal file
View File

@@ -0,0 +1,21 @@
# fly.io deploy config for the proxy (charter §4, prod). Stub — set app name and
# region, then `fly deploy` from api/. Secrets (REPLICATE_API_TOKEN) go via
# `fly secrets set`, never in this file.
app = "coc-rpg-proxy" # TODO: claim a real app name
primary_region = "ord" # TODO: pick a region
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
[[http_service.checks]]
method = "GET"
path = "/health"
interval = "15s"
timeout = "2s"

5
api/requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
# FastAPI proxy runtime deps. Pinned from first resolve; bump deliberately.
fastapi==0.139.0
uvicorn[standard]==0.51.0
httpx==0.28.1 # calling Ollama / Replicate
pydantic==2.13.4 # request/response contracts

17
docker-compose.yml Normal file
View File

@@ -0,0 +1,17 @@
# Local dev — run the proxy in Docker with live reload.
# docker compose up --build
# Ollama runs on the homelab (charter §4), not here — point OLLAMA_BASE_URL at it.
services:
api:
build: ./api
ports:
- "8000:8000"
env_file:
- ./api/.env
environment:
PORT: 8000
# Mount source + reload so edits are live without a rebuild.
volumes:
- ./api/app:/app/app:ro
command: >
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload