diff --git a/.env.example b/.env.example index d341c9c..facb774 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,18 @@ # Copie para .env. O .env não é commitado. # Compose (Postgres local em localhost:5433) -POSTGRES_USER=mansao -POSTGRES_PASSWORD=mansao -POSTGRES_DB=mansao +POSTGRES_USER=firenze +POSTGRES_PASSWORD=firenze +POSTGRES_DB=firenze -# API — prefixo MANSAO_ (ver apps/api/src/mansao/config.py) -MANSAO_ENVIRONMENT=dev -MANSAO_DATABASE_URL=postgresql+psycopg://mansao:mansao@localhost:5433/mansao -MANSAO_REDIS_URL=redis://localhost:6379/0 +# API — prefixo FIRENZE_ (ver apps/api/src/firenze/config.py) +FIRENZE_ENVIRONMENT=dev +FIRENZE_DATABASE_URL=postgresql+psycopg://firenze:firenze@localhost:5433/firenze +FIRENZE_REDIS_URL=redis://localhost:6379/0 # Provedor de LLM. Nunca commitar valor real. -# O SDK lê ANTHROPIC_API_KEY sozinho — sem prefixo MANSAO_. +# O SDK lê ANTHROPIC_API_KEY sozinho — sem prefixo FIRENZE_. # ANTHROPIC_API_KEY= # Qual modelo escreve o verniz. É o botão que move o custo por caso. -MANSAO_VENEER_MODEL=claude-haiku-4-5 +FIRENZE_VENEER_MODEL=claude-haiku-4-5 diff --git a/CLAUDE.md b/CLAUDE.md index 92c5186..27b1b75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# Mansão +# Firenze Jogo de mistério investigativo com NPCs agênticos. Next.js (front) + FastAPI/LangGraph (core de IA) + Postgres com pgvector. diff --git a/Makefile b/Makefile index 8e3d755..6ea27b4 100644 --- a/Makefile +++ b/Makefile @@ -18,16 +18,16 @@ logs: ## segue os logs do compose $(COMPOSE) logs -f psql: ## abre psql no banco local - $(COMPOSE) exec db psql -U mansao -d mansao + $(COMPOSE) exec db psql -U firenze -d firenze install: ## sincroniza o venv de apps/api a partir do uv.lock cd $(API) && uv sync --extra dev api: ## roda a API local com reload (sem container) - cd $(API) && uv run uvicorn mansao.main:app --reload --port 8000 + cd $(API) && uv run uvicorn firenze.main:app --reload --port 8000 case: ## gera um caso (make case SEED=42 [LOCALE=en] [REVEAL=1] [VENEER=1]) - cd $(API) && uv run mansao generate --seed $(or $(SEED),1) --locale $(or $(LOCALE),pt-BR) $(if $(REVEAL),--reveal,) $(if $(VENEER),--veneer,) + cd $(API) && uv run firenze generate --seed $(or $(SEED),1) --locale $(or $(LOCALE),pt-BR) $(if $(REVEAL),--reveal,) $(if $(VENEER),--veneer,) openapi: ## regenera apps/api/openapi.json cd $(API) && uv run python scripts/dump_openapi.py diff --git a/README.md b/README.md index f5d6b74..9256d09 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mansão +# Firenze An investigative mystery game where the suspects are LLM-driven NPCs. You question them in free text, confront them with evidence, and name a culprit. @@ -70,7 +70,7 @@ Roughly half of the mysteries you get by sampling constraints at random are unsolvable, and they *look* fine until a player wastes an hour on one. So a case is not publishable until an automated solver proves a deduction path exists. -[`generation/solver.py`](apps/api/src/mansao/generation/solver.py) takes `Case` +[`generation/solver.py`](apps/api/src/firenze/generation/solver.py) takes `Case` and never `CaseWithSolution` — it cannot read the answer, and the type signature is what guarantees that rather than the discipline of whoever writes the next function. It starts from the public facts, adds everything a suspect would @@ -83,7 +83,7 @@ regenerated. "Do not reveal the solution" in a system prompt is a wish. The solution is a separate entity from the case, so the function that assembles an NPC's context takes a type that has no path to the culprit -([`domain/models.py`](apps/api/src/mansao/domain/models.py)). +([`domain/models.py`](apps/api/src/firenze/domain/models.py)). ```mermaid flowchart LR @@ -138,9 +138,9 @@ curl localhost:8000/health | Path | What lives there | |---|---| -| [`apps/api/src/mansao/domain/`](apps/api/src/mansao/domain/) | Entities. Structure only — no prose, no rendered sentence | -| [`apps/api/src/mansao/generation/`](apps/api/src/mansao/generation/) | Generator, solver, and the invariant checks | -| [`apps/api/src/mansao/i18n/`](apps/api/src/mansao/i18n/) | Message catalogs. Grammar lives here, not in the domain | +| [`apps/api/src/firenze/domain/`](apps/api/src/firenze/domain/) | Entities. Structure only — no prose, no rendered sentence | +| [`apps/api/src/firenze/generation/`](apps/api/src/firenze/generation/) | Generator, solver, and the invariant checks | +| [`apps/api/src/firenze/i18n/`](apps/api/src/firenze/i18n/) | Message catalogs. Grammar lives here, not in the domain | | [`docs/adr/`](docs/adr/) | Architecture decisions, with their downsides written down | | [`infra/compose/`](infra/compose/) | Local Postgres with pgvector, Redis, API | diff --git a/apps/api/openapi.json b/apps/api/openapi.json index 5138311..9660350 100644 --- a/apps/api/openapi.json +++ b/apps/api/openapi.json @@ -36,7 +36,7 @@ "info": { "description": "Owns the whole deterministic domain and every call to a model (ADR-0003). The front end never talks to the model provider or to the database.", "summary": "AI core and domain of the mystery game.", - "title": "Mansão API", + "title": "Firenze API", "version": "0.1.0" }, "openapi": "3.1.0", diff --git a/apps/api/pyproject.toml b/apps/api/pyproject.toml index b511b12..ac399ce 100644 --- a/apps/api/pyproject.toml +++ b/apps/api/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "mansao-api" +name = "firenze-api" version = "0.1.0" -description = "Core de IA e domínio do Mansão" +description = "Core de IA e domínio do Firenze" requires-python = ">=3.13" dependencies = [ "fastapi>=0.115", @@ -11,7 +11,7 @@ dependencies = [ ] [project.scripts] -mansao = "mansao.cli:main" +firenze = "firenze.cli:main" [project.optional-dependencies] dev = [ @@ -26,7 +26,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] -packages = ["src/mansao"] +packages = ["src/firenze"] [tool.ruff] line-length = 100 diff --git a/apps/api/scripts/dump_openapi.py b/apps/api/scripts/dump_openapi.py index 072807c..be8bd77 100644 --- a/apps/api/scripts/dump_openapi.py +++ b/apps/api/scripts/dump_openapi.py @@ -8,7 +8,7 @@ import json from pathlib import Path -from mansao.main import app +from firenze.main import app DESTINO = Path(__file__).resolve().parents[1] / "openapi.json" diff --git a/apps/api/src/mansao/__init__.py b/apps/api/src/firenze/__init__.py similarity index 51% rename from apps/api/src/mansao/__init__.py rename to apps/api/src/firenze/__init__.py index d7e7fd5..d79c7af 100644 --- a/apps/api/src/mansao/__init__.py +++ b/apps/api/src/firenze/__init__.py @@ -1,3 +1,3 @@ from importlib.metadata import version -__version__ = version("mansao-api") +__version__ = version("firenze-api") diff --git a/apps/api/src/mansao/cli.py b/apps/api/src/firenze/cli.py similarity index 89% rename from apps/api/src/mansao/cli.py rename to apps/api/src/firenze/cli.py index 33a89e0..eb0f2bd 100644 --- a/apps/api/src/mansao/cli.py +++ b/apps/api/src/firenze/cli.py @@ -1,6 +1,6 @@ """Case generator CLI. -`mansao generate --seed 42` prints the case as a player would receive it. The +`firenze generate --seed 42` prints the case as a player would receive it. The solution only shows with `--reveal`, and that is not decoration: if printing the culprit were the default, running the command while developing would spoil every case you meant to play. @@ -14,11 +14,11 @@ import sys from collections.abc import Sequence -from mansao.config import settings -from mansao.domain import CaseWithSolution, FactKind, Role -from mansao.generation import UnsolvableCase, generate, solve -from mansao.i18n import DEFAULT_LOCALE, Catalog, UnknownLocale, available_locales, load -from mansao.veneer import CaseVeneer, VeneerRejected, VeneerUnavailable, write +from firenze.config import settings +from firenze.domain import CaseWithSolution, FactKind, Role +from firenze.generation import UnsolvableCase, generate, solve +from firenze.i18n import DEFAULT_LOCALE, Catalog, UnknownLocale, available_locales, load +from firenze.veneer import CaseVeneer, VeneerRejected, VeneerUnavailable, write def _briefing( @@ -80,7 +80,7 @@ def _briefing( def main(argv: Sequence[str] | None = None) -> int: - parser = argparse.ArgumentParser(prog="mansao", description="Mansão case generator.") + parser = argparse.ArgumentParser(prog="firenze", description="Firenze case generator.") sub = parser.add_subparsers(dest="command", required=True) gen = sub.add_parser("generate", help="generate a case from a seed") diff --git a/apps/api/src/mansao/config.py b/apps/api/src/firenze/config.py similarity index 63% rename from apps/api/src/mansao/config.py rename to apps/api/src/firenze/config.py index cc68edf..aceb17a 100644 --- a/apps/api/src/mansao/config.py +++ b/apps/api/src/firenze/config.py @@ -6,12 +6,12 @@ class Settings(BaseSettings): - """Process configuration. MANSAO_ prefix so nothing collides.""" + """Process configuration. FIRENZE_ prefix so nothing collides.""" - model_config = SettingsConfigDict(env_prefix="MANSAO_", env_file=".env", extra="ignore") + model_config = SettingsConfigDict(env_prefix="FIRENZE_", env_file=".env", extra="ignore") environment: Environment = "dev" - database_url: str = "postgresql+psycopg://mansao:mansao@localhost:5433/mansao" + database_url: str = "postgresql+psycopg://firenze:firenze@localhost:5433/firenze" redis_url: str = "redis://localhost:6379/0" veneer_model: str = "claude-haiku-4-5" """Which model writes the veneer. Env-switchable because it is the one diff --git a/apps/api/src/mansao/domain/__init__.py b/apps/api/src/firenze/domain/__init__.py similarity index 89% rename from apps/api/src/mansao/domain/__init__.py rename to apps/api/src/firenze/domain/__init__.py index 9a5ae3d..3e570df 100644 --- a/apps/api/src/mansao/domain/__init__.py +++ b/apps/api/src/firenze/domain/__init__.py @@ -1,6 +1,6 @@ """Domain model. Vocabulary in docs/01-dominio.md.""" -from mansao.domain.models import ( +from firenze.domain.models import ( Case, CaseWithSolution, Character, diff --git a/apps/api/src/mansao/domain/models.py b/apps/api/src/firenze/domain/models.py similarity index 100% rename from apps/api/src/mansao/domain/models.py rename to apps/api/src/firenze/domain/models.py diff --git a/apps/api/src/mansao/generation/__init__.py b/apps/api/src/firenze/generation/__init__.py similarity index 50% rename from apps/api/src/mansao/generation/__init__.py rename to apps/api/src/firenze/generation/__init__.py index d10eb79..1ca914b 100644 --- a/apps/api/src/mansao/generation/__init__.py +++ b/apps/api/src/firenze/generation/__init__.py @@ -1,8 +1,8 @@ """Case generation, validation and the deducibility gate.""" -from mansao.generation.generator import GENERATOR_VERSION, UnsolvableCase, generate -from mansao.generation.solver import SolverResult, solve -from mansao.generation.validation import InvalidCase, validate +from firenze.generation.generator import GENERATOR_VERSION, UnsolvableCase, generate +from firenze.generation.solver import SolverResult, solve +from firenze.generation.validation import InvalidCase, validate __all__ = [ "GENERATOR_VERSION", diff --git a/apps/api/src/mansao/generation/generator.py b/apps/api/src/firenze/generation/generator.py similarity index 98% rename from apps/api/src/mansao/generation/generator.py rename to apps/api/src/firenze/generation/generator.py index 929a1e5..693490b 100644 --- a/apps/api/src/mansao/generation/generator.py +++ b/apps/api/src/firenze/generation/generator.py @@ -13,7 +13,7 @@ import random from itertools import count -from mansao.domain import ( +from firenze.domain import ( Case, CaseWithSolution, Character, @@ -23,8 +23,8 @@ Scope, Solution, ) -from mansao.generation.solver import solve -from mansao.generation.validation import validate +from firenze.generation.solver import solve +from firenze.generation.validation import validate GENERATOR_VERSION = "2" diff --git a/apps/api/src/mansao/generation/solver.py b/apps/api/src/firenze/generation/solver.py similarity index 98% rename from apps/api/src/mansao/generation/solver.py rename to apps/api/src/firenze/generation/solver.py index 9da43d3..f1837a4 100644 --- a/apps/api/src/mansao/generation/solver.py +++ b/apps/api/src/firenze/generation/solver.py @@ -15,7 +15,7 @@ from pydantic import BaseModel, ConfigDict -from mansao.domain import Case, Fact, FactKind +from firenze.domain import Case, Fact, FactKind class SolverResult(BaseModel): diff --git a/apps/api/src/mansao/generation/validation.py b/apps/api/src/firenze/generation/validation.py similarity index 97% rename from apps/api/src/mansao/generation/validation.py rename to apps/api/src/firenze/generation/validation.py index 80d45de..8cf3847 100644 --- a/apps/api/src/mansao/generation/validation.py +++ b/apps/api/src/firenze/generation/validation.py @@ -5,7 +5,7 @@ something when the generator changes, or when a case arrives from elsewhere. """ -from mansao.domain import CaseWithSolution, FactKind, Role +from firenze.domain import CaseWithSolution, FactKind, Role class InvalidCase(ValueError): diff --git a/apps/api/src/mansao/i18n/__init__.py b/apps/api/src/firenze/i18n/__init__.py similarity index 89% rename from apps/api/src/mansao/i18n/__init__.py rename to apps/api/src/firenze/i18n/__init__.py index 40c83e5..3e97228 100644 --- a/apps/api/src/mansao/i18n/__init__.py +++ b/apps/api/src/firenze/i18n/__init__.py @@ -1,6 +1,6 @@ """Locale catalogs: structure in, prose out.""" -from mansao.i18n.catalog import ( +from firenze.i18n.catalog import ( DEFAULT_LOCALE, Catalog, MissingMessage, diff --git a/apps/api/src/mansao/i18n/catalog.py b/apps/api/src/firenze/i18n/catalog.py similarity index 99% rename from apps/api/src/mansao/i18n/catalog.py rename to apps/api/src/firenze/i18n/catalog.py index 1645b2c..cd0b3bb 100644 --- a/apps/api/src/mansao/i18n/catalog.py +++ b/apps/api/src/firenze/i18n/catalog.py @@ -20,7 +20,7 @@ from pathlib import Path from typing import Any -from mansao.domain import Case, Fact +from firenze.domain import Case, Fact MESSAGES_DIR = Path(__file__).parent / "messages" DEFAULT_LOCALE = "pt-BR" diff --git a/apps/api/src/mansao/i18n/messages/en.json b/apps/api/src/firenze/i18n/messages/en.json similarity index 100% rename from apps/api/src/mansao/i18n/messages/en.json rename to apps/api/src/firenze/i18n/messages/en.json diff --git a/apps/api/src/mansao/i18n/messages/pt-BR.json b/apps/api/src/firenze/i18n/messages/pt-BR.json similarity index 100% rename from apps/api/src/mansao/i18n/messages/pt-BR.json rename to apps/api/src/firenze/i18n/messages/pt-BR.json diff --git a/apps/api/src/mansao/main.py b/apps/api/src/firenze/main.py similarity index 89% rename from apps/api/src/mansao/main.py rename to apps/api/src/firenze/main.py index 9586b10..8b62212 100644 --- a/apps/api/src/mansao/main.py +++ b/apps/api/src/firenze/main.py @@ -3,8 +3,8 @@ from fastapi import FastAPI from pydantic import BaseModel -from mansao import __version__ -from mansao.config import Environment, settings +from firenze import __version__ +from firenze.config import Environment, settings class Health(BaseModel): @@ -17,7 +17,7 @@ class Health(BaseModel): def create_app() -> FastAPI: app = FastAPI( - title="Mansão API", + title="Firenze API", version=__version__, summary="AI core and domain of the mystery game.", description=( diff --git a/apps/api/src/mansao/veneer/__init__.py b/apps/api/src/firenze/veneer/__init__.py similarity index 54% rename from apps/api/src/mansao/veneer/__init__.py rename to apps/api/src/firenze/veneer/__init__.py index 125d6da..8b622ce 100644 --- a/apps/api/src/mansao/veneer/__init__.py +++ b/apps/api/src/firenze/veneer/__init__.py @@ -1,8 +1,8 @@ """Prose over an approved structure. The model writes; it decides nothing.""" -from mansao.veneer.models import CaseVeneer, CharacterVeneer, VeneerDraft -from mansao.veneer.validation import VeneerRejected, check -from mansao.veneer.writer import PROMPT_VERSION, VeneerUnavailable, load_prompt, write +from firenze.veneer.models import CaseVeneer, CharacterVeneer, VeneerDraft +from firenze.veneer.validation import VeneerRejected, check +from firenze.veneer.writer import PROMPT_VERSION, VeneerUnavailable, load_prompt, write __all__ = [ "PROMPT_VERSION", diff --git a/apps/api/src/mansao/veneer/models.py b/apps/api/src/firenze/veneer/models.py similarity index 100% rename from apps/api/src/mansao/veneer/models.py rename to apps/api/src/firenze/veneer/models.py diff --git a/apps/api/src/mansao/veneer/validation.py b/apps/api/src/firenze/veneer/validation.py similarity index 98% rename from apps/api/src/mansao/veneer/validation.py rename to apps/api/src/firenze/veneer/validation.py index 1699323..ea0ccd7 100644 --- a/apps/api/src/mansao/veneer/validation.py +++ b/apps/api/src/firenze/veneer/validation.py @@ -11,8 +11,8 @@ import re -from mansao.domain import Case -from mansao.veneer.models import VeneerDraft +from firenze.domain import Case +from firenze.veneer.models import VeneerDraft class VeneerRejected(ValueError): diff --git a/apps/api/src/mansao/veneer/writer.py b/apps/api/src/firenze/veneer/writer.py similarity index 94% rename from apps/api/src/mansao/veneer/writer.py rename to apps/api/src/firenze/veneer/writer.py index 729ffd5..259c68e 100644 --- a/apps/api/src/mansao/veneer/writer.py +++ b/apps/api/src/firenze/veneer/writer.py @@ -20,10 +20,10 @@ from pathlib import Path from typing import Any, Protocol, cast -from mansao.domain import Case -from mansao.i18n import Catalog -from mansao.veneer.models import CaseVeneer, VeneerDraft -from mansao.veneer.validation import check +from firenze.domain import Case +from firenze.i18n import Catalog +from firenze.veneer.models import CaseVeneer, VeneerDraft +from firenze.veneer.validation import check PROMPT_VERSION = "v1" # Haiku: the veneer is short, structured, and its failure modes are caught by @@ -49,8 +49,8 @@ def messages(self) -> _Parseable: ... def prompts_dir() -> Path: - """Repository `prompts/`, or wherever `MANSAO_PROMPTS_DIR` points.""" - override = os.environ.get("MANSAO_PROMPTS_DIR") + """Repository `prompts/`, or wherever `FIRENZE_PROMPTS_DIR` points.""" + override = os.environ.get("FIRENZE_PROMPTS_DIR") if override: return Path(override) return Path(__file__).resolve().parents[5] / "prompts" diff --git a/apps/api/tests/test_cli.py b/apps/api/tests/test_cli.py index 0cb42f7..e01fc73 100644 --- a/apps/api/tests/test_cli.py +++ b/apps/api/tests/test_cli.py @@ -1,6 +1,6 @@ import pytest -from mansao.cli import main +from firenze.cli import main Capture = pytest.CaptureFixture[str] diff --git a/apps/api/tests/test_generation.py b/apps/api/tests/test_generation.py index 1d54c71..4e4321e 100644 --- a/apps/api/tests/test_generation.py +++ b/apps/api/tests/test_generation.py @@ -6,10 +6,10 @@ import pytest -from mansao.domain import CaseWithSolution, FactKind, Role -from mansao.generation import InvalidCase, generate, solve, validate -from mansao.generation.solver import reachable_facts -from mansao.generation.validation import rn_004_no_overlap +from firenze.domain import CaseWithSolution, FactKind, Role +from firenze.generation import InvalidCase, generate, solve, validate +from firenze.generation.solver import reachable_facts +from firenze.generation.validation import rn_004_no_overlap SEEDS = range(1, 41) diff --git a/apps/api/tests/test_health.py b/apps/api/tests/test_health.py index 6e2491e..378031f 100644 --- a/apps/api/tests/test_health.py +++ b/apps/api/tests/test_health.py @@ -1,7 +1,7 @@ from fastapi.testclient import TestClient -from mansao import __version__ -from mansao.main import app +from firenze import __version__ +from firenze.main import app cliente = TestClient(app) @@ -16,5 +16,5 @@ def test_health_responde_ok() -> None: def test_openapi_e_servido() -> None: esquema = cliente.get("/openapi.json").json() - assert esquema["info"]["title"] == "Mansão API" + assert esquema["info"]["title"] == "Firenze API" assert "/health" in esquema["paths"] diff --git a/apps/api/tests/test_i18n.py b/apps/api/tests/test_i18n.py index 7b7c2f9..c8f0f26 100644 --- a/apps/api/tests/test_i18n.py +++ b/apps/api/tests/test_i18n.py @@ -7,10 +7,10 @@ import pytest -from mansao.domain import FactKind -from mansao.generation import generate -from mansao.generation.generator import MEANS_KEYS, MOTIVE_KEYS, ROOMS, SECRET_KEYS -from mansao.i18n import UnknownLocale, available_locales, load +from firenze.domain import FactKind +from firenze.generation import generate +from firenze.generation.generator import MEANS_KEYS, MOTIVE_KEYS, ROOMS, SECRET_KEYS +from firenze.i18n import UnknownLocale, available_locales, load LOCALES = available_locales() diff --git a/apps/api/tests/test_veneer.py b/apps/api/tests/test_veneer.py index ed85d22..309edf7 100644 --- a/apps/api/tests/test_veneer.py +++ b/apps/api/tests/test_veneer.py @@ -11,10 +11,10 @@ import pytest -from mansao.domain import Case -from mansao.generation import generate -from mansao.i18n import load -from mansao.veneer import ( +from firenze.domain import Case +from firenze.generation import generate +from firenze.i18n import load +from firenze.veneer import ( CharacterVeneer, VeneerDraft, VeneerRejected, @@ -23,7 +23,7 @@ load_prompt, write, ) -from mansao.veneer.writer import _render_prompt +from firenze.veneer.writer import _render_prompt class StubMessages: diff --git a/apps/api/uv.lock b/apps/api/uv.lock index fda8129..a32f4b8 100644 --- a/apps/api/uv.lock +++ b/apps/api/uv.lock @@ -170,6 +170,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, ] +[[package]] +name = "firenze-api" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "anthropic" }, + { name = "fastapi" }, + { name = "pydantic-settings" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.optional-dependencies] +dev = [ + { name = "httpx" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=1.2.0" }, + { name = "fastapi", specifier = ">=0.115" }, + { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.28" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.14" }, + { name = "pydantic-settings", specifier = ">=2.7" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.34" }, +] +provides-extras = ["dev"] + [[package]] name = "h11" version = "0.16.0" @@ -428,38 +460,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, ] -[[package]] -name = "mansao-api" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "anthropic" }, - { name = "fastapi" }, - { name = "pydantic-settings" }, - { name = "uvicorn", extra = ["standard"] }, -] - -[package.optional-dependencies] -dev = [ - { name = "httpx" }, - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "anthropic", specifier = ">=1.2.0" }, - { name = "fastapi", specifier = ">=0.115" }, - { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.28" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.14" }, - { name = "pydantic-settings", specifier = ">=2.7" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9" }, - { name = "uvicorn", extras = ["standard"], specifier = ">=0.34" }, -] -provides-extras = ["dev"] - [[package]] name = "mypy" version = "2.3.1" diff --git a/docs/00-plano-de-projeto.md b/docs/00-plano-de-projeto.md index 657db71..990444c 100644 --- a/docs/00-plano-de-projeto.md +++ b/docs/00-plano-de-projeto.md @@ -1,4 +1,4 @@ -# Plano de Projeto — Mansão (working title) +# Plano de Projeto — Firenze > Jogo de mistério investigativo com NPCs agênticos. > Documento vivo. Commitar em `docs/00-plano-de-projeto.md` e atualizar a cada fase. @@ -32,7 +32,7 @@ com o código que ela descreve. Diagrama que não está em Git apodrece. ### Estrutura (monorepo) ``` -mansao/ +firenze/ ├── README.md # o que é, como rodar em 5 min ├── CONTRIBUTING.md # convenções (mesmo solo — disciplina) ├── CHANGELOG.md # gerado por conventional commits @@ -197,14 +197,14 @@ Níveis 1 e 2 bastam. Nível 3 só para o subsistema de agentes. ```mermaid C4Context - title Contexto — Mansão + title Contexto — Firenze Person(jogador, "Jogador") - System(mansao, "Mansão", "Jogo de mistério com NPCs agênticos") + System(firenze, "Firenze", "Jogo de mistério com NPCs agênticos") System_Ext(llm, "Provedor LLM", "Inferência dos NPCs") System_Ext(obs, "Langfuse", "Tracing e avaliação") - Rel(jogador, mansao, "Interroga, confronta, acusa") - Rel(mansao, llm, "Completions estruturadas") - Rel(mansao, obs, "Traces, custo, latência") + Rel(jogador, firenze, "Interroga, confronta, acusa") + Rel(firenze, llm, "Completions estruturadas") + Rel(firenze, obs, "Traces, custo, latência") ``` ### 4.3 Diagramas de sequência diff --git a/docs/adr/0004-deterministic-generation-with-an-llm-veneer.md b/docs/adr/0004-deterministic-generation-with-an-llm-veneer.md index e092053..67b2950 100644 --- a/docs/adr/0004-deterministic-generation-with-an-llm-veneer.md +++ b/docs/adr/0004-deterministic-generation-with-an-llm-veneer.md @@ -33,7 +33,7 @@ Three constraints bear on who generates the structure: ## Decision The structure is assembled by a seeded deterministic generator in -`mansao.generation.generator`. The solver, in `mansao.generation.solver`, takes +`firenze.generation.generator`. The solver, in `firenze.generation.solver`, takes `Case` (never `CaseWithSolution`) and holds a veto: a case that is not deducible is discarded and regenerated from a derived seed. diff --git a/docs/adr/0005-locale-is-a-property-of-the-match.md b/docs/adr/0005-locale-is-a-property-of-the-match.md index 04bccc8..e7c6e26 100644 --- a/docs/adr/0005-locale-is-a-property-of-the-match.md +++ b/docs/adr/0005-locale-is-a-property-of-the-match.md @@ -34,9 +34,9 @@ German would need three different articles. A `Fact` carries a kind and slots — character, room id, interval index. Rooms are stable identifiers (`cellar`), never display names. Time is minutes on a clock the case defines, not a formatted string. Secrets, means and motives are -message keys. Nothing in `mansao.domain` contains a sentence. +message keys. Nothing in `firenze.domain` contains a sentence. -Catalogs live in `mansao/i18n/messages/.json` and ship with `pt-BR` and +Catalogs live in `firenze/i18n/messages/.json` and ship with `pt-BR` and `en` from day one — a second locale is the only way to know the first one is actually separable. **Grammar lives in the catalog**: each locale declares the preposition its rooms need, and its own clock format. diff --git a/infra/compose/docker-compose.yml b/infra/compose/docker-compose.yml index 7237c8b..5124090 100644 --- a/infra/compose/docker-compose.yml +++ b/infra/compose/docker-compose.yml @@ -1,13 +1,13 @@ -name: mansao +name: firenze services: db: # pgvector já compilado sobre o Postgres 16 — mesma versão do DBaaS (ADR-0002) image: pgvector/pgvector:pg16 environment: - POSTGRES_USER: ${POSTGRES_USER:-mansao} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mansao} - POSTGRES_DB: ${POSTGRES_DB:-mansao} + POSTGRES_USER: ${POSTGRES_USER:-firenze} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-firenze} + POSTGRES_DB: ${POSTGRES_DB:-firenze} ports: # 5433 no host: 5432 costuma estar ocupada por um Postgres instalado na máquina - "5433:5432" @@ -15,7 +15,7 @@ services: - pgdata:/var/lib/postgresql/data - ./init:/docker-entrypoint-initdb.d:ro healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mansao} -d ${POSTGRES_DB:-mansao}"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-firenze} -d ${POSTGRES_DB:-firenze}"] interval: 5s timeout: 3s retries: 10 @@ -34,9 +34,9 @@ services: build: context: ../../apps/api environment: - MANSAO_ENVIRONMENT: dev - MANSAO_DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-mansao}:${POSTGRES_PASSWORD:-mansao}@db:5432/${POSTGRES_DB:-mansao} - MANSAO_REDIS_URL: redis://redis:6379/0 + FIRENZE_ENVIRONMENT: dev + FIRENZE_DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-firenze}:${POSTGRES_PASSWORD:-firenze}@db:5432/${POSTGRES_DB:-firenze} + FIRENZE_REDIS_URL: redis://redis:6379/0 ports: - "8000:8000" volumes: diff --git a/infra/compose/init/01-extensions.sql b/infra/compose/init/01-extensions.sql index 22b88a8..b291209 100644 --- a/infra/compose/init/01-extensions.sql +++ b/infra/compose/init/01-extensions.sql @@ -1,4 +1,4 @@ --- Roda uma vez, na criação do volume. Para reexecutar: make down && docker volume rm mansao_pgdata +-- Roda uma vez, na criação do volume. Para reexecutar: make down && docker volume rm firenze_pgdata -- -- pgaudit fica de fora aqui de propósito: exige shared_preload_libraries e não -- vem na imagem pgvector. É extensão do DBaaS da Magalu (ADR-0002), habilitada