Files
soleprint/CLAUDE.md
2026-09-12 02:44:40 -03:00

248 lines
9.3 KiB
Markdown

# Soleprint - Development Control Room
## What Is This?
Soleprint is a **development workflow platform** - a self-contained environment where you can run, test, and document everything in isolation. Born from the friction of working on small teams where testing required PRs, documentation was scattered, and quick API connectors took too long to set up.
**Core idea:** BDD -> Gherkin -> Backend/Frontend Tests, with reusable connectors and tools that work across projects.
**Name:** Soleprint - "Cada paso deja huella" / "Each step leaves a mark"
## Project Structure
```
spr/
├── CLAUDE.md # You are here
├── README.md # User-facing docs
├── schema.json # Source of truth for models
├── build.py # Build tool
├── soleprint/ # Core framework (all systems inside)
│ ├── main.py # Hub entry point
│ ├── run.py # Bare-metal dev server
│ ├── index.html
│ ├── requirements.txt
│ ├── Dockerfile
│ ├── dataloader/
│ │
│ ├── artery/ # Connectors
│ │ ├── veins/ # Real API connectors (jira, slack, google)
│ │ ├── shunts/ # Fake connectors for testing
│ │ ├── pulses/ # Composed: Vein + Room + Depot
│ │ └── depots/
│ │
│ ├── atlas/ # Documentation
│ │ ├── books/ # Documentation (soleprint-only)
│ │ ├── templates/
│ │ ├── depots/
│ │ └── static/
│ │
│ └── station/ # Tools & execution
│ ├── tools/ # modelgen, datagen, tester, sbwrapper
│ ├── monitors/ # databrowse
│ └── desks/
├── cfg/ # Room configurations
│ ├── standalone/ # Base soleprint config
│ │ ├── config.json
│ │ ├── data/
│ │ └── soleprint/
│ │
│ └── amar/ # Amar room config
│ ├── config.json
│ ├── data/
│ ├── soleprint/ # Room overlay — merged over soleprint/ on build
│ │ ├── artery/ # room shunts, pulses
│ │ │ └── shunts/amar/
│ │ ├── atlas/ # room books
│ │ │ └── books/
│ │ ├── station/ # room tool configs
│ │ │ └── tools/datagen/
│ │ └── nginx/
│ ├── ctrl/ # Room lifecycle scripts (copied into gen/<room>/)
│ ├── link/ # Bridge to managed app
│ └── amar/ # The managed app itself
├── ctrl/ # Build/run scripts (see Build & Run)
└── gen/ # Built instances (gitignored)
├── standalone/
└── amar/
```
## The Four Systems
| System | Purpose | Tagline |
|--------|---------|---------|
| **Soleprint** | Core coordinator | Cada paso deja huella |
| **Artery** | Connectors to external services | Todo lo vital |
| **Atlas** | Actionable documentation | Mapeando el recorrido |
| **Station** | Tools, environments, execution | Centro de control |
## Artery Hierarchy
```
Vein ──────► Pulse ──────► Plexus
│ │ │
│ │ └── Full app: backend + frontend + DB
│ │
│ └── Composed: Vein + Room + Depot
└── Stateless API connector
Shunt ─── Fake connector for testing
```
## Room Configuration
Each room in `cfg/` has:
- `config.json` - Framework branding/terminology
- `data/` - Data files (veins.json, shunts.json, etc.)
Room-specific system configs live under `cfg/<room>/soleprint/` and are merged over
the core `soleprint/` tree at build time:
- `soleprint/artery/` - Room-specific shunts, pulses
- `soleprint/atlas/` - Room-specific books
- `soleprint/station/` - Room-specific tool configs (datagen generators, tester tests)
A room's own lifecycle scripts live in `cfg/<room>/ctrl/` and land in
`gen/<room>/ctrl/` — that is what `make start <room>` dispatches to.
## Build & Run
`make` is the front door — one target per `ctrl/` script, with the subcommand as an
argument (`make cluster up`, not `make cluster-up`). The logic lives in the scripts,
never in the Makefile. `make help` lists every target.
```bash
make # = make help
make build [room|all|models] # -> gen/<room>/ (default: standalone)
make start [room] [-d] # dispatches to gen/<room>/ctrl/start.sh
make stop [room]
make cluster [up|down|status] # the shared `spr` kind cluster
make component [list|sync|watch|publish|diff]
make deploy [--build|--sync-only]
```
Every script stays runnable on its own — the standalone rule holds:
```bash
python build.py --cfg amar # -> gen/amar/
cd gen/standalone && python run.py # bare-metal
./ctrl/cluster.sh up # still runs directly; rig builds the cluster
cd gen/<room> && ./ctrl/start.sh # each room owns its lifecycle scripts
```
## Adding a New Room
```bash
mkdir -p cfg/newroom/data
cp cfg/standalone/config.json cfg/newroom/
cp -r cfg/standalone/data/* cfg/newroom/data/
# Add room-specific configs as needed (note the soleprint/ overlay level):
# cfg/newroom/soleprint/artery/shunts/...
# cfg/newroom/soleprint/atlas/books/...
# cfg/newroom/soleprint/station/tools/datagen/<name>.py
make build newroom
```
## Ports
| Service | Port |
|---------|------|
| Soleprint | 12000 |
## Tools
| Tool | Purpose |
|------|---------|
| modelgen | Generate models from config |
| datagen | Generate test data (uses faker) |
| tester | HTTP contract test runner |
| graphgen | Generate navigable model graphs |
| databrowse | SQL data browser |
## Shared UI (`soleprint/common/ui`)
Upstream copy of the `soleprint-ui` package. `mpr/ui/framework` and
`meetus/ui/framework` are **downstream copies** synced by `ctrl/spr.py` — edit here,
never there, or the next sync overwrites the change.
```bash
cd soleprint/common/ui
pnpm install && pnpm typecheck && pnpm test
pnpm build # -> dist/soleprint-ui.js + dist/style.css
```
**The theme ships with the bundle.** Every component styles itself with
`var(--surface-0)` and friends from `src/tokens.css`, so `src/index.ts` imports
`src/theme.css` (tokens + `base.css`). A bundle without it renders broken, not merely
unbranded. Retheme by overriding the variables in `tokens.css`; don't inline colours
in components.
`dist/` and `node_modules/` are gitignored — the dist is an artifact a container
boots, not source. To hand someone a running artifact without the sources:
```bash
python ctrl/spr.py publish soleprint-ui <dest> --dist # bundle + manifest only
```
## Tests
**No test bodies are committed to core.** `soleprint/station/tools/tester/` ships the
base class, runner and UI; tests belong to a room at
`cfg/<room>/soleprint/station/tools/tester/tests/`.
Two rules, both in `tester/tests/test_template.py`:
1. **One suite, any environment** — a test states what the API promises, never who
implements it or where it runs.
2. **No helper framework tools** — stdlib `unittest` + `httpx`. No pytest fixtures,
no framework test client, no factories, no ORM or database access.
Django, where it appears, is an optional private DB editor via its admin — never the
framework, and never something a test reaches into.
## External Paths
| What | Path |
|------|------|
| Pipelines | /home/mariano/wdir/ppl |
## Files Ignored
- `gen/` - Regenerate with `make build [room]`
- `dist/` - Build artifact; regenerate with `pnpm build`
- `node_modules/`
- `fails/`, `def/` - Drafts
- `__pycache__/`, `.venv/`
## Known Broken (found, not yet fixed)
- `tester/tests/example/test_health.py` imports `pytest`, which is neither a
dependency nor allowed here — `tests/README.md` says stdlib `unittest` and
`httpx`, no pytest. It fails to import, so `python -m tester discover` reports
it as `_FailedTest`. It also duplicates `test_template.py`, which is the
sanctioned example. Removing it is probably the fix; core ships no tests.
- Old vocabulary survives in prose — `album`, `larder`, `ward`, `nest` and
`pawprint` still appear in READMEs, `docs/`, and this file's history. Live code
is clean; the docs lag.
## Fixed
- **A real API key was tracked** in `station/tools/tester/.env`. Untracked, `.env`
added to `.gitignore`, `.env.example` added. **The key is still in git history
and must be rotated** — untracking does not unpublish it.
- Client vocabulary is out of live code. `atlas/main.py` was written entirely as
`Album`/`larder`/`PAWPRINT_URL`; `databrowse` docs described a `larder/` the
code stopped using; `get-api-key.sh` defaulted to the client's database name.
- Two dead back-links, found while renaming: `atlas/main.py` passed
`pawprint_url` and `artery/index.html` read `pawprint_url`, while `run.py`
passes `soleprint_url`. Neither "← Soleprint" link had ever rendered.
- `atlas/main.py` fetched `/api/data/album`; `main.py` serves `/api/data/atlas`.
`get_data()` had been failing and returning empty lists.
- `tester/tests/_dev/test_health.py` imported a non-existent `..endpoints`. The
import was unused; removing it makes the module discoverable (2 tests).